aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2019-09-29 17:26:32 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2019-09-29 17:26:32 +0200
commit9dfecbe49fbbd85714d64b18933600b0d7f88ff3 (patch)
treee511ec10f77ad78137d911a56887335e014d9992 /src
parentUpdate evince (diff)
downloadfirejail-9dfecbe49fbbd85714d64b18933600b0d7f88ff3.tar.gz
firejail-9dfecbe49fbbd85714d64b18933600b0d7f88ff3.tar.zst
firejail-9dfecbe49fbbd85714d64b18933600b0d7f88ff3.zip
move chroot from path based to file descriptor based mounts
Diffstat (limited to 'src')
-rw-r--r--src/firejail/fs.c181
1 files changed, 105 insertions, 76 deletions
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index ce2ca5e2a..1c91d96d4 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -27,6 +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#include <fcntl.h> 32#include <fcntl.h>
32#ifndef O_PATH 33#ifndef O_PATH
@@ -1228,122 +1229,150 @@ void fs_check_chroot_dir(const char *rootdir) {
1228 close(parentfd); 1229 close(parentfd);
1229} 1230}
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
1231// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf 1262// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
1232void fs_chroot(const char *rootdir) { 1263void fs_chroot(const char *rootdir) {
1233 assert(rootdir); 1264 assert(rootdir);
1234 1265
1266 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1267 if (parentfd == -1)
1268 errExit("safe_fd");
1269
1235 // mount-bind a /dev in rootdir 1270 // mount-bind a /dev in rootdir
1236 char *newdev;
1237 if (asprintf(&newdev, "%s/dev", rootdir) == -1)
1238 errExit("asprintf");
1239 if (arg_debug) 1271 if (arg_debug)
1240 printf("Mounting /dev on %s\n", newdev); 1272 printf("Mounting /dev on chroot /dev\n");
1241 if (mount("/dev", newdev, NULL, MS_BIND|MS_REC, NULL) < 0) 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)
1242 errExit("mounting /dev"); 1280 errExit("mounting /dev");
1243 free(newdev); 1281 free(proc);
1282 close(fd);
1244 1283
1245 // mount a new proc filesystem 1284 // mount a brand new proc filesystem
1246 char *newproc;
1247 if (asprintf(&newproc, "%s/proc", rootdir) == -1)
1248 errExit("asprintf");
1249 if (arg_debug) 1285 if (arg_debug)
1250 printf("Mounting /proc filesystem on %s\n", newproc); 1286 printf("Mounting /proc filesystem on chroot /proc\n");
1251 if (mount("proc", newproc, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) 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)
1252 errExit("mounting /proc"); 1293 errExit("mounting /proc");
1253 free(newproc); 1294 free(proc);
1295 close(fd);
1254 1296
1255 // x11 1297 // x11
1256 if (getenv("FIREJAIL_X11")) { 1298 if (getenv("FIREJAIL_X11")) {
1257 char *newx11;
1258 if (asprintf(&newx11, "%s/tmp/.X11-unix", rootdir) == -1)
1259 errExit("asprintf");
1260 if (arg_debug) 1299 if (arg_debug)
1261 printf("Mounting /tmp/.X11-unix on %s\n", newx11); 1300 printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n");
1262 if (mount("/tmp/.X11-unix", newx11, NULL, MS_BIND|MS_REC, NULL) < 0) 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)
1263 errExit("mounting /tmp/.X11-unix"); 1307 errExit("mounting /tmp/.X11-unix");
1264 free(newx11); 1308 free(proc);
1309 close(fd);
1265 } 1310 }
1266 1311
1267 // some older distros don't have a /run directory 1312 // update chroot resolv.conf
1268 // create one by default 1313 copy_resolvconf(parentfd);
1269 char *rundir; 1314
1270 if (asprintf(&rundir, "%s/run", rootdir) == -1) 1315 // some older distros don't have a /run directory, create one by default
1271 errExit("asprintf");
1272 struct stat s; 1316 struct stat s;
1273 if (lstat(rundir, &s) == 0) { 1317 if (fstatat(parentfd, "run", &s, AT_SYMLINK_NOFOLLOW) == 0) {
1274 if (S_ISLNK(s.st_mode)) { 1318 if (S_ISLNK(s.st_mode)) {
1275 fprintf(stderr, "Error: chroot /run is a symbolic link\n"); 1319 fprintf(stderr, "Error: chroot /run is a symbolic link\n");
1276 exit(1); 1320 exit(1);
1277 } 1321 }
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 } 1322 }
1287 else { 1323 else if (mkdirat(parentfd, "run", 0755) == -1 && errno != EEXIST)
1288 // several sandboxes could race to create /run 1324 errExit("mkdir");
1289 if (mkdir(rundir, 0755) == -1 && errno != EEXIST) 1325 fs_check_chroot_subdir("run", parentfd, 1);
1290 errExit("mkdir");
1291 ASSERT_PERMS(rundir, 0, 0, 0755);
1292 }
1293 free(rundir);
1294 1326
1295 // create /run/firejail directory in chroot 1327 // create /run/firejail directory in chroot
1296 if (asprintf(&rundir, "%s/run/firejail", rootdir) == -1) 1328 if (mkdirat(parentfd, RUN_FIREJAIL_DIR+1, 0755) == -1 && errno != EEXIST)
1297 errExit("asprintf");
1298 if (mkdir(rundir, 0755) == -1 && errno != EEXIST)
1299 errExit("mkdir"); 1329 errExit("mkdir");
1300 ASSERT_PERMS(rundir, 0, 0, 0755);
1301 free(rundir);
1302 1330
1303 // create /run/firejail/lib directory in chroot and mount it 1331 // create /run/firejail/lib directory in chroot
1304 if (asprintf(&rundir, "%s%s", rootdir, RUN_FIREJAIL_LIB_DIR) == -1) 1332 if (mkdirat(parentfd, RUN_FIREJAIL_LIB_DIR+1, 0755) == -1 && errno != EEXIST)
1305 errExit("asprintf");
1306 if (mkdir(rundir, 0755) == -1 && errno != EEXIST)
1307 errExit("mkdir"); 1333 errExit("mkdir");
1308 ASSERT_PERMS(rundir, 0, 0, 0755); 1334 // mount lib directory into the chroot
1309 if (mount(RUN_FIREJAIL_LIB_DIR, rundir, NULL, MS_BIND|MS_REC, NULL) < 0) 1335 fd = openat(parentfd, RUN_FIREJAIL_LIB_DIR+1, O_PATH|O_DIRECTORY|O_CLOEXEC);
1310 errExit("mount bind"); 1336 if (fd == -1)
1311 free(rundir); 1337 errExit("open");
1312 1338 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
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"); 1339 errExit("asprintf");
1316 if (mkdir(rundir, 0755) == -1 && errno != EEXIST) 1340 if (mount(RUN_FIREJAIL_LIB_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
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"); 1341 errExit("mount bind");
1321 free(rundir); 1342 free(proc);
1343 close(fd);
1322 1344
1323 // copy /etc/resolv.conf in chroot directory 1345 // create /run/firejail/mnt directory in chroot
1324 char *fname; 1346 if (mkdirat(parentfd, RUN_MNT_DIR+1, 0755) == -1 && errno != EEXIST)
1325 if (asprintf(&fname, "%s/etc/resolv.conf", rootdir) == -1) 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)
1326 errExit("asprintf"); 1353 errExit("asprintf");
1327 if (arg_debug) 1354 if (mount(RUN_MNT_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
1328 printf("Updating /etc/resolv.conf in %s\n", fname); 1355 errExit("mount bind");
1329 unlink(fname); 1356 free(proc);
1330 if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) // root needed 1357 close(fd);
1331 fwarning("/etc/resolv.conf not initialized\n");
1332 free(fname);
1333 1358
1334 // chroot into the new directory
1335#ifdef HAVE_GCOV 1359#ifdef HAVE_GCOV
1336 __gcov_flush(); 1360 __gcov_flush();
1337#endif 1361#endif
1338 // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay 1362 // create /run/firejail/mnt/oroot
1339 // and chroot into this new directory
1340 if (arg_debug)
1341 printf("Chrooting into %s\n", rootdir);
1342 char *oroot = RUN_OVERLAY_ROOT; 1363 char *oroot = RUN_OVERLAY_ROOT;
1343 if (mkdir(oroot, 0755) == -1) 1364 if (mkdir(oroot, 0755) == -1)
1344 errExit("mkdir"); 1365 errExit("mkdir");
1345 if (mount(rootdir, oroot, NULL, MS_BIND|MS_REC, NULL) < 0) 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)
1346 errExit("mounting rootdir oroot"); 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);
1347 if (chroot(oroot) < 0) 1376 if (chroot(oroot) < 0)
1348 errExit("chroot"); 1377 errExit("chroot");
1349 1378