aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/fs.c')
-rw-r--r--src/firejail/fs.c825
1 files changed, 825 insertions, 0 deletions
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
new file mode 100644
index 000000000..1fc1c0942
--- /dev/null
+++ b/src/firejail/fs.c
@@ -0,0 +1,825 @@
1/*
2 * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
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#include "firejail.h"
21#include <sys/mount.h>
22#include <sys/stat.h>
23#include <linux/limits.h>
24#include <glob.h>
25#include <dirent.h>
26#include <fcntl.h>
27#include <errno.h>
28
29// build /tmp/firejail directory
30void fs_build_firejail_dir(void) {
31 struct stat s;
32
33 if (stat(FIREJAIL_DIR, &s)) {
34 if (arg_debug)
35 printf("Creating %s directory\n", FIREJAIL_DIR);
36 /* coverity[toctou] */
37 int rv = mkdir(FIREJAIL_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
38 if (rv == -1)
39 errExit("mkdir");
40 if (chown(FIREJAIL_DIR, 0, 0) < 0)
41 errExit("chown");
42 if (chmod(FIREJAIL_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
43 errExit("chmod");
44 }
45 else { // check /tmp/firejail directory belongs to root end exit if doesn't!
46 if (s.st_uid != 0 || s.st_gid != 0) {
47 fprintf(stderr, "Error: non-root %s directory, exiting...\n", FIREJAIL_DIR);
48 exit(1);
49 }
50 }
51}
52
53
54// build /tmp/firejail/mnt directory
55static int tmpfs_mounted = 0;
56void fs_build_mnt_dir(void) {
57 struct stat s;
58 fs_build_firejail_dir();
59
60 // create /tmp/firejail directory
61 if (stat(MNT_DIR, &s)) {
62 if (arg_debug)
63 printf("Creating %s directory\n", MNT_DIR);
64 /* coverity[toctou] */
65 int rv = mkdir(MNT_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
66 if (rv == -1)
67 errExit("mkdir");
68 if (chown(MNT_DIR, 0, 0) < 0)
69 errExit("chown");
70 if (chmod(MNT_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
71 errExit("chmod");
72 }
73
74 // ... and mount tmpfs on top of it
75 if (!tmpfs_mounted) {
76 // mount tmpfs on top of /tmp/firejail/mnt
77 if (arg_debug)
78 printf("Mounting tmpfs on %s directory\n", MNT_DIR);
79 if (mount("tmpfs", MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
80 errExit("mounting /tmp/firejail/mnt");
81 tmpfs_mounted = 1;
82 }
83}
84
85// build /tmp/firejail/overlay directory
86void fs_build_overlay_dir(void) {
87 struct stat s;
88 fs_build_firejail_dir();
89
90 // create /tmp/firejail directory
91 if (stat(OVERLAY_DIR, &s)) {
92 if (arg_debug)
93 printf("Creating %s directory\n", MNT_DIR);
94 /* coverity[toctou] */
95 int rv = mkdir(OVERLAY_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
96 if (rv == -1)
97 errExit("mkdir");
98 if (chown(OVERLAY_DIR, 0, 0) < 0)
99 errExit("chown");
100 if (chmod(OVERLAY_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
101 errExit("chmod");
102 }
103}
104
105
106
107
108
109//***********************************************
110// process profile file
111//***********************************************
112typedef enum {
113 BLACKLIST_FILE,
114 MOUNT_READONLY,
115 MOUNT_TMPFS,
116 OPERATION_MAX
117} OPERATION;
118
119
120static char *create_empty_dir(void) {
121 struct stat s;
122 fs_build_firejail_dir();
123
124 if (stat(RO_DIR, &s)) {
125 /* coverity[toctou] */
126 int rv = mkdir(RO_DIR, S_IRUSR | S_IXUSR);
127 if (rv == -1)
128 errExit("mkdir");
129 if (chown(RO_DIR, 0, 0) < 0)
130 errExit("chown");
131 }
132
133 return RO_DIR;
134}
135
136static char *create_empty_file(void) {
137 struct stat s;
138 fs_build_firejail_dir();
139
140 if (stat(RO_FILE, &s)) {
141 /* coverity[toctou] */
142 FILE *fp = fopen(RO_FILE, "w");
143 if (!fp)
144 errExit("fopen");
145 fclose(fp);
146 if (chown(RO_FILE, 0, 0) < 0)
147 errExit("chown");
148 if (chmod(RO_FILE, S_IRUSR) < 0)
149 errExit("chown");
150 }
151
152 return RO_FILE;
153}
154
155static void disable_file(OPERATION op, const char *fname, const char *emptydir, const char *emptyfile) {
156 assert(fname);
157 assert(emptydir);
158 assert(emptyfile);
159 assert(op <OPERATION_MAX);
160
161 // if the file is a link, follow the link
162 char *lnk = NULL;
163 if (is_link(fname)) {
164 lnk = get_link(fname);
165 if (lnk)
166 fname = lnk;
167 else
168 fprintf(stderr, "Warning: cannot follow link %s, skipping...\n", fname);
169 }
170
171 // if the file is not present, do nothing
172 struct stat s;
173 if (stat(fname, &s) == -1) {
174 if (lnk)
175 free(lnk);
176 return;
177 }
178
179 // modify the file
180 if (op == BLACKLIST_FILE) {
181 if (arg_debug)
182 printf("Disable %s\n", fname);
183 if (S_ISDIR(s.st_mode)) {
184 if (mount(emptydir, fname, "none", MS_BIND, "mode=400,gid=0") < 0)
185 errExit("disable file");
186 }
187 else {
188 if (mount(emptyfile, fname, "none", MS_BIND, "mode=400,gid=0") < 0)
189 errExit("disable file");
190 }
191 }
192 else if (op == MOUNT_READONLY) {
193 if (arg_debug)
194 printf("Mounting read-only %s\n", fname);
195 fs_rdonly(fname);
196 }
197 else if (op == MOUNT_TMPFS) {
198 if (S_ISDIR(s.st_mode)) {
199 if (arg_debug)
200 printf("Mounting tmpfs on %s\n", fname);
201 // preserve owner and mode for the directory
202 if (mount("tmpfs", fname, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, 0) < 0)
203 errExit("mounting tmpfs");
204 /* coverity[toctou] */
205 if (chown(fname, s.st_uid, s.st_gid) == -1)
206 errExit("mounting tmpfs chmod");
207 }
208 else
209 printf("Warning: %s is not a directory; cannot mount a tmpfs on top of it.\n", fname);
210 }
211 else
212 assert(0);
213
214 if (lnk)
215 free(lnk);
216}
217
218static void globbing(OPERATION op, const char *fname, const char *emptydir, const char *emptyfile) {
219 assert(fname);
220 assert(emptydir);
221 assert(emptyfile);
222
223 // filename globbing: expand * macro and continue processing for every single file
224 if (strchr(fname, '*')) {
225 glob_t globbuf;
226 globbuf.gl_offs = 0;
227 glob(fname, GLOB_DOOFFS, NULL, &globbuf);
228 int i;
229 for (i = 0; i < globbuf.gl_pathc; i++) {
230 assert(globbuf.gl_pathv[i]);
231 disable_file(op, globbuf.gl_pathv[i], emptydir, emptyfile);
232 }
233 }
234 else
235 disable_file(op, fname, emptydir, emptyfile);
236}
237
238static void expand_path(OPERATION op, const char *path, const char *fname, const char *emptydir, const char *emptyfile) {
239 assert(path);
240 assert(fname);
241 assert(emptydir);
242 assert(emptyfile);
243 char newname[strlen(path) + strlen(fname) + 1];
244 sprintf(newname, "%s%s", path, fname);
245
246 globbing(op, newname, emptydir, emptyfile);
247}
248
249// blacklist files or directoies by mounting empty files on top of them
250void fs_blacklist(const char *homedir) {
251 ProfileEntry *entry = cfg.profile;
252 if (!entry)
253 return;
254
255 char *emptydir = create_empty_dir();
256 char *emptyfile = create_empty_file();
257
258 while (entry) {
259 OPERATION op = OPERATION_MAX;
260 char *ptr;
261
262 // process blacklist command
263 if (strncmp(entry->data, "bind", 4) == 0) {
264 char *dname1 = entry->data + 5;
265 char *dname2 = split_comma(dname1);
266 if (dname2 == NULL) {
267 fprintf(stderr, "Error: second directory missing in bind command\n");
268 entry = entry->next;
269 continue;
270 }
271 struct stat s;
272 if (stat(dname1, &s) == -1) {
273 fprintf(stderr, "Error: cannot find directories for bind command\n");
274 entry = entry->next;
275 continue;
276 }
277 if (stat(dname2, &s) == -1) {
278 fprintf(stderr, "Error: cannot find directories for bind command\n");
279 entry = entry->next;
280 continue;
281 }
282
283 // mount --bind olddir newdir
284 if (arg_debug)
285 printf("Mount-bind %s on top of %s\n", dname1, dname2);
286 // preserve dname2 mode and ownership
287 if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0)
288 errExit("mount bind");
289 /* coverity[toctou] */
290 if (chown(dname2, s.st_uid, s.st_gid) == -1)
291 errExit("mount-bind chown");
292 /* coverity[toctou] */
293 if (chmod(dname2, s.st_mode) == -1)
294 errExit("mount-bind chmod");
295
296 entry = entry->next;
297 continue;
298 }
299
300 // process blacklist command
301 if (strncmp(entry->data, "blacklist", 9) == 0) {
302 ptr = entry->data + 10;
303 op = BLACKLIST_FILE;
304 }
305 else if (strncmp(entry->data, "read-only", 9) == 0) {
306 ptr = entry->data + 10;
307 op = MOUNT_READONLY;
308 }
309 else if (strncmp(entry->data, "tmpfs", 5) == 0) {
310 ptr = entry->data + 6;
311 op = MOUNT_TMPFS;
312 }
313 else {
314 fprintf(stderr, "Error: invalid profile line %s\n", entry->data);
315 entry = entry->next;
316 continue;
317 }
318
319 // replace home macro in blacklist array
320 char *new_name = NULL;
321 if (strncmp(ptr, "${HOME}", 7) == 0) {
322 if (asprintf(&new_name, "%s%s", homedir, ptr + 7) == -1)
323 errExit("asprintf");
324 ptr = new_name;
325 }
326
327 // expand path macro - look for the file in /bin, /usr/bin, /sbin and /usr/sbin directories
328 if (strncmp(ptr, "${PATH}", 7) == 0) {
329 expand_path(op, "/bin", ptr + 7, emptydir, emptyfile);
330 expand_path(op, "/sbin", ptr + 7, emptydir, emptyfile);
331 expand_path(op, "/usr/bin", ptr + 7, emptydir, emptyfile);
332 expand_path(op, "/usr/sbin", ptr + 7, emptydir, emptyfile);
333 }
334 else
335 globbing(op, ptr, emptydir, emptyfile);
336
337 if (new_name)
338 free(new_name);
339 entry = entry->next;
340 }
341}
342
343//***********************************************
344// mount namespace
345//***********************************************
346
347// remount a directory read-only
348void fs_rdonly(const char *dir) {
349 assert(dir);
350 // check directory exists
351 struct stat s;
352 int rv = stat(dir, &s);
353 if (rv == 0) {
354 // mount --bind /bin /bin
355 if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0)
356 errExit("mount read-only");
357 // mount --bind -o remount,ro /bin
358 if (mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0)
359 errExit("mount read-only");
360 }
361}
362void fs_rdonly_noexit(const char *dir) {
363 assert(dir);
364 // check directory exists
365 struct stat s;
366 int rv = stat(dir, &s);
367 if (rv == 0) {
368 int merr = 0;
369 // mount --bind /bin /bin
370 if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0)
371 merr = 1;
372 // mount --bind -o remount,ro /bin
373 if (mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0)
374 merr = 1;
375 if (merr)
376 fprintf(stderr, "Warning: cannot mount %s read-only\n", dir);
377 }
378}
379
380// mount /proc and /sys directories
381void fs_proc_sys_dev_boot(void) {
382 struct stat s;
383
384 if (arg_debug)
385 printf("Remounting /proc and /proc/sys filesystems\n");
386 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
387 errExit("mounting /proc");
388
389 // remount /proc/sys readonly
390 if (mount("/proc/sys", "/proc/sys", NULL, MS_BIND | MS_REC, NULL) < 0)
391 errExit("mounting /proc/sys");
392
393 if (mount(NULL, "/proc/sys", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, NULL) < 0)
394 errExit("mounting /proc/sys");
395
396
397 /* Mount a version of /sys that describes the network namespace */
398 if (arg_debug)
399 printf("Remounting /sys directory\n");
400 if (umount2("/sys", MNT_DETACH) < 0)
401 fprintf(stderr, "Warning: failed to unmount /sys\n");
402 if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0)
403 fprintf(stderr, "Warning: failed to mount /sys\n");
404
405// if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0)
406// errExit("mounting /sys");
407
408
409 // mounting firejail kernel module files
410 if (stat("/proc/firejail-uptime", &s) == 0) {
411 errno = 0;
412 FILE *fp = fopen("/proc/firejail", "w");
413 int cnt = 0;
414 while (errno == EBUSY && cnt < 10) {
415 if (!fp) {
416 int s = random();
417 s /= 200000;
418 usleep(s);
419 fp = fopen("/proc/firejail", "w");
420 }
421 else
422 break;
423 }
424 if (!fp) {
425 fprintf(stderr, "Error: cannot register sandbox with firejail-lkm\n");
426 exit(1);
427 }
428 if (fp) {
429 // registration
430 fprintf(fp, "register\n");
431 fflush(0);
432 // filtering x11 connect calls
433 if (arg_nox11) {
434 fprintf(fp, "no connect unix /tmp/.X11\n");
435 fflush(0);
436 printf("X11 access disabled\n");
437 }
438 if (arg_nodbus) {
439 fprintf(fp, "no connect unix /var/run/dbus/system_bus_socket\n");
440 fflush(0);
441 fprintf(fp, "no connect unix /tmp/dbus\n");
442 fflush(0);
443 printf("D-Bus access disabled\n");
444 }
445 fclose(fp);
446 if (mount("/proc/firejail-uptime", "/proc/uptime", NULL, MS_BIND|MS_REC, NULL) < 0)
447 fprintf(stderr, "Warning: cannot mount /proc/firejail-uptime\n");
448 }
449 }
450
451 // Disable SysRq
452 // a linux box can be shut down easily using the following commands (as root):
453 // # echo 1 > /proc/sys/kernel/sysrq
454 // #echo b > /proc/sysrq-trigger
455 // for more information see https://www.kernel.org/doc/Documentation/sysrq.txt
456 if (arg_debug)
457 printf("Disable /proc/sysrq-trigger\n");
458 fs_rdonly_noexit("/proc/sysrq-trigger");
459
460 // disable hotplug and uevent_helper
461 if (arg_debug)
462 printf("Disable /proc/sys/kernel/hotplug\n");
463 fs_rdonly_noexit("/proc/sys/kernel/hotplug");
464 if (arg_debug)
465 printf("Disable /sys/kernel/uevent_helper\n");
466 fs_rdonly_noexit("/sys/kernel/uevent_helper");
467
468 // read-only /proc/irq and /proc/bus
469 if (arg_debug)
470 printf("Disable /proc/irq\n");
471 fs_rdonly_noexit("/proc/irq");
472 if (arg_debug)
473 printf("Disable /proc/bus\n");
474 fs_rdonly_noexit("/proc/bus");
475
476 // disable /proc/kcore
477 disable_file(BLACKLIST_FILE, "/proc/kcore", "not used", "/dev/null");
478
479 // disable /proc/kallsyms
480 disable_file(BLACKLIST_FILE, "/proc/kallsyms", "not used", "/dev/null");
481
482 // disable /boot
483 if (stat("/boot", &s) == 0) {
484 if (arg_debug)
485 printf("Mounting a new /boot directory\n");
486 if (mount("tmpfs", "/boot", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
487 errExit("mounting /boot directory");
488 }
489
490 // disable /dev/port
491 if (stat("/dev/port", &s) == 0) {
492 disable_file(BLACKLIST_FILE, "/dev/port", "not used", "/dev/null");
493 }
494}
495
496static void sanitize_home(void) {
497 // extract current /home directory data
498 struct dirent *dir;
499 DIR *d = opendir("/home");
500 if (d == NULL)
501 return;
502
503 char *emptydir = create_empty_dir();
504 while ((dir = readdir(d))) {
505 if(strcmp(dir->d_name, "." ) == 0 || strcmp(dir->d_name, ".." ) == 0)
506 continue;
507
508 if (dir->d_type == DT_DIR ) {
509 // get properties
510 struct stat s;
511 char *name;
512 if (asprintf(&name, "/home/%s", dir->d_name) == -1)
513 continue;
514 if (stat(name, &s) == -1)
515 continue;
516 if (S_ISLNK(s.st_mode)) {
517 free(name);
518 continue;
519 }
520
521 if (strcmp(name, cfg.homedir) == 0)
522 continue;
523
524// printf("directory %u %u:%u #%s#\n",
525// s.st_mode,
526// s.st_uid,
527// s.st_gid,
528// name);
529
530 // disable directory
531 disable_file(BLACKLIST_FILE, name, emptydir, "not used");
532 free(name);
533 }
534 }
535 closedir(d);
536}
537
538
539
540
541
542
543// build a basic read-only filesystem
544void fs_basic_fs(void) {
545 if (arg_debug)
546 printf("Mounting read-only /bin, /sbin, /lib, /lib64, /usr, /etc, /var\n");
547 fs_rdonly("/bin");
548 fs_rdonly("/sbin");
549 fs_rdonly("/lib");
550 fs_rdonly("/lib64");
551 fs_rdonly("/usr");
552 fs_rdonly("/etc");
553 fs_rdonly("/var");
554
555 // update /var directory in order to support multiple sandboxes running on the same root directory
556 if (!arg_private_dev)
557 fs_dev_shm();
558 fs_var_lock();
559 fs_var_tmp();
560 fs_var_log();
561 fs_var_lib();
562 fs_var_cache();
563 fs_var_utmp();
564
565 // only in user mode
566 if (getuid())
567 sanitize_home();
568}
569
570
571// mount overlayfs on top of / directory
572// mounting an overlay and chrooting into it:
573//
574// Old Ubuntu kernel
575// # cd ~
576// # mkdir -p overlay/root
577// # mkdir -p overlay/diff
578// # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root
579// # chroot /root/overlay/root
580// to shutdown, first exit the chroot and then unmount the overlay
581// # exit
582// # umount /root/overlay/root
583//
584// Kernels 3.18+
585// # cd ~
586// # mkdir -p overlay/root
587// # mkdir -p overlay/diff
588// # mkdir -p overlay/work
589// # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root
590// # cat /etc/mtab | grep overlay
591// /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0
592// # chroot /root/overlay/root
593// to shutdown, first exit the chroot and then unmount the overlay
594// # exit
595// # umount /root/overlay/root
596
597
598// to do: fix the code below; also, it might work without /dev; impose seccomp/caps filters when not root
599#include <sys/utsname.h>
600void fs_overlayfs(void) {
601 // check kernel version
602 struct utsname u;
603 int rv = uname(&u);
604 if (rv != 0)
605 errExit("uname");
606 int major;
607 int minor;
608 if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
609 fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
610 exit(1);
611 }
612
613 if (arg_debug)
614 printf("Linux kernel version %d.%d\n", major, minor);
615 int oldkernel = 0;
616 if (major < 3) {
617 fprintf(stderr, "Error: minimum kernel version required 3.x\n");
618 exit(1);
619 }
620 if (major == 3 && minor < 18)
621 oldkernel = 1;
622
623 // build overlay directories
624 fs_build_mnt_dir();
625
626 char *oroot;
627 if(asprintf(&oroot, "%s/oroot", MNT_DIR) == -1)
628 errExit("asprintf");
629 if (mkdir(oroot, S_IRWXU | S_IRWXG | S_IRWXO))
630 errExit("mkdir");
631 if (chown(oroot, 0, 0) < 0)
632 errExit("chown");
633 if (chmod(oroot, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
634 errExit("chmod");
635
636 char *odiff;
637 if(asprintf(&odiff, "%s/odiff", MNT_DIR) == -1)
638 errExit("asprintf");
639 if (mkdir(odiff, S_IRWXU | S_IRWXG | S_IRWXO))
640 errExit("mkdir");
641 if (chown(odiff, 0, 0) < 0)
642 errExit("chown");
643 if (chmod(odiff, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
644 errExit("chmod");
645
646 char *owork;
647 if(asprintf(&owork, "%s/owork", MNT_DIR) == -1)
648 errExit("asprintf");
649 if (mkdir(owork, S_IRWXU | S_IRWXG | S_IRWXO))
650 errExit("mkdir");
651 if (chown(owork, 0, 0) < 0)
652 errExit("chown");
653 if (chmod(owork, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
654 errExit("chmod");
655
656 // mount overlayfs
657 if (arg_debug)
658 printf("Mounting OverlayFS\n");
659 char *option;
660 if (oldkernel) { // old Ubuntu/OpenSUSE kernels
661 if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1)
662 errExit("asprintf");
663 if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0)
664 errExit("mounting overlayfs");
665 }
666 else { // kernel 3.18 or newer
667 if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1)
668 errExit("asprintf");
669 if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0)
670 errExit("mounting overlayfs");
671 }
672
673 // mount-bind dev directory
674 if (arg_debug)
675 printf("Mounting /dev\n");
676 char *dev;
677 if (asprintf(&dev, "%s/dev", oroot) == -1)
678 errExit("asprintf");
679 if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0)
680 errExit("mounting /dev");
681
682 // chroot in the new filesystem
683 if (chroot(oroot) == -1)
684 errExit("chroot");
685 // update /var directory in order to support multiple sandboxes running on the same root directory
686 if (!arg_private_dev)
687 fs_dev_shm();
688 fs_var_lock();
689 fs_var_tmp();
690 fs_var_log();
691 fs_var_lib();
692 fs_var_cache();
693 fs_var_utmp();
694
695 // only in user mode
696 if (getuid())
697 sanitize_home();
698
699 // cleanup and exit
700 free(option);
701 free(oroot);
702 free(odiff);
703}
704
705
706
707#ifdef HAVE_CHROOT
708// return 1 if error
709int fs_check_chroot_dir(const char *rootdir) {
710 assert(rootdir);
711 struct stat s;
712 char *name;
713
714 // check /dev
715 if (asprintf(&name, "%s/dev", rootdir) == -1)
716 errExit("asprintf");
717 if (stat(name, &s) == -1) {
718 fprintf(stderr, "Error: cannot find /dev in chroot directory\n");
719 return 1;
720 }
721 free(name);
722
723 // check /var/tmp
724 if (asprintf(&name, "%s/var/tmp", rootdir) == -1)
725 errExit("asprintf");
726 if (stat(name, &s) == -1) {
727 fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n");
728 return 1;
729 }
730 free(name);
731
732 // check /proc
733 if (asprintf(&name, "%s/proc", rootdir) == -1)
734 errExit("asprintf");
735 if (stat(name, &s) == -1) {
736 fprintf(stderr, "Error: cannot find /proc in chroot directory\n");
737 return 1;
738 }
739 free(name);
740
741 // check /proc
742 if (asprintf(&name, "%s/tmp", rootdir) == -1)
743 errExit("asprintf");
744 if (stat(name, &s) == -1) {
745 fprintf(stderr, "Error: cannot find /tmp in chroot directory\n");
746 return 1;
747 }
748 free(name);
749
750 // check /bin/bash
751 if (asprintf(&name, "%s/bin/bash", rootdir) == -1)
752 errExit("asprintf");
753 if (stat(name, &s) == -1) {
754 fprintf(stderr, "Error: cannot find /bin/bash in chroot directory\n");
755 return 1;
756 }
757 free(name);
758
759 return 0;
760}
761
762// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
763void fs_chroot(const char *rootdir) {
764 assert(rootdir);
765
766 //***********************************
767 // mount-bind a /dev in rootdir
768 //***********************************
769 // mount /dev
770 char *newdev;
771 if (asprintf(&newdev, "%s/dev", rootdir) == -1)
772 errExit("asprintf");
773 if (arg_debug)
774 printf("Mounting /dev on %s\n", newdev);
775 if (mount("/dev", newdev, NULL, MS_BIND|MS_REC, NULL) < 0)
776 errExit("mounting /dev");
777
778 // some older distros don't have a /run directory
779 // create one by default
780 // no exit on error, let the user deal with any problems
781 char *rundir;
782 if (asprintf(&rundir, "%s/run", rootdir) == -1)
783 errExit("asprintf");
784 if (!is_dir(rundir)) {
785 int rv = mkdir(rundir, S_IRWXU | S_IRWXG | S_IRWXO);
786 (void) rv;
787 rv = chown(rundir, 0, 0);
788 (void) rv;
789 }
790
791 // copy /etc/resolv.conf in chroot directory
792 // if resolv.conf in chroot is a symbolic link, this will fail
793 // no exit on error, let the user deal with the problem
794 char *fname;
795 if (asprintf(&fname, "%s/etc/resolv.conf", rootdir) == -1)
796 errExit("asprintf");
797 if (arg_debug)
798 printf("Updating /etc/resolv.conf in %s\n", fname);
799 if (copy_file("/etc/resolv.conf", fname) == -1)
800 fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n");
801
802 // chroot into the new directory
803 if (arg_debug)
804 printf("Chrooting into %s\n", rootdir);
805 if (chroot(rootdir) < 0)
806 errExit("chroot");
807
808 // update /var directory in order to support multiple sandboxes running on the same root directory
809 if (!arg_private_dev)
810 fs_dev_shm();
811 fs_var_lock();
812 fs_var_tmp();
813 fs_var_log();
814 fs_var_lib();
815 fs_var_cache();
816 fs_var_utmp();
817
818 // only in user mode
819 if (getuid())
820 sanitize_home();
821
822}
823#endif
824
825