diff options
-rw-r--r-- | src/firejail/firejail.h | 9 | ||||
-rw-r--r-- | src/firejail/fs.c | 366 | ||||
-rw-r--r-- | src/firejail/fs_overlayfs.c | 470 | ||||
-rw-r--r-- | src/firejail/util.c | 87 |
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); | |||
434 | void disable_config(void); | 434 | void disable_config(void); |
435 | // build a basic read-only filesystem | 435 | // build a basic read-only filesystem |
436 | void fs_basic_fs(void); | 436 | void fs_basic_fs(void); |
437 | // mount overlayfs on top of / directory | ||
438 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse); | ||
439 | void fs_overlayfs(void); | ||
440 | void fs_private_tmp(void); | 437 | void fs_private_tmp(void); |
441 | void fs_private_cache(void); | 438 | void fs_private_cache(void); |
442 | void fs_mnt(const int enforce); | 439 | void fs_mnt(const int enforce); |
443 | 440 | ||
441 | // fs_overlayfs.c | ||
442 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse); | ||
443 | void fs_overlayfs(void); | ||
444 | int 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 |
446 | void fs_check_chroot_dir(void); | 448 | void fs_check_chroot_dir(void); |
@@ -531,7 +533,6 @@ void wait_for_other(int fd); | |||
531 | void notify_other(int fd); | 533 | void notify_other(int fd); |
532 | uid_t pid_get_uid(pid_t pid); | 534 | uid_t pid_get_uid(pid_t pid); |
533 | uid_t get_group_id(const char *group); | 535 | uid_t get_group_id(const char *group); |
534 | int remove_overlay_directory(void); | ||
535 | void flush_stdin(void); | 536 | void flush_stdin(void); |
536 | int create_empty_dir_as_user(const char *dir, mode_t mode); | 537 | int create_empty_dir_as_user(const char *dir, mode_t mode); |
537 | void create_empty_dir_as_root(const char *dir, mode_t mode); | 538 | void 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 | ||
901 | char *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> | ||
988 | void 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 |
1261 | void fs_private_tmp(void) { | 898 | void 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 | |||
35 | char *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> | ||
120 | void 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 | |||
392 | static 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 | |||
409 | int 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 | |||
969 | uid_t get_group_id(const char *group) { | 962 | uid_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 | ||
980 | static 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 | |||
999 | int 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 |
1061 | void flush_stdin(void) { | 974 | void flush_stdin(void) { |
1062 | if (!isatty(STDIN_FILENO)) | 975 | if (!isatty(STDIN_FILENO)) |