aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@yahoo.com>2019-10-04 16:11:06 -0500
committerLibravatar GitHub <noreply@github.com>2019-10-04 16:11:06 -0500
commit6cf61b6ab53d44bc35e2bd7eb1a1987f72c25329 (patch)
treec459376a3d0d4516b170394388940f2038d32608
parentfix the fix (diff)
parentimprove enforce_filters warning (diff)
downloadfirejail-6cf61b6ab53d44bc35e2bd7eb1a1987f72c25329.tar.gz
firejail-6cf61b6ab53d44bc35e2bd7eb1a1987f72c25329.tar.zst
firejail-6cf61b6ab53d44bc35e2bd7eb1a1987f72c25329.zip
Merge pull request #2982 from smitsohu/chroot
Move chroot entirely from path based to file descriptor based mounts
-rw-r--r--src/firejail/chroot.c280
-rw-r--r--src/firejail/firejail.h10
-rw-r--r--src/firejail/fs.c255
-rw-r--r--src/firejail/main.c28
-rw-r--r--src/firejail/sandbox.c2
5 files changed, 299 insertions, 276 deletions
diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c
new file mode 100644
index 000000000..f5bb11a76
--- /dev/null
+++ b/src/firejail/chroot.c
@@ -0,0 +1,280 @@
1/*
2 * Copyright (C) 2014-2019 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#ifdef HAVE_CHROOT
22#include "firejail.h"
23#include <sys/mount.h>
24#include <sys/sendfile.h>
25#include <errno.h>
26
27#include <fcntl.h>
28#ifndef O_PATH
29# define O_PATH 010000000
30#endif
31
32
33// exit if error
34void fs_check_chroot_dir(void) {
35 EUID_ASSERT();
36 assert(cfg.chrootdir);
37 if (strstr(cfg.chrootdir, "..") ||
38 is_link(cfg.chrootdir) ||
39 !is_dir(cfg.chrootdir))
40 goto errout;
41
42 // check chroot dirname exists, chrooting into the root directory is not allowed
43 char *rpath = realpath(cfg.chrootdir, NULL);
44 if (rpath == NULL || strcmp(rpath, "/") == 0)
45 goto errout;
46
47 char *overlay;
48 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1)
49 errExit("asprintf");
50 if (strncmp(rpath, overlay, strlen(overlay)) == 0) {
51 fprintf(stderr, "Error: invalid chroot directory: no directories in %s are allowed\n", overlay);
52 exit(1);
53 }
54 free(overlay);
55 cfg.chrootdir = rpath;
56 return;
57
58errout:
59 fprintf(stderr, "Error: invalid chroot directory %s\n", cfg.chrootdir);
60 exit(1);
61}
62
63// copy /etc/resolv.conf in chroot directory
64static void copy_resolvconf(int parentfd) {
65 int in = open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC);
66 if (in == -1) {
67 fwarning("/etc/resolv.conf not initialized\n");
68 return;
69 }
70 struct stat src;
71 if (fstat(in, &src) == -1)
72 errExit("fstat");
73 // try to detect if resolv.conf has been bind mounted into the chroot
74 // do nothing in this case in order to not unlink the real file
75 struct stat dst;
76 if (fstatat(parentfd, "etc/resolv.conf", &dst, 0) == 0) {
77 if (src.st_dev == dst.st_dev && src.st_ino == dst.st_ino) {
78 close(in);
79 return;
80 }
81 }
82 if (arg_debug)
83 printf("Updating /etc/resolv.conf in chroot\n");
84 unlinkat(parentfd, "etc/resolv.conf", 0);
85 int out = openat(parentfd, "etc/resolv.conf", O_CREAT|O_WRONLY|O_CLOEXEC, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH);
86 if (out == -1)
87 errExit("open");
88 if (sendfile(out, in, NULL, src.st_size) == -1)
89 errExit("sendfile");
90 close(in);
91 close(out);
92}
93
94// exit if error
95static void check_subdir(int parentfd, const char *subdir, int check_writable) {
96 assert(subdir);
97 struct stat s;
98 if (fstatat(parentfd, subdir, &s, AT_SYMLINK_NOFOLLOW) != 0) {
99 fprintf(stderr, "Error: cannot find /%s in chroot directory\n", subdir);
100 exit(1);
101 }
102 if (!S_ISDIR(s.st_mode)) {
103 if (S_ISLNK(s.st_mode))
104 fprintf(stderr, "Error: chroot /%s is a symbolic link\n", subdir);
105 else
106 fprintf(stderr, "Error: chroot /%s is not a directory\n", subdir);
107 exit(1);
108 }
109 if (s.st_uid != 0) {
110 fprintf(stderr, "Error: chroot /%s should owned by root\n", subdir);
111 exit(1);
112 }
113 if (check_writable && ((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
114 fprintf(stderr, "Error: only root user should be given write permission on chroot /%s\n", subdir);
115 exit(1);
116 }
117}
118
119// chroot into an existing directory; mount existing /dev and update /etc/resolv.conf
120void fs_chroot(const char *rootdir) {
121 assert(rootdir);
122
123 // fails if there is any symlink or if rootdir is not a directory
124 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
125 if (parentfd == -1)
126 errExit("safe_fd");
127 // rootdir has to be owned by root and is not allowed to be generally writable,
128 // this also excludes /tmp and friends
129 struct stat s;
130 if (fstat(parentfd, &s) == -1)
131 errExit("fstat");
132 if (s.st_uid != 0) {
133 fprintf(stderr, "Error: chroot directory should be owned by root\n");
134 exit(1);
135 }
136 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
137 fprintf(stderr, "Error: only root user should be given write permission on chroot directory\n");
138 exit(1);
139 }
140 // check chroot subdirectories; /tmp/.X11-unix and /run are treated separately
141 check_subdir(parentfd, "dev", 0);
142 check_subdir(parentfd, "etc", 1);
143 check_subdir(parentfd, "proc", 0);
144 check_subdir(parentfd, "tmp", 0);
145 check_subdir(parentfd, "var/tmp", 0);
146
147 // mount-bind a /dev in rootdir
148 if (arg_debug)
149 printf("Mounting /dev on chroot /dev\n");
150 // open chroot /dev to get a file descriptor,
151 // then use this descriptor as a mount target
152 int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
153 if (fd == -1)
154 errExit("open");
155 char *proc;
156 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
157 errExit("asprintf");
158 if (mount("/dev", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
159 errExit("mounting /dev");
160 free(proc);
161 close(fd);
162
163 // mount a brand new proc filesystem
164 if (arg_debug)
165 printf("Mounting /proc filesystem on chroot /proc\n");
166 fd = openat(parentfd, "proc", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
167 if (fd == -1)
168 errExit("open");
169 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
170 errExit("asprintf");
171 if (mount("proc", proc, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
172 errExit("mounting /proc");
173 free(proc);
174 close(fd);
175
176 // x11
177 if (getenv("FIREJAIL_X11")) {
178 if (arg_debug)
179 printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n");
180 check_subdir(parentfd, "tmp/.X11-unix", 0);
181 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
182 if (fd == -1)
183 errExit("open");
184 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
185 errExit("asprintf");
186 if (mount("/tmp/.X11-unix", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
187 errExit("mounting /tmp/.X11-unix");
188 free(proc);
189 close(fd);
190 }
191
192 // some older distros don't have a /run directory, create one by default
193 if (mkdirat(parentfd, "run", 0755) == -1 && errno != EEXIST)
194 errExit("mkdir");
195 check_subdir(parentfd, "run", 1);
196
197 // create /run/firejail directory in chroot
198 if (mkdirat(parentfd, RUN_FIREJAIL_DIR+1, 0755) == -1 && errno != EEXIST)
199 errExit("mkdir");
200 check_subdir(parentfd, RUN_FIREJAIL_DIR+1, 1);
201
202 // create /run/firejail/lib directory in chroot
203 if (mkdirat(parentfd, RUN_FIREJAIL_LIB_DIR+1, 0755) == -1 && errno != EEXIST)
204 errExit("mkdir");
205 check_subdir(parentfd, RUN_FIREJAIL_LIB_DIR+1, 1);
206 // mount lib directory into the chroot
207 fd = openat(parentfd, RUN_FIREJAIL_LIB_DIR+1, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
208 if (fd == -1)
209 errExit("open");
210 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
211 errExit("asprintf");
212 if (mount(RUN_FIREJAIL_LIB_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
213 errExit("mount bind");
214 free(proc);
215 close(fd);
216
217 // create /run/firejail/mnt directory in chroot
218 if (mkdirat(parentfd, RUN_MNT_DIR+1, 0755) == -1 && errno != EEXIST)
219 errExit("mkdir");
220 check_subdir(parentfd, RUN_MNT_DIR+1, 1);
221 // mount the current mnt directory into the chroot
222 fd = openat(parentfd, RUN_MNT_DIR+1, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
223 if (fd == -1)
224 errExit("open");
225 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
226 errExit("asprintf");
227 if (mount(RUN_MNT_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
228 errExit("mount bind");
229 free(proc);
230 close(fd);
231
232 // update chroot resolv.conf
233 copy_resolvconf(parentfd);
234
235#ifdef HAVE_GCOV
236 __gcov_flush();
237#endif
238 // create /run/firejail/mnt/oroot
239 char *oroot = RUN_OVERLAY_ROOT;
240 if (mkdir(oroot, 0755) == -1)
241 errExit("mkdir");
242 // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay
243 if (asprintf(&proc, "/proc/self/fd/%d", parentfd) == -1)
244 errExit("asprintf");
245 if (mount(proc, oroot, NULL, MS_BIND|MS_REC, NULL) < 0)
246 errExit("mounting rootdir oroot");
247 free(proc);
248 close(parentfd);
249 // chroot into the new directory
250 if (arg_debug)
251 printf("Chrooting into %s\n", rootdir);
252 if (chroot(oroot) < 0)
253 errExit("chroot");
254
255 // create all other /run/firejail files and directories
256 preproc_build_firejail_dir();
257
258 // update /var directory in order to support multiple sandboxes running on the same root directory
259 // if (!arg_private_dev)
260 // fs_dev_shm();
261 fs_var_lock();
262 if (!arg_keep_var_tmp)
263 fs_var_tmp();
264 if (!arg_writable_var_log)
265 fs_var_log();
266
267 fs_var_lib();
268 fs_var_cache();
269 fs_var_utmp();
270 fs_machineid();
271
272 // don't leak user information
273 restrict_users();
274
275 // when starting as root, firejail config is not disabled;
276 if (getuid() != 0)
277 disable_config();
278}
279
280#endif // HAVE_CHROOT
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index a6377261f..fdbeb4691 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -386,18 +386,22 @@ void fs_remount(const char *dir, OPERATION op, unsigned check_mnt);
386void fs_remount_rec(const char *dir, OPERATION op, unsigned check_mnt); 386void fs_remount_rec(const char *dir, OPERATION op, unsigned check_mnt);
387// mount /proc and /sys directories 387// mount /proc and /sys directories
388void fs_proc_sys_dev_boot(void); 388void fs_proc_sys_dev_boot(void);
389// blacklist firejail configuration and runtime directories
390void disable_config(void);
389// build a basic read-only filesystem 391// build a basic read-only filesystem
390void fs_basic_fs(void); 392void fs_basic_fs(void);
391// mount overlayfs on top of / directory 393// mount overlayfs on top of / directory
392char *fs_check_overlay_dir(const char *subdirname, int allow_reuse); 394char *fs_check_overlay_dir(const char *subdirname, int allow_reuse);
393void fs_overlayfs(void); 395void fs_overlayfs(void);
394// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
395void fs_chroot(const char *rootdir);
396void fs_check_chroot_dir(const char *rootdir);
397void fs_private_tmp(void); 396void fs_private_tmp(void);
398void fs_private_cache(void); 397void fs_private_cache(void);
399void fs_mnt(const int enforce); 398void fs_mnt(const int enforce);
400 399
400// chroot.c
401// chroot into an existing directory; mount existing /dev and update /etc/resolv.conf
402void fs_check_chroot_dir(void);
403void fs_chroot(const char *rootdir);
404
401// profile.c 405// profile.c
402// find and read the profile specified by name from dir directory 406// find and read the profile specified by name from dir directory
403int profile_find_firejail(const char *name, int add_ext); 407int profile_find_firejail(const char *name, int add_ext);
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index ce2ca5e2a..f2639f318 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -28,6 +28,7 @@
28#include <dirent.h> 28#include <dirent.h>
29#include <errno.h> 29#include <errno.h>
30 30
31
31#include <fcntl.h> 32#include <fcntl.h>
32#ifndef O_PATH 33#ifndef O_PATH
33# define O_PATH 010000000 34# define O_PATH 010000000
@@ -696,8 +697,8 @@ void fs_proc_sys_dev_boot(void) {
696 } 697 }
697} 698}
698 699
699// disable firejail configuration in /etc/firejail and in ~/.config/firejail 700// disable firejail configuration in ~/.config/firejail
700static void disable_config(void) { 701void disable_config(void) {
701 struct stat s; 702 struct stat s;
702 703
703 char *fname; 704 char *fname;
@@ -1123,256 +1124,6 @@ void fs_overlayfs(void) {
1123} 1124}
1124#endif 1125#endif
1125 1126
1126
1127#ifdef HAVE_CHROOT
1128// exit if error
1129static void fs_check_chroot_subdir(const char *subdir, int parentfd, int check_writable) {
1130 assert(subdir);
1131 int fd = openat(parentfd, subdir, O_PATH|O_CLOEXEC);
1132 if (fd == -1) {
1133 if (errno == ENOENT)
1134 fprintf(stderr, "Error: cannot find /%s in chroot directory\n", subdir);
1135 else {
1136 perror("open");
1137 fprintf(stderr, "Error: cannot open /%s in chroot directory\n", subdir);
1138 }
1139 exit(1);
1140 }
1141 struct stat s;
1142 if (fstat(fd, &s) == -1)
1143 errExit("fstat");
1144 close(fd);
1145 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1146 fprintf(stderr, "Error: chroot /%s should be a directory owned by root\n", subdir);
1147 exit(1);
1148 }
1149 if (check_writable && ((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
1150 fprintf(stderr, "Error: only root user should be given write permission on chroot /%s\n", subdir);
1151 exit(1);
1152 }
1153}
1154
1155// exit if error
1156void fs_check_chroot_dir(const char *rootdir) {
1157 EUID_ASSERT();
1158 assert(rootdir);
1159
1160 char *overlay;
1161 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1)
1162 errExit("asprintf");
1163 if (strncmp(rootdir, overlay, strlen(overlay)) == 0) {
1164 fprintf(stderr, "Error: invalid chroot directory: no directories in %s are allowed\n", overlay);
1165 exit(1);
1166 }
1167 free(overlay);
1168
1169 // fails if there is any symlink or if rootdir is not a directory
1170 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1171 if (parentfd == -1) {
1172 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir);
1173 exit(1);
1174 }
1175 // rootdir has to be owned by root and is not allowed to be generally writable,
1176 // this also excludes /tmp, /var/tmp and such
1177 struct stat s;
1178 if (fstat(parentfd, &s) == -1)
1179 errExit("fstat");
1180 if (s.st_uid != 0) {
1181 fprintf(stderr, "Error: chroot directory should be owned by root\n");
1182 exit(1);
1183 }
1184 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
1185 fprintf(stderr, "Error: only root user should be given write permission on chroot directory\n");
1186 exit(1);
1187 }
1188
1189 // check subdirectories in rootdir
1190 fs_check_chroot_subdir("dev", parentfd, 0);
1191 fs_check_chroot_subdir("etc", parentfd, 1);
1192 fs_check_chroot_subdir("proc", parentfd, 0);
1193 fs_check_chroot_subdir("tmp", parentfd, 0);
1194 fs_check_chroot_subdir("var/tmp", parentfd, 0);
1195
1196 // there should be no checking on <chrootdir>/etc/resolv.conf
1197 // the file is replaced with the real /etc/resolv.conf anyway
1198#if 0
1199 if (asprintf(&name, "%s/etc/resolv.conf", rootdir) == -1)
1200 errExit("asprintf");
1201 if (stat(name, &s) == 0) {
1202 if (s.st_uid != 0) {
1203 fprintf(stderr, "Error: chroot /etc/resolv.conf should be owned by root\n");
1204 exit(1);
1205 }
1206 }
1207 else {
1208 fprintf(stderr, "Error: chroot /etc/resolv.conf not found\n");
1209 exit(1);
1210 }
1211 // on Arch /etc/resolv.conf could be a symlink to /run/systemd/resolve/resolv.conf
1212 // on Ubuntu 17.04 /etc/resolv.conf could be a symlink to /run/resolveconf/resolv.conf
1213 if (is_link(name)) {
1214 // check the link points in chroot
1215 char *rname = realpath(name, NULL);
1216 if (!rname || strncmp(rname, rootdir, strlen(rootdir)) != 0) {
1217 fprintf(stderr, "Error: chroot /etc/resolv.conf is pointing outside chroot\n");
1218 exit(1);
1219 }
1220 }
1221 free(name);
1222#endif
1223
1224 // check x11 socket directory
1225 if (getenv("FIREJAIL_X11"))
1226 fs_check_chroot_subdir("tmp/.X11-unix", parentfd, 0);
1227
1228 close(parentfd);
1229}
1230
1231// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
1232void fs_chroot(const char *rootdir) {
1233 assert(rootdir);
1234
1235 // mount-bind a /dev in rootdir
1236 char *newdev;
1237 if (asprintf(&newdev, "%s/dev", rootdir) == -1)
1238 errExit("asprintf");
1239 if (arg_debug)
1240 printf("Mounting /dev on %s\n", newdev);
1241 if (mount("/dev", newdev, NULL, MS_BIND|MS_REC, NULL) < 0)
1242 errExit("mounting /dev");
1243 free(newdev);
1244
1245 // mount a new proc filesystem
1246 char *newproc;
1247 if (asprintf(&newproc, "%s/proc", rootdir) == -1)
1248 errExit("asprintf");
1249 if (arg_debug)
1250 printf("Mounting /proc filesystem on %s\n", newproc);
1251 if (mount("proc", newproc, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
1252 errExit("mounting /proc");
1253 free(newproc);
1254
1255 // x11
1256 if (getenv("FIREJAIL_X11")) {
1257 char *newx11;
1258 if (asprintf(&newx11, "%s/tmp/.X11-unix", rootdir) == -1)
1259 errExit("asprintf");
1260 if (arg_debug)
1261 printf("Mounting /tmp/.X11-unix on %s\n", newx11);
1262 if (mount("/tmp/.X11-unix", newx11, NULL, MS_BIND|MS_REC, NULL) < 0)
1263 errExit("mounting /tmp/.X11-unix");
1264 free(newx11);
1265 }
1266
1267 // some older distros don't have a /run directory
1268 // create one by default
1269 char *rundir;
1270 if (asprintf(&rundir, "%s/run", rootdir) == -1)
1271 errExit("asprintf");
1272 struct stat s;
1273 if (lstat(rundir, &s) == 0) {
1274 if (S_ISLNK(s.st_mode)) {
1275 fprintf(stderr, "Error: chroot /run is a symbolic link\n");
1276 exit(1);
1277 }
1278 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1279 fprintf(stderr, "Error: chroot /run should be a directory owned by root\n");
1280 exit(1);
1281 }
1282 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
1283 fprintf(stderr, "Error: only root user should be given write permission on chroot /run\n");
1284 exit(1);
1285 }
1286 }
1287 else {
1288 // several sandboxes could race to create /run
1289 if (mkdir(rundir, 0755) == -1 && errno != EEXIST)
1290 errExit("mkdir");
1291 ASSERT_PERMS(rundir, 0, 0, 0755);
1292 }
1293 free(rundir);
1294
1295 // create /run/firejail directory in chroot
1296 if (asprintf(&rundir, "%s/run/firejail", rootdir) == -1)
1297 errExit("asprintf");
1298 if (mkdir(rundir, 0755) == -1 && errno != EEXIST)
1299 errExit("mkdir");
1300 ASSERT_PERMS(rundir, 0, 0, 0755);
1301 free(rundir);
1302
1303 // create /run/firejail/lib directory in chroot and mount it
1304 if (asprintf(&rundir, "%s%s", rootdir, RUN_FIREJAIL_LIB_DIR) == -1)
1305 errExit("asprintf");
1306 if (mkdir(rundir, 0755) == -1 && errno != EEXIST)
1307 errExit("mkdir");
1308 ASSERT_PERMS(rundir, 0, 0, 0755);
1309 if (mount(RUN_FIREJAIL_LIB_DIR, rundir, NULL, MS_BIND|MS_REC, NULL) < 0)
1310 errExit("mount bind");
1311 free(rundir);
1312
1313 // create /run/firejail/mnt directory in chroot and mount the current one
1314 if (asprintf(&rundir, "%s%s", rootdir, RUN_MNT_DIR) == -1)
1315 errExit("asprintf");
1316 if (mkdir(rundir, 0755) == -1 && errno != EEXIST)
1317 errExit("mkdir");
1318 ASSERT_PERMS(rundir, 0, 0, 0755);
1319 if (mount(RUN_MNT_DIR, rundir, NULL, MS_BIND|MS_REC, NULL) < 0)
1320 errExit("mount bind");
1321 free(rundir);
1322
1323 // copy /etc/resolv.conf in chroot directory
1324 char *fname;
1325 if (asprintf(&fname, "%s/etc/resolv.conf", rootdir) == -1)
1326 errExit("asprintf");
1327 if (arg_debug)
1328 printf("Updating /etc/resolv.conf in %s\n", fname);
1329 unlink(fname);
1330 if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) // root needed
1331 fwarning("/etc/resolv.conf not initialized\n");
1332 free(fname);
1333
1334 // chroot into the new directory
1335#ifdef HAVE_GCOV
1336 __gcov_flush();
1337#endif
1338 // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay
1339 // and chroot into this new directory
1340 if (arg_debug)
1341 printf("Chrooting into %s\n", rootdir);
1342 char *oroot = RUN_OVERLAY_ROOT;
1343 if (mkdir(oroot, 0755) == -1)
1344 errExit("mkdir");
1345 if (mount(rootdir, oroot, NULL, MS_BIND|MS_REC, NULL) < 0)
1346 errExit("mounting rootdir oroot");
1347 if (chroot(oroot) < 0)
1348 errExit("chroot");
1349
1350 // create all other /run/firejail files and directories
1351 preproc_build_firejail_dir();
1352
1353 // update /var directory in order to support multiple sandboxes running on the same root directory
1354// if (!arg_private_dev)
1355// fs_dev_shm();
1356 fs_var_lock();
1357 if (!arg_keep_var_tmp)
1358 fs_var_tmp();
1359 if (!arg_writable_var_log)
1360 fs_var_log();
1361
1362 fs_var_lib();
1363 fs_var_cache();
1364 fs_var_utmp();
1365 fs_machineid();
1366
1367 // don't leak user information
1368 restrict_users();
1369
1370 // when starting as root, firejail config is not disabled;
1371 if (getuid() != 0)
1372 disable_config();
1373}
1374#endif
1375
1376// this function is called from sandbox.c before blacklist/whitelist functions 1127// this function is called from sandbox.c before blacklist/whitelist functions
1377void fs_private_tmp(void) { 1128void fs_private_tmp(void) {
1378 // check XAUTHORITY file, KDE keeps it under /tmp 1129 // check XAUTHORITY file, KDE keeps it under /tmp
diff --git a/src/firejail/main.c b/src/firejail/main.c
index e8664e914..cbe3292ba 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -1659,12 +1659,14 @@ int main(int argc, char **argv) {
1659 fprintf(stderr, "Error: --chroot option is not available on Grsecurity systems\n"); 1659 fprintf(stderr, "Error: --chroot option is not available on Grsecurity systems\n");
1660 exit(1); 1660 exit(1);
1661 } 1661 }
1662
1663
1664 invalid_filename(argv[i] + 9, 0); // no globbing
1665
1666 // extract chroot dirname 1662 // extract chroot dirname
1667 cfg.chrootdir = argv[i] + 9; 1663 cfg.chrootdir = argv[i] + 9;
1664 if (*cfg.chrootdir == '\0') {
1665 fprintf(stderr, "Error: invalid chroot option\n");
1666 exit(1);
1667 }
1668 invalid_filename(cfg.chrootdir, 0); // no globbing
1669
1668 // if the directory starts with ~, expand the home directory 1670 // if the directory starts with ~, expand the home directory
1669 if (*cfg.chrootdir == '~') { 1671 if (*cfg.chrootdir == '~') {
1670 char *tmp; 1672 char *tmp;
@@ -1672,22 +1674,8 @@ int main(int argc, char **argv) {
1672 errExit("asprintf"); 1674 errExit("asprintf");
1673 cfg.chrootdir = tmp; 1675 cfg.chrootdir = tmp;
1674 } 1676 }
1675 1677 // check chroot directory
1676 if (strstr(cfg.chrootdir, "..") || is_link(cfg.chrootdir)) { 1678 fs_check_chroot_dir();
1677 fprintf(stderr, "Error: invalid chroot directory %s\n", cfg.chrootdir);
1678 return 1;
1679 }
1680
1681 // check chroot dirname exists, don't allow "--chroot=/"
1682 char *rpath = realpath(cfg.chrootdir, NULL);
1683 if (rpath == NULL || strcmp(rpath, "/") == 0) {
1684 fprintf(stderr, "Error: invalid chroot directory\n");
1685 exit(1);
1686 }
1687 cfg.chrootdir = rpath;
1688
1689 // check chroot directory structure
1690 fs_check_chroot_dir(cfg.chrootdir);
1691 } 1679 }
1692 else 1680 else
1693 exit_err_feature("chroot"); 1681 exit_err_feature("chroot");
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 288726d22..80b595a9f 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -578,7 +578,7 @@ static void enforce_filters(void) {
578 force_nonewprivs = 1; 578 force_nonewprivs = 1;
579 579
580 // disable all capabilities 580 // disable all capabilities
581 fmessage("\n** Warning: dropping all Linux capabilities **\n"); 581 fmessage("\n** Warning: dropping all Linux capabilities **\n\n");
582 arg_caps_drop_all = 1; 582 arg_caps_drop_all = 1;
583 583
584 // drop all supplementary groups; /etc/group file inside chroot 584 // drop all supplementary groups; /etc/group file inside chroot