aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2019-09-29 17:58:27 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2019-09-29 17:58:27 +0200
commit94aba62229f083173cb28ffe370eaa9f3ee540b9 (patch)
treeb1a70e5ff502df77bf0ddc45e82f11547677f09a /src
parentmove chroot from path based to file descriptor based mounts (diff)
downloadfirejail-94aba62229f083173cb28ffe370eaa9f3ee540b9.tar.gz
firejail-94aba62229f083173cb28ffe370eaa9f3ee540b9.tar.zst
firejail-94aba62229f083173cb28ffe370eaa9f3ee540b9.zip
chroot module
Diffstat (limited to 'src')
-rw-r--r--src/firejail/chroot.c309
-rw-r--r--src/firejail/firejail.h10
-rw-r--r--src/firejail/fs.c284
3 files changed, 319 insertions, 284 deletions
diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c
new file mode 100644
index 000000000..c99af1d52
--- /dev/null
+++ b/src/firejail/chroot.c
@@ -0,0 +1,309 @@
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/stat.h>
25#include <sys/sendfile.h>
26#include <errno.h>
27
28#include <fcntl.h>
29#ifndef O_PATH
30# define O_PATH 010000000
31#endif
32
33
34// exit if error
35static void fs_check_chroot_subdir(const char *subdir, int parentfd, int check_writable) {
36 assert(subdir);
37 int fd = openat(parentfd, subdir, O_PATH|O_CLOEXEC);
38 if (fd == -1) {
39 if (errno == ENOENT)
40 fprintf(stderr, "Error: cannot find /%s in chroot directory\n", subdir);
41 else {
42 perror("open");
43 fprintf(stderr, "Error: cannot open /%s in chroot directory\n", subdir);
44 }
45 exit(1);
46 }
47 struct stat s;
48 if (fstat(fd, &s) == -1)
49 errExit("fstat");
50 close(fd);
51 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
52 fprintf(stderr, "Error: chroot /%s should be a directory owned by root\n", subdir);
53 exit(1);
54 }
55 if (check_writable && ((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
56 fprintf(stderr, "Error: only root user should be given write permission on chroot /%s\n", subdir);
57 exit(1);
58 }
59}
60
61// exit if error
62void fs_check_chroot_dir(const char *rootdir) {
63 EUID_ASSERT();
64 assert(rootdir);
65
66 char *overlay;
67 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1)
68 errExit("asprintf");
69 if (strncmp(rootdir, overlay, strlen(overlay)) == 0) {
70 fprintf(stderr, "Error: invalid chroot directory: no directories in %s are allowed\n", overlay);
71 exit(1);
72 }
73 free(overlay);
74
75 // fails if there is any symlink or if rootdir is not a directory
76 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
77 if (parentfd == -1) {
78 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir);
79 exit(1);
80 }
81 // rootdir has to be owned by root and is not allowed to be generally writable,
82 // this also excludes /tmp, /var/tmp and such
83 struct stat s;
84 if (fstat(parentfd, &s) == -1)
85 errExit("fstat");
86 if (s.st_uid != 0) {
87 fprintf(stderr, "Error: chroot directory should be owned by root\n");
88 exit(1);
89 }
90 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
91 fprintf(stderr, "Error: only root user should be given write permission on chroot directory\n");
92 exit(1);
93 }
94
95 // check subdirectories in rootdir
96 fs_check_chroot_subdir("dev", parentfd, 0);
97 fs_check_chroot_subdir("etc", parentfd, 1);
98 fs_check_chroot_subdir("proc", parentfd, 0);
99 fs_check_chroot_subdir("tmp", parentfd, 0);
100 fs_check_chroot_subdir("var/tmp", parentfd, 0);
101
102 // there should be no checking on <chrootdir>/etc/resolv.conf
103 // the file is replaced with the real /etc/resolv.conf anyway
104#if 0
105 if (asprintf(&name, "%s/etc/resolv.conf", rootdir) == -1)
106 errExit("asprintf");
107 if (stat(name, &s) == 0) {
108 if (s.st_uid != 0) {
109 fprintf(stderr, "Error: chroot /etc/resolv.conf should be owned by root\n");
110 exit(1);
111 }
112 }
113 else {
114 fprintf(stderr, "Error: chroot /etc/resolv.conf not found\n");
115 exit(1);
116 }
117 // on Arch /etc/resolv.conf could be a symlink to /run/systemd/resolve/resolv.conf
118 // on Ubuntu 17.04 /etc/resolv.conf could be a symlink to /run/resolveconf/resolv.conf
119 if (is_link(name)) {
120 // check the link points in chroot
121 char *rname = realpath(name, NULL);
122 if (!rname || strncmp(rname, rootdir, strlen(rootdir)) != 0) {
123 fprintf(stderr, "Error: chroot /etc/resolv.conf is pointing outside chroot\n");
124 exit(1);
125 }
126 }
127 free(name);
128#endif
129
130 // check x11 socket directory
131 if (getenv("FIREJAIL_X11"))
132 fs_check_chroot_subdir("tmp/.X11-unix", parentfd, 0);
133
134 close(parentfd);
135}
136
137// copy /etc/resolv.conf in chroot directory
138static void copy_resolvconf(int parentfd) {
139 int in = open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC);
140 if (in == -1) {
141 fwarning("/etc/resolv.conf not initialized\n");
142 return;
143 }
144 struct stat instat;
145 if (fstat(in, &instat) == -1)
146 errExit("fstat");
147 // try to detect if resolv.conf has been bind mounted into the chroot
148 // do nothing in this case in order to not truncate the real file
149 struct stat outstat;
150 if (fstatat(parentfd, "etc/resolv.conf", &outstat, 0) == 0) {
151 if (instat.st_dev == outstat.st_dev && instat.st_ino == outstat.st_ino) {
152 close(in);
153 return;
154 }
155 }
156 if (arg_debug)
157 printf("Updating /etc/resolv.conf in chroot\n");
158 int out = openat(parentfd, "etc/resolv.conf", O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH);
159 if (out == -1)
160 errExit("open");
161 if (sendfile(out, in, NULL, instat.st_size) == -1)
162 errExit("sendfile");
163 close(in);
164 close(out);
165}
166
167// chroot into an existing directory; mount existing /dev and update /etc/resolv.conf
168void fs_chroot(const char *rootdir) {
169 assert(rootdir);
170
171 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
172 if (parentfd == -1)
173 errExit("safe_fd");
174
175 // mount-bind a /dev in rootdir
176 if (arg_debug)
177 printf("Mounting /dev on chroot /dev\n");
178 int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_CLOEXEC);
179 if (fd == -1)
180 errExit("open");
181 char *proc;
182 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
183 errExit("asprintf");
184 if (mount("/dev", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
185 errExit("mounting /dev");
186 free(proc);
187 close(fd);
188
189 // mount a brand new proc filesystem
190 if (arg_debug)
191 printf("Mounting /proc filesystem on chroot /proc\n");
192 fd = openat(parentfd, "proc", O_PATH|O_DIRECTORY|O_CLOEXEC);
193 if (fd == -1)
194 errExit("open");
195 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
196 errExit("asprintf");
197 if (mount("proc", proc, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
198 errExit("mounting /proc");
199 free(proc);
200 close(fd);
201
202 // x11
203 if (getenv("FIREJAIL_X11")) {
204 if (arg_debug)
205 printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n");
206 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_CLOEXEC);
207 if (fd == -1)
208 errExit("open");
209 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
210 errExit("asprintf");
211 if (mount("/tmp/.X11-unix", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
212 errExit("mounting /tmp/.X11-unix");
213 free(proc);
214 close(fd);
215 }
216
217 // update chroot resolv.conf
218 copy_resolvconf(parentfd);
219
220 // some older distros don't have a /run directory, create one by default
221 struct stat s;
222 if (fstatat(parentfd, "run", &s, AT_SYMLINK_NOFOLLOW) == 0) {
223 if (S_ISLNK(s.st_mode)) {
224 fprintf(stderr, "Error: chroot /run is a symbolic link\n");
225 exit(1);
226 }
227 }
228 else if (mkdirat(parentfd, "run", 0755) == -1 && errno != EEXIST)
229 errExit("mkdir");
230 fs_check_chroot_subdir("run", parentfd, 1);
231
232 // create /run/firejail directory in chroot
233 if (mkdirat(parentfd, RUN_FIREJAIL_DIR+1, 0755) == -1 && errno != EEXIST)
234 errExit("mkdir");
235
236 // create /run/firejail/lib directory in chroot
237 if (mkdirat(parentfd, RUN_FIREJAIL_LIB_DIR+1, 0755) == -1 && errno != EEXIST)
238 errExit("mkdir");
239 // mount lib directory into the chroot
240 fd = openat(parentfd, RUN_FIREJAIL_LIB_DIR+1, O_PATH|O_DIRECTORY|O_CLOEXEC);
241 if (fd == -1)
242 errExit("open");
243 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
244 errExit("asprintf");
245 if (mount(RUN_FIREJAIL_LIB_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
246 errExit("mount bind");
247 free(proc);
248 close(fd);
249
250 // create /run/firejail/mnt directory in chroot
251 if (mkdirat(parentfd, RUN_MNT_DIR+1, 0755) == -1 && errno != EEXIST)
252 errExit("mkdir");
253 // mount the current mnt directory into the chroot
254 fd = openat(parentfd, RUN_MNT_DIR+1, O_PATH|O_DIRECTORY|O_CLOEXEC);
255 if (fd == -1)
256 errExit("open");
257 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
258 errExit("asprintf");
259 if (mount(RUN_MNT_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
260 errExit("mount bind");
261 free(proc);
262 close(fd);
263
264#ifdef HAVE_GCOV
265 __gcov_flush();
266#endif
267 // create /run/firejail/mnt/oroot
268 char *oroot = RUN_OVERLAY_ROOT;
269 if (mkdir(oroot, 0755) == -1)
270 errExit("mkdir");
271 // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay
272 if (asprintf(&proc, "/proc/self/fd/%d", parentfd) == -1)
273 errExit("asprintf");
274 if (mount(proc, oroot, NULL, MS_BIND|MS_REC, NULL) < 0)
275 errExit("mounting rootdir oroot");
276 free(proc);
277 close(parentfd);
278 // chroot into the new directory
279 if (arg_debug)
280 printf("Chrooting into %s\n", rootdir);
281 if (chroot(oroot) < 0)
282 errExit("chroot");
283
284 // create all other /run/firejail files and directories
285 preproc_build_firejail_dir();
286
287 // update /var directory in order to support multiple sandboxes running on the same root directory
288 // if (!arg_private_dev)
289 // fs_dev_shm();
290 fs_var_lock();
291 if (!arg_keep_var_tmp)
292 fs_var_tmp();
293 if (!arg_writable_var_log)
294 fs_var_log();
295
296 fs_var_lib();
297 fs_var_cache();
298 fs_var_utmp();
299 fs_machineid();
300
301 // don't leak user information
302 restrict_users();
303
304 // when starting as root, firejail config is not disabled;
305 if (getuid() != 0)
306 disable_config();
307}
308
309#endif // HAVE_CHROOT
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index a6377261f..80cf71caf 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(const char *rootdir);
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 1c91d96d4..f2639f318 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -27,7 +27,7 @@
27#include <glob.h> 27#include <glob.h>
28#include <dirent.h> 28#include <dirent.h>
29#include <errno.h> 29#include <errno.h>
30#include <sys/sendfile.h> 30
31 31
32#include <fcntl.h> 32#include <fcntl.h>
33#ifndef O_PATH 33#ifndef O_PATH
@@ -697,8 +697,8 @@ void fs_proc_sys_dev_boot(void) {
697 } 697 }
698} 698}
699 699
700// disable firejail configuration in /etc/firejail and in ~/.config/firejail 700// disable firejail configuration in ~/.config/firejail
701static void disable_config(void) { 701void disable_config(void) {
702 struct stat s; 702 struct stat s;
703 703
704 char *fname; 704 char *fname;
@@ -1124,284 +1124,6 @@ void fs_overlayfs(void) {
1124} 1124}
1125#endif 1125#endif
1126 1126
1127
1128#ifdef HAVE_CHROOT
1129// exit if error
1130static void fs_check_chroot_subdir(const char *subdir, int parentfd, int check_writable) {
1131 assert(subdir);
1132 int fd = openat(parentfd, subdir, O_PATH|O_CLOEXEC);
1133 if (fd == -1) {
1134 if (errno == ENOENT)
1135 fprintf(stderr, "Error: cannot find /%s in chroot directory\n", subdir);
1136 else {
1137 perror("open");
1138 fprintf(stderr, "Error: cannot open /%s in chroot directory\n", subdir);
1139 }
1140 exit(1);
1141 }
1142 struct stat s;
1143 if (fstat(fd, &s) == -1)
1144 errExit("fstat");
1145 close(fd);
1146 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1147 fprintf(stderr, "Error: chroot /%s should be a directory owned by root\n", subdir);
1148 exit(1);
1149 }
1150 if (check_writable && ((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
1151 fprintf(stderr, "Error: only root user should be given write permission on chroot /%s\n", subdir);
1152 exit(1);
1153 }
1154}
1155
1156// exit if error
1157void fs_check_chroot_dir(const char *rootdir) {
1158 EUID_ASSERT();
1159 assert(rootdir);
1160
1161 char *overlay;
1162 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1)
1163 errExit("asprintf");
1164 if (strncmp(rootdir, overlay, strlen(overlay)) == 0) {
1165 fprintf(stderr, "Error: invalid chroot directory: no directories in %s are allowed\n", overlay);
1166 exit(1);
1167 }
1168 free(overlay);
1169
1170 // fails if there is any symlink or if rootdir is not a directory
1171 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1172 if (parentfd == -1) {
1173 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir);
1174 exit(1);
1175 }
1176 // rootdir has to be owned by root and is not allowed to be generally writable,
1177 // this also excludes /tmp, /var/tmp and such
1178 struct stat s;
1179 if (fstat(parentfd, &s) == -1)
1180 errExit("fstat");
1181 if (s.st_uid != 0) {
1182 fprintf(stderr, "Error: chroot directory should be owned by root\n");
1183 exit(1);
1184 }
1185 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
1186 fprintf(stderr, "Error: only root user should be given write permission on chroot directory\n");
1187 exit(1);
1188 }
1189
1190 // check subdirectories in rootdir
1191 fs_check_chroot_subdir("dev", parentfd, 0);
1192 fs_check_chroot_subdir("etc", parentfd, 1);
1193 fs_check_chroot_subdir("proc", parentfd, 0);
1194 fs_check_chroot_subdir("tmp", parentfd, 0);
1195 fs_check_chroot_subdir("var/tmp", parentfd, 0);
1196
1197 // there should be no checking on <chrootdir>/etc/resolv.conf
1198 // the file is replaced with the real /etc/resolv.conf anyway
1199#if 0
1200 if (asprintf(&name, "%s/etc/resolv.conf", rootdir) == -1)
1201 errExit("asprintf");
1202 if (stat(name, &s) == 0) {
1203 if (s.st_uid != 0) {
1204 fprintf(stderr, "Error: chroot /etc/resolv.conf should be owned by root\n");
1205 exit(1);
1206 }
1207 }
1208 else {
1209 fprintf(stderr, "Error: chroot /etc/resolv.conf not found\n");
1210 exit(1);
1211 }
1212 // on Arch /etc/resolv.conf could be a symlink to /run/systemd/resolve/resolv.conf
1213 // on Ubuntu 17.04 /etc/resolv.conf could be a symlink to /run/resolveconf/resolv.conf
1214 if (is_link(name)) {
1215 // check the link points in chroot
1216 char *rname = realpath(name, NULL);
1217 if (!rname || strncmp(rname, rootdir, strlen(rootdir)) != 0) {
1218 fprintf(stderr, "Error: chroot /etc/resolv.conf is pointing outside chroot\n");
1219 exit(1);
1220 }
1221 }
1222 free(name);
1223#endif
1224
1225 // check x11 socket directory
1226 if (getenv("FIREJAIL_X11"))
1227 fs_check_chroot_subdir("tmp/.X11-unix", parentfd, 0);
1228
1229 close(parentfd);
1230}
1231
1232// copy /etc/resolv.conf in chroot directory
1233static void copy_resolvconf(int parentfd) {
1234 int in = open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC);
1235 if (in == -1) {
1236 fwarning("/etc/resolv.conf not initialized\n");
1237 return;
1238 }
1239 struct stat instat;
1240 if (fstat(in, &instat) == -1)
1241 errExit("fstat");
1242 // try to detect if resolv.conf has been bind mounted into the chroot
1243 // do nothing in this case in order to not truncate the real file
1244 struct stat outstat;
1245 if (fstatat(parentfd, "etc/resolv.conf", &outstat, 0) == 0) {
1246 if (instat.st_dev == outstat.st_dev && instat.st_ino == outstat.st_ino) {
1247 close(in);
1248 return;
1249 }
1250 }
1251 if (arg_debug)
1252 printf("Updating /etc/resolv.conf in chroot\n");
1253 int out = openat(parentfd, "etc/resolv.conf", O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH);
1254 if (out == -1)
1255 errExit("open");
1256 if (sendfile(out, in, NULL, instat.st_size) == -1)
1257 errExit("sendfile");
1258 close(in);
1259 close(out);
1260}
1261
1262// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
1263void fs_chroot(const char *rootdir) {
1264 assert(rootdir);
1265
1266 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1267 if (parentfd == -1)
1268 errExit("safe_fd");
1269
1270 // mount-bind a /dev in rootdir
1271 if (arg_debug)
1272 printf("Mounting /dev on chroot /dev\n");
1273 int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_CLOEXEC);
1274 if (fd == -1)
1275 errExit("open");
1276 char *proc;
1277 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
1278 errExit("asprintf");
1279 if (mount("/dev", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
1280 errExit("mounting /dev");
1281 free(proc);
1282 close(fd);
1283
1284 // mount a brand new proc filesystem
1285 if (arg_debug)
1286 printf("Mounting /proc filesystem on chroot /proc\n");
1287 fd = openat(parentfd, "proc", O_PATH|O_DIRECTORY|O_CLOEXEC);
1288 if (fd == -1)
1289 errExit("open");
1290 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
1291 errExit("asprintf");
1292 if (mount("proc", proc, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
1293 errExit("mounting /proc");
1294 free(proc);
1295 close(fd);
1296
1297 // x11
1298 if (getenv("FIREJAIL_X11")) {
1299 if (arg_debug)
1300 printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n");
1301 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_CLOEXEC);
1302 if (fd == -1)
1303 errExit("open");
1304 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
1305 errExit("asprintf");
1306 if (mount("/tmp/.X11-unix", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
1307 errExit("mounting /tmp/.X11-unix");
1308 free(proc);
1309 close(fd);
1310 }
1311
1312 // update chroot resolv.conf
1313 copy_resolvconf(parentfd);
1314
1315 // some older distros don't have a /run directory, create one by default
1316 struct stat s;
1317 if (fstatat(parentfd, "run", &s, AT_SYMLINK_NOFOLLOW) == 0) {
1318 if (S_ISLNK(s.st_mode)) {
1319 fprintf(stderr, "Error: chroot /run is a symbolic link\n");
1320 exit(1);
1321 }
1322 }
1323 else if (mkdirat(parentfd, "run", 0755) == -1 && errno != EEXIST)
1324 errExit("mkdir");
1325 fs_check_chroot_subdir("run", parentfd, 1);
1326
1327 // create /run/firejail directory in chroot
1328 if (mkdirat(parentfd, RUN_FIREJAIL_DIR+1, 0755) == -1 && errno != EEXIST)
1329 errExit("mkdir");
1330
1331 // create /run/firejail/lib directory in chroot
1332 if (mkdirat(parentfd, RUN_FIREJAIL_LIB_DIR+1, 0755) == -1 && errno != EEXIST)
1333 errExit("mkdir");
1334 // mount lib directory into the chroot
1335 fd = openat(parentfd, RUN_FIREJAIL_LIB_DIR+1, O_PATH|O_DIRECTORY|O_CLOEXEC);
1336 if (fd == -1)
1337 errExit("open");
1338 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
1339 errExit("asprintf");
1340 if (mount(RUN_FIREJAIL_LIB_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
1341 errExit("mount bind");
1342 free(proc);
1343 close(fd);
1344
1345 // create /run/firejail/mnt directory in chroot
1346 if (mkdirat(parentfd, RUN_MNT_DIR+1, 0755) == -1 && errno != EEXIST)
1347 errExit("mkdir");
1348 // mount the current mnt directory into the chroot
1349 fd = openat(parentfd, RUN_MNT_DIR+1, O_PATH|O_DIRECTORY|O_CLOEXEC);
1350 if (fd == -1)
1351 errExit("open");
1352 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
1353 errExit("asprintf");
1354 if (mount(RUN_MNT_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
1355 errExit("mount bind");
1356 free(proc);
1357 close(fd);
1358
1359#ifdef HAVE_GCOV
1360 __gcov_flush();
1361#endif
1362 // create /run/firejail/mnt/oroot
1363 char *oroot = RUN_OVERLAY_ROOT;
1364 if (mkdir(oroot, 0755) == -1)
1365 errExit("mkdir");
1366 // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay
1367 if (asprintf(&proc, "/proc/self/fd/%d", parentfd) == -1)
1368 errExit("asprintf");
1369 if (mount(proc, oroot, NULL, MS_BIND|MS_REC, NULL) < 0)
1370 errExit("mounting rootdir oroot");
1371 free(proc);
1372 close(parentfd);
1373 // chroot into the new directory
1374 if (arg_debug)
1375 printf("Chrooting into %s\n", rootdir);
1376 if (chroot(oroot) < 0)
1377 errExit("chroot");
1378
1379 // create all other /run/firejail files and directories
1380 preproc_build_firejail_dir();
1381
1382 // update /var directory in order to support multiple sandboxes running on the same root directory
1383// if (!arg_private_dev)
1384// fs_dev_shm();
1385 fs_var_lock();
1386 if (!arg_keep_var_tmp)
1387 fs_var_tmp();
1388 if (!arg_writable_var_log)
1389 fs_var_log();
1390
1391 fs_var_lib();
1392 fs_var_cache();
1393 fs_var_utmp();
1394 fs_machineid();
1395
1396 // don't leak user information
1397 restrict_users();
1398
1399 // when starting as root, firejail config is not disabled;
1400 if (getuid() != 0)
1401 disable_config();
1402}
1403#endif
1404
1405// this function is called from sandbox.c before blacklist/whitelist functions 1127// this function is called from sandbox.c before blacklist/whitelist functions
1406void fs_private_tmp(void) { 1128void fs_private_tmp(void) {
1407 // check XAUTHORITY file, KDE keeps it under /tmp 1129 // check XAUTHORITY file, KDE keeps it under /tmp