aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2021-10-13 19:44:03 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2021-10-13 19:44:03 +0200
commitec38ea1abbc0ea29775c7887e817581ce13abd83 (patch)
treeb8d009c170cb01ecfa97b5620695580478b6ff87 /src
parentMerge pull request #4599 from rusty-snake/use-allow-tray (diff)
downloadfirejail-ec38ea1abbc0ea29775c7887e817581ce13abd83.tar.gz
firejail-ec38ea1abbc0ea29775c7887e817581ce13abd83.tar.zst
firejail-ec38ea1abbc0ea29775c7887e817581ce13abd83.zip
cleanup: move overlayfs code in separate module
Diffstat (limited to 'src')
-rw-r--r--src/firejail/firejail.h9
-rw-r--r--src/firejail/fs.c366
-rw-r--r--src/firejail/fs_overlayfs.c470
-rw-r--r--src/firejail/util.c87
4 files changed, 476 insertions, 456 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index bf51a4c93..45075d137 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -434,13 +434,15 @@ void fs_proc_sys_dev_boot(void);
434void disable_config(void); 434void disable_config(void);
435// build a basic read-only filesystem 435// build a basic read-only filesystem
436void fs_basic_fs(void); 436void fs_basic_fs(void);
437// mount overlayfs on top of / directory
438char *fs_check_overlay_dir(const char *subdirname, int allow_reuse);
439void fs_overlayfs(void);
440void fs_private_tmp(void); 437void fs_private_tmp(void);
441void fs_private_cache(void); 438void fs_private_cache(void);
442void fs_mnt(const int enforce); 439void fs_mnt(const int enforce);
443 440
441// fs_overlayfs.c
442char *fs_check_overlay_dir(const char *subdirname, int allow_reuse);
443void fs_overlayfs(void);
444int remove_overlay_directory(void);
445
444// chroot.c 446// chroot.c
445// chroot into an existing directory; mount existing /dev and update /etc/resolv.conf 447// chroot into an existing directory; mount existing /dev and update /etc/resolv.conf
446void fs_check_chroot_dir(void); 448void fs_check_chroot_dir(void);
@@ -531,7 +533,6 @@ void wait_for_other(int fd);
531void notify_other(int fd); 533void notify_other(int fd);
532uid_t pid_get_uid(pid_t pid); 534uid_t pid_get_uid(pid_t pid);
533uid_t get_group_id(const char *group); 535uid_t get_group_id(const char *group);
534int remove_overlay_directory(void);
535void flush_stdin(void); 536void flush_stdin(void);
536int create_empty_dir_as_user(const char *dir, mode_t mode); 537int create_empty_dir_as_user(const char *dir, mode_t mode);
537void create_empty_dir_as_root(const char *dir, mode_t mode); 538void create_empty_dir_as_root(const char *dir, mode_t mode);
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index 1a9a8df0d..0b9a38035 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -20,9 +20,7 @@
20#include "firejail.h" 20#include "firejail.h"
21#include "../include/gcov_wrapper.h" 21#include "../include/gcov_wrapper.h"
22#include <sys/mount.h> 22#include <sys/mount.h>
23#include <sys/stat.h>
24#include <sys/statvfs.h> 23#include <sys/statvfs.h>
25#include <sys/wait.h>
26#include <fnmatch.h> 24#include <fnmatch.h>
27#include <glob.h> 25#include <glob.h>
28#include <dirent.h> 26#include <dirent.h>
@@ -34,7 +32,7 @@
34#endif 32#endif
35 33
36#define MAX_BUF 4096 34#define MAX_BUF 4096
37#define EMPTY_STRING ("") 35
38// check noblacklist statements not matched by a proper blacklist in disable-*.inc files 36// check noblacklist statements not matched by a proper blacklist in disable-*.inc files
39//#define TEST_NO_BLACKLIST_MATCHING 37//#define TEST_NO_BLACKLIST_MATCHING
40 38
@@ -896,367 +894,6 @@ void fs_basic_fs(void) {
896} 894}
897 895
898 896
899
900#ifdef HAVE_OVERLAYFS
901char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
902 assert(subdirname);
903 EUID_ASSERT();
904 struct stat s;
905 char *dirname;
906
907 if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1)
908 errExit("asprintf");
909 // check if ~/.firejail already exists
910 if (lstat(dirname, &s) == 0) {
911 if (!S_ISDIR(s.st_mode)) {
912 if (S_ISLNK(s.st_mode))
913 fprintf(stderr, "Error: %s is a symbolic link\n", dirname);
914 else
915 fprintf(stderr, "Error: %s is not a directory\n", dirname);
916 exit(1);
917 }
918 if (s.st_uid != getuid()) {
919 fprintf(stderr, "Error: %s is not owned by the current user\n", dirname);
920 exit(1);
921 }
922 }
923 else {
924 // create ~/.firejail directory
925 create_empty_dir_as_user(dirname, 0700);
926 if (stat(dirname, &s) == -1) {
927 fprintf(stderr, "Error: cannot create directory %s\n", dirname);
928 exit(1);
929 }
930 }
931 free(dirname);
932
933 // check overlay directory
934 if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1)
935 errExit("asprintf");
936 if (lstat(dirname, &s) == 0) {
937 if (!S_ISDIR(s.st_mode)) {
938 if (S_ISLNK(s.st_mode))
939 fprintf(stderr, "Error: %s is a symbolic link\n", dirname);
940 else
941 fprintf(stderr, "Error: %s is not a directory\n", dirname);
942 exit(1);
943 }
944 if (s.st_uid != 0) {
945 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", dirname);
946 exit(1);
947 }
948 if (allow_reuse == 0) {
949 fprintf(stderr, "Error: overlay directory exists, but reuse is not allowed\n");
950 exit(1);
951 }
952 }
953
954 return dirname;
955}
956
957
958
959// mount overlayfs on top of / directory
960// mounting an overlay and chrooting into it:
961//
962// Old Ubuntu kernel
963// # cd ~
964// # mkdir -p overlay/root
965// # mkdir -p overlay/diff
966// # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root
967// # chroot /root/overlay/root
968// to shutdown, first exit the chroot and then unmount the overlay
969// # exit
970// # umount /root/overlay/root
971//
972// Kernels 3.18+
973// # cd ~
974// # mkdir -p overlay/root
975// # mkdir -p overlay/diff
976// # mkdir -p overlay/work
977// # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root
978// # cat /etc/mtab | grep overlay
979// /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0
980// # chroot /root/overlay/root
981// to shutdown, first exit the chroot and then unmount the overlay
982// # exit
983// # umount /root/overlay/root
984
985
986// to do: fix the code below; also, it might work without /dev, but consider keeping /dev/shm; add locking mechanism for overlay-clean
987#include <sys/utsname.h>
988void fs_overlayfs(void) {
989 struct stat s;
990
991 // check kernel version
992 struct utsname u;
993 int rv = uname(&u);
994 if (rv != 0)
995 errExit("uname");
996 int major;
997 int minor;
998 if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
999 fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
1000 exit(1);
1001 }
1002
1003 if (arg_debug)
1004 printf("Linux kernel version %d.%d\n", major, minor);
1005 int oldkernel = 0;
1006 if (major < 3) {
1007 fprintf(stderr, "Error: minimum kernel version required 3.x\n");
1008 exit(1);
1009 }
1010 if (major == 3 && minor < 18)
1011 oldkernel = 1;
1012
1013 // mounting an overlayfs on top of / seems to be broken for kernels > 4.19
1014 // we disable overlayfs for now, pending fixing
1015 if (major >= 4 &&minor >= 19) {
1016 fprintf(stderr, "Error: OverlayFS disabled for Linux kernels 4.19 and newer, pending fixing.\n");
1017 exit(1);
1018 }
1019
1020 char *oroot = RUN_OVERLAY_ROOT;
1021 mkdir_attr(oroot, 0755, 0, 0);
1022
1023 // set base for working and diff directories
1024 char *basedir = RUN_MNT_DIR;
1025 int basefd = -1;
1026
1027 if (arg_overlay_keep) {
1028 basedir = cfg.overlay_dir;
1029 assert(basedir);
1030 // get a file descriptor for ~/.firejail, fails if there is any symlink
1031 char *firejail;
1032 if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1)
1033 errExit("asprintf");
1034 int fd = safer_openat(-1, firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1035 if (fd == -1)
1036 errExit("safer_openat");
1037 free(firejail);
1038 // create basedir if it doesn't exist
1039 // the new directory will be owned by root
1040 const char *dirname = gnu_basename(basedir);
1041 if (mkdirat(fd, dirname, 0755) == -1 && errno != EEXIST) {
1042 perror("mkdir");
1043 fprintf(stderr, "Error: cannot create overlay directory %s\n", basedir);
1044 exit(1);
1045 }
1046 // open basedir
1047 basefd = openat(fd, dirname, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1048 close(fd);
1049 }
1050 else {
1051 basefd = open(basedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1052 }
1053 if (basefd == -1) {
1054 perror("open");
1055 fprintf(stderr, "Error: cannot open overlay directory %s\n", basedir);
1056 exit(1);
1057 }
1058
1059 // confirm once more base is owned by root
1060 if (fstat(basefd, &s) == -1)
1061 errExit("fstat");
1062 if (s.st_uid != 0) {
1063 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", basedir);
1064 exit(1);
1065 }
1066 // confirm permissions of base are 0755
1067 if (((S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) & s.st_mode) != (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
1068 fprintf(stderr, "Error: invalid permissions on overlay directory %s\n", basedir);
1069 exit(1);
1070 }
1071
1072 // create diff and work directories inside base
1073 // no need to check arg_overlay_reuse
1074 char *odiff;
1075 if (asprintf(&odiff, "%s/odiff", basedir) == -1)
1076 errExit("asprintf");
1077 // the new directory will be owned by root
1078 if (mkdirat(basefd, "odiff", 0755) == -1 && errno != EEXIST) {
1079 perror("mkdir");
1080 fprintf(stderr, "Error: cannot create overlay directory %s\n", odiff);
1081 exit(1);
1082 }
1083 ASSERT_PERMS(odiff, 0, 0, 0755);
1084
1085 char *owork;
1086 if (asprintf(&owork, "%s/owork", basedir) == -1)
1087 errExit("asprintf");
1088 // the new directory will be owned by root
1089 if (mkdirat(basefd, "owork", 0755) == -1 && errno != EEXIST) {
1090 perror("mkdir");
1091 fprintf(stderr, "Error: cannot create overlay directory %s\n", owork);
1092 exit(1);
1093 }
1094 ASSERT_PERMS(owork, 0, 0, 0755);
1095
1096 // mount overlayfs
1097 if (arg_debug)
1098 printf("Mounting OverlayFS\n");
1099 char *option;
1100 if (oldkernel) { // old Ubuntu/OpenSUSE kernels
1101 if (arg_overlay_keep) {
1102 fprintf(stderr, "Error: option --overlay= not available for kernels older than 3.18\n");
1103 exit(1);
1104 }
1105 if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1)
1106 errExit("asprintf");
1107 if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0)
1108 errExit("mounting overlayfs");
1109 }
1110 else { // kernel 3.18 or newer
1111 if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1)
1112 errExit("asprintf");
1113 if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0) {
1114 fprintf(stderr, "Debug: running on kernel version %d.%d\n", major, minor);
1115 errExit("mounting overlayfs");
1116 }
1117
1118 //***************************
1119 // issue #263 start code
1120 // My setup has a separate mount point for /home. When the overlay is mounted,
1121 // the overlay does not contain the original /home contents.
1122 // I added code to create a second overlay for /home if the overlay home dir is empty and this seems to work
1123 // @dshmgh, Jan 2016
1124 {
1125 char *overlayhome;
1126 struct stat s;
1127 char *hroot;
1128 char *hdiff;
1129 char *hwork;
1130
1131 // dons add debug
1132 if (arg_debug) printf ("DEBUG: chroot dirs are oroot %s odiff %s owork %s\n",oroot,odiff,owork);
1133
1134 // BEFORE NEXT, WE NEED TO TEST IF /home has any contents or do we need to mount it?
1135 // must create var for oroot/cfg.homedir
1136 if (asprintf(&overlayhome, "%s%s", oroot, cfg.homedir) == -1)
1137 errExit("asprintf");
1138 if (arg_debug) printf ("DEBUG: overlayhome var holds ##%s##\n", overlayhome);
1139
1140 // if no homedir in overlay -- create another overlay for /home
1141 if (stat(cfg.homedir, &s) == 0 && stat(overlayhome, &s) == -1) {
1142
1143 // no need to check arg_overlay_reuse
1144 if (asprintf(&hdiff, "%s/hdiff", basedir) == -1)
1145 errExit("asprintf");
1146 // the new directory will be owned by root
1147 if (mkdirat(basefd, "hdiff", 0755) == -1 && errno != EEXIST) {
1148 perror("mkdir");
1149 fprintf(stderr, "Error: cannot create overlay directory %s\n", hdiff);
1150 exit(1);
1151 }
1152 ASSERT_PERMS(hdiff, 0, 0, 0755);
1153
1154 // no need to check arg_overlay_reuse
1155 if (asprintf(&hwork, "%s/hwork", basedir) == -1)
1156 errExit("asprintf");
1157 // the new directory will be owned by root
1158 if (mkdirat(basefd, "hwork", 0755) == -1 && errno != EEXIST) {
1159 perror("mkdir");
1160 fprintf(stderr, "Error: cannot create overlay directory %s\n", hwork);
1161 exit(1);
1162 }
1163 ASSERT_PERMS(hwork, 0, 0, 0755);
1164
1165 // no homedir in overlay so now mount another overlay for /home
1166 if (asprintf(&hroot, "%s/home", oroot) == -1)
1167 errExit("asprintf");
1168 if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1)
1169 errExit("asprintf");
1170 if (mount("overlay", hroot, "overlay", MS_MGC_VAL, option) < 0)
1171 errExit("mounting overlayfs for mounted home directory");
1172
1173 printf("OverlayFS for /home configured in %s directory\n", basedir);
1174 free(hroot);
1175 free(hdiff);
1176 free(hwork);
1177
1178 } // stat(overlayhome)
1179 free(overlayhome);
1180 }
1181 // issue #263 end code
1182 //***************************
1183 }
1184 fmessage("OverlayFS configured in %s directory\n", basedir);
1185 close(basefd);
1186
1187 // /dev, /run and /tmp are not covered by the overlay
1188 // mount-bind dev directory
1189 if (arg_debug)
1190 printf("Mounting /dev\n");
1191 char *dev;
1192 if (asprintf(&dev, "%s/dev", oroot) == -1)
1193 errExit("asprintf");
1194 if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0)
1195 errExit("mounting /dev");
1196 fs_logger("whitelist /dev");
1197
1198 // mount-bind run directory
1199 if (arg_debug)
1200 printf("Mounting /run\n");
1201 char *run;
1202 if (asprintf(&run, "%s/run", oroot) == -1)
1203 errExit("asprintf");
1204 if (mount("/run", run, NULL, MS_BIND|MS_REC, NULL) < 0)
1205 errExit("mounting /run");
1206 fs_logger("whitelist /run");
1207
1208 // mount-bind tmp directory
1209 if (arg_debug)
1210 printf("Mounting /tmp\n");
1211 char *tmp;
1212 if (asprintf(&tmp, "%s/tmp", oroot) == -1)
1213 errExit("asprintf");
1214 if (mount("/tmp", tmp, NULL, MS_BIND|MS_REC, NULL) < 0)
1215 errExit("mounting /tmp");
1216 fs_logger("whitelist /tmp");
1217
1218 // chroot in the new filesystem
1219 __gcov_flush();
1220
1221 if (chroot(oroot) == -1)
1222 errExit("chroot");
1223
1224 // mount a new proc filesystem
1225 if (arg_debug)
1226 printf("Mounting /proc filesystem representing the PID namespace\n");
1227 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
1228 errExit("mounting /proc");
1229
1230 // update /var directory in order to support multiple sandboxes running on the same root directory
1231// if (!arg_private_dev)
1232// fs_dev_shm();
1233 fs_var_lock();
1234 if (!arg_keep_var_tmp)
1235 fs_var_tmp();
1236 if (!arg_writable_var_log)
1237 fs_var_log();
1238 fs_var_lib();
1239 fs_var_cache();
1240 fs_var_utmp();
1241 fs_machineid();
1242
1243 // don't leak user information
1244 restrict_users();
1245
1246 // when starting as root, firejail config is not disabled;
1247 if (getuid() != 0)
1248 disable_config();
1249
1250 // cleanup and exit
1251 free(option);
1252 free(odiff);
1253 free(owork);
1254 free(dev);
1255 free(run);
1256 free(tmp);
1257}
1258#endif
1259
1260// this function is called from sandbox.c before blacklist/whitelist functions 897// this function is called from sandbox.c before blacklist/whitelist functions
1261void fs_private_tmp(void) { 898void fs_private_tmp(void) {
1262 EUID_ASSERT(); 899 EUID_ASSERT();
@@ -1280,7 +917,6 @@ void fs_private_tmp(void) {
1280 917
1281 // whitelist x11 directory 918 // whitelist x11 directory
1282 profile_add("whitelist /tmp/.X11-unix"); 919 profile_add("whitelist /tmp/.X11-unix");
1283 // read-only x11 directory
1284 profile_add("read-only /tmp/.X11-unix"); 920 profile_add("read-only /tmp/.X11-unix");
1285 921
1286 // whitelist sndio directory 922 // whitelist sndio directory
diff --git a/src/firejail/fs_overlayfs.c b/src/firejail/fs_overlayfs.c
new file mode 100644
index 000000000..fe3761cb6
--- /dev/null
+++ b/src/firejail/fs_overlayfs.c
@@ -0,0 +1,470 @@
1/*
2 * Copyright (C) 2014-2021 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_OVERLAYFS
22#include "firejail.h"
23#include "../include/gcov_wrapper.h"
24#include <sys/mount.h>
25#include <sys/wait.h>
26#include <ftw.h>
27#include <errno.h>
28
29#include <fcntl.h>
30#ifndef O_PATH
31#define O_PATH 010000000
32#endif
33
34
35char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
36 assert(subdirname);
37 EUID_ASSERT();
38 struct stat s;
39 char *dirname;
40
41 if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1)
42 errExit("asprintf");
43 // check if ~/.firejail already exists
44 if (lstat(dirname, &s) == 0) {
45 if (!S_ISDIR(s.st_mode)) {
46 if (S_ISLNK(s.st_mode))
47 fprintf(stderr, "Error: %s is a symbolic link\n", dirname);
48 else
49 fprintf(stderr, "Error: %s is not a directory\n", dirname);
50 exit(1);
51 }
52 if (s.st_uid != getuid()) {
53 fprintf(stderr, "Error: %s is not owned by the current user\n", dirname);
54 exit(1);
55 }
56 }
57 else {
58 // create ~/.firejail directory
59 create_empty_dir_as_user(dirname, 0700);
60 if (stat(dirname, &s) == -1) {
61 fprintf(stderr, "Error: cannot create directory %s\n", dirname);
62 exit(1);
63 }
64 }
65 free(dirname);
66
67 // check overlay directory
68 if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1)
69 errExit("asprintf");
70 if (lstat(dirname, &s) == 0) {
71 if (!S_ISDIR(s.st_mode)) {
72 if (S_ISLNK(s.st_mode))
73 fprintf(stderr, "Error: %s is a symbolic link\n", dirname);
74 else
75 fprintf(stderr, "Error: %s is not a directory\n", dirname);
76 exit(1);
77 }
78 if (s.st_uid != 0) {
79 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", dirname);
80 exit(1);
81 }
82 if (allow_reuse == 0) {
83 fprintf(stderr, "Error: overlay directory exists, but reuse is not allowed\n");
84 exit(1);
85 }
86 }
87
88 return dirname;
89}
90
91
92// mount overlayfs on top of / directory
93// mounting an overlay and chrooting into it:
94//
95// Old Ubuntu kernel
96// # cd ~
97// # mkdir -p overlay/root
98// # mkdir -p overlay/diff
99// # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root
100// # chroot /root/overlay/root
101// to shutdown, first exit the chroot and then unmount the overlay
102// # exit
103// # umount /root/overlay/root
104//
105// Kernels 3.18+
106// # cd ~
107// # mkdir -p overlay/root
108// # mkdir -p overlay/diff
109// # mkdir -p overlay/work
110// # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root
111// # cat /etc/mtab | grep overlay
112// /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0
113// # chroot /root/overlay/root
114// to shutdown, first exit the chroot and then unmount the overlay
115// # exit
116// # umount /root/overlay/root
117
118// to do: fix the code below
119#include <sys/utsname.h>
120void fs_overlayfs(void) {
121 struct stat s;
122
123 // check kernel version
124 struct utsname u;
125 int rv = uname(&u);
126 if (rv != 0)
127 errExit("uname");
128 int major;
129 int minor;
130 if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
131 fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
132 exit(1);
133 }
134
135 if (arg_debug)
136 printf("Linux kernel version %d.%d\n", major, minor);
137 int oldkernel = 0;
138 if (major < 3) {
139 fprintf(stderr, "Error: minimum kernel version required 3.x\n");
140 exit(1);
141 }
142 if (major == 3 && minor < 18)
143 oldkernel = 1;
144
145 // mounting an overlayfs on top of / seems to be broken for kernels > 4.19
146 // we disable overlayfs for now, pending fixing
147 if (major >= 4 &&minor >= 19) {
148 fprintf(stderr, "Error: OverlayFS disabled for Linux kernels 4.19 and newer, pending fixing.\n");
149 exit(1);
150 }
151
152 char *oroot = RUN_OVERLAY_ROOT;
153 mkdir_attr(oroot, 0755, 0, 0);
154
155 // set base for working and diff directories
156 char *basedir = RUN_MNT_DIR;
157 int basefd = -1;
158
159 if (arg_overlay_keep) {
160 basedir = cfg.overlay_dir;
161 assert(basedir);
162 // get a file descriptor for ~/.firejail, fails if there is any symlink
163 char *firejail;
164 if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1)
165 errExit("asprintf");
166 int fd = safer_openat(-1, firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
167 if (fd == -1)
168 errExit("safer_openat");
169 free(firejail);
170 // create basedir if it doesn't exist
171 // the new directory will be owned by root
172 const char *dirname = gnu_basename(basedir);
173 if (mkdirat(fd, dirname, 0755) == -1 && errno != EEXIST) {
174 perror("mkdir");
175 fprintf(stderr, "Error: cannot create overlay directory %s\n", basedir);
176 exit(1);
177 }
178 // open basedir
179 basefd = openat(fd, dirname, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
180 close(fd);
181 }
182 else {
183 basefd = open(basedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
184 }
185 if (basefd == -1) {
186 perror("open");
187 fprintf(stderr, "Error: cannot open overlay directory %s\n", basedir);
188 exit(1);
189 }
190
191 // confirm once more base is owned by root
192 if (fstat(basefd, &s) == -1)
193 errExit("fstat");
194 if (s.st_uid != 0) {
195 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", basedir);
196 exit(1);
197 }
198 // confirm permissions of base are 0755
199 if (((S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) & s.st_mode) != (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
200 fprintf(stderr, "Error: invalid permissions on overlay directory %s\n", basedir);
201 exit(1);
202 }
203
204 // create diff and work directories inside base
205 // no need to check arg_overlay_reuse
206 char *odiff;
207 if (asprintf(&odiff, "%s/odiff", basedir) == -1)
208 errExit("asprintf");
209 // the new directory will be owned by root
210 if (mkdirat(basefd, "odiff", 0755) == -1 && errno != EEXIST) {
211 perror("mkdir");
212 fprintf(stderr, "Error: cannot create overlay directory %s\n", odiff);
213 exit(1);
214 }
215 ASSERT_PERMS(odiff, 0, 0, 0755);
216
217 char *owork;
218 if (asprintf(&owork, "%s/owork", basedir) == -1)
219 errExit("asprintf");
220 // the new directory will be owned by root
221 if (mkdirat(basefd, "owork", 0755) == -1 && errno != EEXIST) {
222 perror("mkdir");
223 fprintf(stderr, "Error: cannot create overlay directory %s\n", owork);
224 exit(1);
225 }
226 ASSERT_PERMS(owork, 0, 0, 0755);
227
228 // mount overlayfs
229 if (arg_debug)
230 printf("Mounting OverlayFS\n");
231 char *option;
232 if (oldkernel) { // old Ubuntu/OpenSUSE kernels
233 if (arg_overlay_keep) {
234 fprintf(stderr, "Error: option --overlay= not available for kernels older than 3.18\n");
235 exit(1);
236 }
237 if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1)
238 errExit("asprintf");
239 if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0)
240 errExit("mounting overlayfs");
241 }
242 else { // kernel 3.18 or newer
243 if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1)
244 errExit("asprintf");
245 if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0) {
246 fprintf(stderr, "Debug: running on kernel version %d.%d\n", major, minor);
247 errExit("mounting overlayfs");
248 }
249
250 //***************************
251 // issue #263 start code
252 // My setup has a separate mount point for /home. When the overlay is mounted,
253 // the overlay does not contain the original /home contents.
254 // I added code to create a second overlay for /home if the overlay home dir is empty and this seems to work
255 // @dshmgh, Jan 2016
256 {
257 char *overlayhome;
258 struct stat s;
259 char *hroot;
260 char *hdiff;
261 char *hwork;
262
263 // dons add debug
264 if (arg_debug) printf ("DEBUG: chroot dirs are oroot %s odiff %s owork %s\n",oroot,odiff,owork);
265
266 // BEFORE NEXT, WE NEED TO TEST IF /home has any contents or do we need to mount it?
267 // must create var for oroot/cfg.homedir
268 if (asprintf(&overlayhome, "%s%s", oroot, cfg.homedir) == -1)
269 errExit("asprintf");
270 if (arg_debug) printf ("DEBUG: overlayhome var holds ##%s##\n", overlayhome);
271
272 // if no homedir in overlay -- create another overlay for /home
273 if (stat(cfg.homedir, &s) == 0 && stat(overlayhome, &s) == -1) {
274
275 // no need to check arg_overlay_reuse
276 if (asprintf(&hdiff, "%s/hdiff", basedir) == -1)
277 errExit("asprintf");
278 // the new directory will be owned by root
279 if (mkdirat(basefd, "hdiff", 0755) == -1 && errno != EEXIST) {
280 perror("mkdir");
281 fprintf(stderr, "Error: cannot create overlay directory %s\n", hdiff);
282 exit(1);
283 }
284 ASSERT_PERMS(hdiff, 0, 0, 0755);
285
286 // no need to check arg_overlay_reuse
287 if (asprintf(&hwork, "%s/hwork", basedir) == -1)
288 errExit("asprintf");
289 // the new directory will be owned by root
290 if (mkdirat(basefd, "hwork", 0755) == -1 && errno != EEXIST) {
291 perror("mkdir");
292 fprintf(stderr, "Error: cannot create overlay directory %s\n", hwork);
293 exit(1);
294 }
295 ASSERT_PERMS(hwork, 0, 0, 0755);
296
297 // no homedir in overlay so now mount another overlay for /home
298 if (asprintf(&hroot, "%s/home", oroot) == -1)
299 errExit("asprintf");
300 if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1)
301 errExit("asprintf");
302 if (mount("overlay", hroot, "overlay", MS_MGC_VAL, option) < 0)
303 errExit("mounting overlayfs for mounted home directory");
304
305 printf("OverlayFS for /home configured in %s directory\n", basedir);
306 free(hroot);
307 free(hdiff);
308 free(hwork);
309
310 } // stat(overlayhome)
311 free(overlayhome);
312 }
313 // issue #263 end code
314 //***************************
315 }
316 fmessage("OverlayFS configured in %s directory\n", basedir);
317 close(basefd);
318
319 // /dev, /run and /tmp are not covered by the overlay
320 // mount-bind dev directory
321 if (arg_debug)
322 printf("Mounting /dev\n");
323 char *dev;
324 if (asprintf(&dev, "%s/dev", oroot) == -1)
325 errExit("asprintf");
326 if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0)
327 errExit("mounting /dev");
328 fs_logger("whitelist /dev");
329
330 // mount-bind run directory
331 if (arg_debug)
332 printf("Mounting /run\n");
333 char *run;
334 if (asprintf(&run, "%s/run", oroot) == -1)
335 errExit("asprintf");
336 if (mount("/run", run, NULL, MS_BIND|MS_REC, NULL) < 0)
337 errExit("mounting /run");
338 fs_logger("whitelist /run");
339
340 // mount-bind tmp directory
341 if (arg_debug)
342 printf("Mounting /tmp\n");
343 char *tmp;
344 if (asprintf(&tmp, "%s/tmp", oroot) == -1)
345 errExit("asprintf");
346 if (mount("/tmp", tmp, NULL, MS_BIND|MS_REC, NULL) < 0)
347 errExit("mounting /tmp");
348 fs_logger("whitelist /tmp");
349
350 // chroot in the new filesystem
351 __gcov_flush();
352
353 if (chroot(oroot) == -1)
354 errExit("chroot");
355
356 // mount a new proc filesystem
357 if (arg_debug)
358 printf("Mounting /proc filesystem representing the PID namespace\n");
359 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
360 errExit("mounting /proc");
361
362 // update /var directory in order to support multiple sandboxes running on the same root directory
363// if (!arg_private_dev)
364// fs_dev_shm();
365 fs_var_lock();
366 if (!arg_keep_var_tmp)
367 fs_var_tmp();
368 if (!arg_writable_var_log)
369 fs_var_log();
370 fs_var_lib();
371 fs_var_cache();
372 fs_var_utmp();
373 fs_machineid();
374
375 // don't leak user information
376 restrict_users();
377
378 // when starting as root, firejail config is not disabled;
379 if (getuid() != 0)
380 disable_config();
381
382 // cleanup and exit
383 free(option);
384 free(odiff);
385 free(owork);
386 free(dev);
387 free(run);
388 free(tmp);
389}
390
391
392static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
393 (void) sb;
394 (void) typeflag;
395 (void) ftwbuf;
396 assert(fpath);
397
398 if (strcmp(fpath, ".") == 0) // rmdir would fail with EINVAL
399 return 0;
400
401 if (remove(fpath)) { // removes the link not the actual file
402 fprintf(stderr, "Error: cannot remove file: %s\n", strerror(errno));
403 exit(1);
404 }
405
406 return 0;
407}
408
409int remove_overlay_directory(void) {
410 EUID_ASSERT();
411 sleep(1);
412
413 char *path;
414 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1)
415 errExit("asprintf");
416
417 if (access(path, F_OK) == 0) {
418 pid_t child = fork();
419 if (child < 0)
420 errExit("fork");
421 if (child == 0) {
422 // open ~/.firejail
423 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
424 if (fd == -1) {
425 fprintf(stderr, "Error: cannot open %s\n", path);
426 exit(1);
427 }
428 struct stat s;
429 if (fstat(fd, &s) == -1)
430 errExit("fstat");
431 if (!S_ISDIR(s.st_mode)) {
432 if (S_ISLNK(s.st_mode))
433 fprintf(stderr, "Error: %s is a symbolic link\n", path);
434 else
435 fprintf(stderr, "Error: %s is not a directory\n", path);
436 exit(1);
437 }
438 if (s.st_uid != getuid()) {
439 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
440 exit(1);
441 }
442 // chdir to ~/.firejail
443 if (fchdir(fd) == -1)
444 errExit("fchdir");
445 close(fd);
446
447 EUID_ROOT();
448 // FTW_PHYS - do not follow symbolic links
449 if (nftw(".", remove_callback, 64, FTW_DEPTH | FTW_PHYS) == -1)
450 errExit("nftw");
451
452 EUID_USER();
453 // remove ~/.firejail
454 if (rmdir(path) == -1)
455 errExit("rmdir");
456
457 __gcov_flush();
458
459 _exit(0);
460 }
461 // wait for the child to finish
462 waitpid(child, NULL, 0);
463 // check if ~/.firejail was deleted
464 if (access(path, F_OK) == 0)
465 return 1;
466 }
467 return 0;
468}
469
470#endif // HAVE_OVERLAYFS
diff --git a/src/firejail/util.c b/src/firejail/util.c
index f0df45eb2..8749f79cc 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -20,8 +20,6 @@
20#define _XOPEN_SOURCE 500 20#define _XOPEN_SOURCE 500
21#include "firejail.h" 21#include "firejail.h"
22#include "../include/gcov_wrapper.h" 22#include "../include/gcov_wrapper.h"
23#include <ftw.h>
24#include <sys/stat.h>
25#include <sys/mount.h> 23#include <sys/mount.h>
26#include <syslog.h> 24#include <syslog.h>
27#include <errno.h> 25#include <errno.h>
@@ -32,9 +30,6 @@
32#include <sys/wait.h> 30#include <sys/wait.h>
33#include <limits.h> 31#include <limits.h>
34 32
35#include <string.h>
36#include <ctype.h>
37
38#include <fcntl.h> 33#include <fcntl.h>
39#ifndef O_PATH 34#ifndef O_PATH
40#define O_PATH 010000000 35#define O_PATH 010000000
@@ -964,8 +959,6 @@ uid_t pid_get_uid(pid_t pid) {
964} 959}
965 960
966 961
967
968
969uid_t get_group_id(const char *group) { 962uid_t get_group_id(const char *group) {
970 // find tty group id 963 // find tty group id
971 gid_t gid = 0; 964 gid_t gid = 0;
@@ -977,86 +970,6 @@ uid_t get_group_id(const char *group) {
977} 970}
978 971
979 972
980static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
981 (void) sb;
982 (void) typeflag;
983 (void) ftwbuf;
984 assert(fpath);
985
986 if (strcmp(fpath, ".") == 0)
987 return 0;
988
989 if (remove(fpath)) { // removes the link not the actual file
990 perror("remove");
991 fprintf(stderr, "Error: cannot remove file from user .firejail directory: %s\n", fpath);
992 exit(1);
993 }
994
995 return 0;
996}
997
998
999int remove_overlay_directory(void) {
1000 EUID_ASSERT();
1001 sleep(1);
1002
1003 char *path;
1004 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1)
1005 errExit("asprintf");
1006
1007 if (access(path, F_OK) == 0) {
1008 pid_t child = fork();
1009 if (child < 0)
1010 errExit("fork");
1011 if (child == 0) {
1012 // open ~/.firejail
1013 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
1014 if (fd == -1) {
1015 fprintf(stderr, "Error: cannot open %s\n", path);
1016 exit(1);
1017 }
1018 struct stat s;
1019 if (fstat(fd, &s) == -1)
1020 errExit("fstat");
1021 if (!S_ISDIR(s.st_mode)) {
1022 if (S_ISLNK(s.st_mode))
1023 fprintf(stderr, "Error: %s is a symbolic link\n", path);
1024 else
1025 fprintf(stderr, "Error: %s is not a directory\n", path);
1026 exit(1);
1027 }
1028 if (s.st_uid != getuid()) {
1029 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
1030 exit(1);
1031 }
1032 // chdir to ~/.firejail
1033 if (fchdir(fd) == -1)
1034 errExit("fchdir");
1035 close(fd);
1036
1037 EUID_ROOT();
1038 // FTW_PHYS - do not follow symbolic links
1039 if (nftw(".", remove_callback, 64, FTW_DEPTH | FTW_PHYS) == -1)
1040 errExit("nftw");
1041
1042 EUID_USER();
1043 // remove ~/.firejail
1044 if (rmdir(path) == -1)
1045 errExit("rmdir");
1046
1047 __gcov_flush();
1048
1049 _exit(0);
1050 }
1051 // wait for the child to finish
1052 waitpid(child, NULL, 0);
1053 // check if ~/.firejail was deleted
1054 if (access(path, F_OK) == 0)
1055 return 1;
1056 }
1057 return 0;
1058}
1059
1060// flush stdin if it is connected to a tty and has input 973// flush stdin if it is connected to a tty and has input
1061void flush_stdin(void) { 974void flush_stdin(void) {
1062 if (!isatty(STDIN_FILENO)) 975 if (!isatty(STDIN_FILENO))