diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/util.c | 53 |
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. |
1327 | int safe_fd(const char *path, int flags) { | 1327 | int 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 | |||
1393 | errexit: | ||
1394 | fprintf(stderr, "Error: cannot open \"%s\", invalid filename\n", path); | ||
1395 | exit(1); | ||
1379 | } | 1396 | } |