aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/firejail/util.c53
1 files changed, 35 insertions, 18 deletions
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 2aa4c26a7..d79e955a7 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -1326,54 +1326,71 @@ void disable_file_path(const char *path, const char *file) {
1326// user controlled paths. Passed flags are ignored if path is a top level directory. 1326// user controlled paths. Passed flags are ignored if path is a top level directory.
1327int safe_fd(const char *path, int flags) { 1327int safe_fd(const char *path, int flags) {
1328 assert(path); 1328 assert(path);
1329 int fd = -1; 1329
1330 // reject empty string, relative path
1331 if (*path != '/')
1332 goto errexit;
1333 // reject ".."
1334 if (strstr(path, ".."))
1335 goto errexit;
1330 1336
1331 // work with a copy of path 1337 // work with a copy of path
1332 char *dup = strdup(path); 1338 char *dup = strdup(path);
1333 if (dup == NULL) 1339 if (dup == NULL)
1334 errExit("strdup"); 1340 errExit("strdup");
1335 // reject relative path and empty string
1336 if (*dup != '/') {
1337 fprintf(stderr, "Error: invalid pathname: %s\n", path);
1338 exit(1);
1339 }
1340 1341
1341 char *p = strrchr(dup, '/'); 1342 char *p = strrchr(dup, '/');
1342 if (p == NULL) 1343 if (p == NULL)
1343 errExit("strrchr"); 1344 errExit("strrchr");
1344 // reject trailing slash and root dir 1345 // reject trailing slash, root directory
1345 if (*(p + 1) == '\0') { 1346 if (*(p + 1) == '\0')
1346 fprintf(stderr, "Error: invalid pathname: %s\n", path); 1347 goto errexit;
1347 exit(1); 1348 // reject trailing dot
1348 } 1349 if (*(p + 1) == '.' && *(p + 2) == '\0')
1350 goto errexit;
1351 // if there is more than one path segment, keep the last one for later
1352 if (p != dup)
1353 *p = '\0';
1349 1354
1350 int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); 1355 int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC);
1351 if (parentfd == -1) 1356 if (parentfd == -1)
1352 errExit("open"); 1357 errExit("open");
1353 1358
1354 // if there is more than one path segment, keep the last one for later 1359 // traverse the path and return -1 if a symlink is encountered
1355 if (p != dup) 1360 int entered = 0;
1356 *p = '\0'; 1361 int fd = -1;
1357
1358 // traverse the path, return -1 if a symlink is encountered
1359 char *tok = strtok(dup, "/"); 1362 char *tok = strtok(dup, "/");
1360 if (tok == NULL)
1361 errExit("strtok");
1362 while (tok) { 1363 while (tok) {
1364 // skip all "/./"
1365 if (strcmp(tok, ".") == 0) {
1366 tok = strtok(NULL, "/");
1367 continue;
1368 }
1369 entered = 1;
1370
1371 // open the directory
1363 fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 1372 fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1364 close(parentfd); 1373 close(parentfd);
1365 if (fd == -1) { 1374 if (fd == -1) {
1366 free(dup); 1375 free(dup);
1367 return -1; 1376 return -1;
1368 } 1377 }
1378
1369 parentfd = fd; 1379 parentfd = fd;
1370 tok = strtok(NULL, "/"); 1380 tok = strtok(NULL, "/");
1371 } 1381 }
1372 if (p != dup) { 1382 if (p != dup) {
1383 // consistent flags for top level directories (////foo, /.///foo)
1384 if (!entered)
1385 flags = O_PATH|O_DIRECTORY|O_CLOEXEC;
1373 // open last path segment 1386 // open last path segment
1374 fd = openat(parentfd, p + 1, flags|O_NOFOLLOW); 1387 fd = openat(parentfd, p + 1, flags|O_NOFOLLOW);
1375 close(parentfd); 1388 close(parentfd);
1376 } 1389 }
1377 free(dup); 1390 free(dup);
1378 return fd; // -1 if open failed 1391 return fd; // -1 if open failed
1392
1393errexit:
1394 fprintf(stderr, "Error: cannot open \"%s\", invalid filename\n", path);
1395 exit(1);
1379} 1396}