aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2018-09-01 03:06:43 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2018-09-01 03:06:43 +0200
commit6a69845df17225546f94bab0930260d5e7185740 (patch)
treed97ae7835d5d7e6219885c2eef9a142b217a9aa9 /src
parentreduce number of chown/chmod calls in fs_chroot (diff)
downloadfirejail-6a69845df17225546f94bab0930260d5e7185740.tar.gz
firejail-6a69845df17225546f94bab0930260d5e7185740.tar.zst
firejail-6a69845df17225546f94bab0930260d5e7185740.zip
consolidate and enhance checks run on chroot directory hierarchy (patch n/n)
Diffstat (limited to 'src')
-rw-r--r--src/firejail/fs.c82
-rw-r--r--src/firejail/main.c2
2 files changed, 50 insertions, 34 deletions
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index bd71a6912..0080f3de2 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -1148,24 +1148,33 @@ void fs_overlayfs(void) {
1148 1148
1149 1149
1150#ifdef HAVE_CHROOT 1150#ifdef HAVE_CHROOT
1151// return 1 if error 1151// exit if error
1152void fs_check_chroot_dir(const char *rootdir) { 1152void fs_check_chroot_dir(const char *rootdir) {
1153 EUID_ASSERT(); 1153 EUID_ASSERT();
1154 assert(rootdir); 1154 assert(rootdir);
1155 struct stat s; 1155 struct stat s;
1156 char *name; 1156 int fd = -1;
1157 int parentfd = -1;
1157 1158
1158 if (strcmp(rootdir, "/tmp") == 0 || strcmp(rootdir, "/var/tmp") == 0) { 1159 char *overlay;
1159 fprintf(stderr, "Error: invalid chroot directory\n"); 1160 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1)
1161 errExit("asprintf");
1162 if (strncmp(rootdir, overlay, strlen(overlay)) == 0) {
1163 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir);
1160 exit(1); 1164 exit(1);
1161 } 1165 }
1166 free(overlay);
1162 1167
1163 // rootdir has to be owned by root and is not allowed to be world-writable 1168 // fails if there is any symlink or if rootdir is not a directory
1164 // we checked already if it is a directory 1169 parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1165 if (stat(rootdir, &s) != 0) { 1170 if (parentfd == -1) {
1166 fprintf(stderr, "Error: cannot find chroot directory\n"); 1171 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir);
1167 exit(1); 1172 exit(1);
1168 } 1173 }
1174 // rootdir has to be owned by root and is not allowed to be world-writable;
1175 // this also excludes /tmp, /var/tmp and such
1176 if (fstat(parentfd, &s) == -1)
1177 errExit("fstat");
1169 if (s.st_uid != 0) { 1178 if (s.st_uid != 0) {
1170 fprintf(stderr, "Error: chroot directory should be owned by root\n"); 1179 fprintf(stderr, "Error: chroot directory should be owned by root\n");
1171 exit(1); 1180 exit(1);
@@ -1176,64 +1185,69 @@ void fs_check_chroot_dir(const char *rootdir) {
1176 } 1185 }
1177 1186
1178 // check /dev 1187 // check /dev
1179 if (asprintf(&name, "%s/dev", rootdir) == -1) 1188 fd = openat(parentfd, "dev", O_PATH|O_CLOEXEC);
1180 errExit("asprintf"); 1189 if (fd == -1) {
1181 if (stat(name, &s) == -1) {
1182 fprintf(stderr, "Error: cannot find /dev in chroot directory\n"); 1190 fprintf(stderr, "Error: cannot find /dev in chroot directory\n");
1183 exit(1); 1191 exit(1);
1184 } 1192 }
1193 if (fstat(fd, &s) == -1)
1194 errExit("fstat");
1185 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) { 1195 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1186 fprintf(stderr, "Error: chroot /dev should be a directory owned by root\n"); 1196 fprintf(stderr, "Error: chroot /dev should be a directory owned by root\n");
1187 exit(1); 1197 exit(1);
1188 } 1198 }
1189 free(name); 1199 close(fd);
1190 1200
1191 // check /var/tmp 1201 // check /var/tmp
1192 if (asprintf(&name, "%s/var/tmp", rootdir) == -1) 1202 fd = openat(parentfd, "var/tmp", O_PATH|O_CLOEXEC);
1193 errExit("asprintf"); 1203 if (fd == -1) {
1194 if (stat(name, &s) == -1) {
1195 fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n"); 1204 fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n");
1196 exit(1); 1205 exit(1);
1197 } 1206 }
1207 if (fstat(fd, &s) == -1)
1208 errExit("fstat");
1198 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) { 1209 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1199 fprintf(stderr, "Error: chroot /var/tmp should be a directory owned by root\n"); 1210 fprintf(stderr, "Error: chroot /var/tmp should be a directory owned by root\n");
1200 exit(1); 1211 exit(1);
1201 } 1212 }
1202 free(name); 1213 close(fd);
1203 1214
1204 // check /proc 1215 // check /proc
1205 if (asprintf(&name, "%s/proc", rootdir) == -1) 1216 fd = openat(parentfd, "proc", O_PATH|O_CLOEXEC);
1206 errExit("asprintf"); 1217 if (fd == -1) {
1207 if (stat(name, &s) == -1) {
1208 fprintf(stderr, "Error: cannot find /proc in chroot directory\n"); 1218 fprintf(stderr, "Error: cannot find /proc in chroot directory\n");
1209 exit(1); 1219 exit(1);
1210 } 1220 }
1221 if (fstat(fd, &s) == -1)
1222 errExit("fstat");
1211 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) { 1223 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1212 fprintf(stderr, "Error: chroot /proc should be a directory owned by root\n"); 1224 fprintf(stderr, "Error: chroot /proc should be a directory owned by root\n");
1213 exit(1); 1225 exit(1);
1214 } 1226 }
1215 free(name); 1227 close(fd);
1216 1228
1217 // check /tmp 1229 // check /tmp
1218 if (asprintf(&name, "%s/tmp", rootdir) == -1) 1230 fd = openat(parentfd, "tmp", O_PATH|O_CLOEXEC);
1219 errExit("asprintf"); 1231 if (fd == -1) {
1220 if (stat(name, &s) == -1) {
1221 fprintf(stderr, "Error: cannot find /tmp in chroot directory\n"); 1232 fprintf(stderr, "Error: cannot find /tmp in chroot directory\n");
1222 exit(1); 1233 exit(1);
1223 } 1234 }
1235 if (fstat(fd, &s) == -1)
1236 errExit("fstat");
1224 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) { 1237 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1225 fprintf(stderr, "Error: chroot /tmp should be a directory owned by root\n"); 1238 fprintf(stderr, "Error: chroot /tmp should be a directory owned by root\n");
1226 exit(1); 1239 exit(1);
1227 } 1240 }
1228 free(name); 1241 close(fd);
1229 1242
1230 // check /etc 1243 // check /etc
1231 if (asprintf(&name, "%s/etc", rootdir) == -1) 1244 fd = openat(parentfd, "etc", O_PATH|O_CLOEXEC);
1232 errExit("asprintf"); 1245 if (fd == -1) {
1233 if (stat(name, &s) == -1) {
1234 fprintf(stderr, "Error: cannot find /etc in chroot directory\n"); 1246 fprintf(stderr, "Error: cannot find /etc in chroot directory\n");
1235 exit(1); 1247 exit(1);
1236 } 1248 }
1249 if (fstat(fd, &s) == -1)
1250 errExit("fstat");
1237 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) { 1251 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1238 fprintf(stderr, "Error: chroot /etc should be a directory owned by root\n"); 1252 fprintf(stderr, "Error: chroot /etc should be a directory owned by root\n");
1239 exit(1); 1253 exit(1);
@@ -1242,7 +1256,7 @@ void fs_check_chroot_dir(const char *rootdir) {
1242 fprintf(stderr, "Error: chroot /etc should not be world-writable\n"); 1256 fprintf(stderr, "Error: chroot /etc should not be world-writable\n");
1243 exit(1); 1257 exit(1);
1244 } 1258 }
1245 free(name); 1259 close(fd);
1246 1260
1247 // there should be no checking on <chrootdir>/etc/resolv.conf 1261 // there should be no checking on <chrootdir>/etc/resolv.conf
1248 // the file is replaced with the real /etc/resolv.conf anyway 1262 // the file is replaced with the real /etc/resolv.conf anyway
@@ -1274,19 +1288,21 @@ void fs_check_chroot_dir(const char *rootdir) {
1274 1288
1275 // check x11 socket directory 1289 // check x11 socket directory
1276 if (getenv("FIREJAIL_X11")) { 1290 if (getenv("FIREJAIL_X11")) {
1277 char *name; 1291 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_CLOEXEC);
1278 if (asprintf(&name, "%s/tmp/.X11-unix", rootdir) == -1) 1292 if (fd == -1) {
1279 errExit("asprintf");
1280 if (stat(name, &s) == -1) {
1281 fprintf(stderr, "Error: cannot find /tmp/.X11-unix in chroot directory\n"); 1293 fprintf(stderr, "Error: cannot find /tmp/.X11-unix in chroot directory\n");
1282 exit(1); 1294 exit(1);
1283 } 1295 }
1296 if (fstat(fd, &s) == -1)
1297 errExit("fstat");
1284 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) { 1298 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) {
1285 fprintf(stderr, "Error: chroot /tmp/.X11-unix should be a directory owned by root\n"); 1299 fprintf(stderr, "Error: chroot /tmp/.X11-unix should be a directory owned by root\n");
1286 exit(1); 1300 exit(1);
1287 } 1301 }
1288 free(name); 1302 close(fd);
1289 } 1303 }
1304
1305 close(parentfd);
1290} 1306}
1291 1307
1292// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf 1308// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 6170806f9..b5d46808a 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -1507,7 +1507,7 @@ int main(int argc, char **argv) {
1507 1507
1508 // check chroot dirname exists, don't allow "--chroot=/" 1508 // check chroot dirname exists, don't allow "--chroot=/"
1509 char *rpath = realpath(cfg.chrootdir, NULL); 1509 char *rpath = realpath(cfg.chrootdir, NULL);
1510 if (rpath == NULL || !is_dir(rpath) || strcmp(rpath, "/") == 0) { 1510 if (rpath == NULL || strcmp(rpath, "/") == 0) {
1511 fprintf(stderr, "Error: invalid chroot directory\n"); 1511 fprintf(stderr, "Error: invalid chroot directory\n");
1512 exit(1); 1512 exit(1);
1513 } 1513 }