From a0b0d8fbbf063c9a2bb0b719e5fbd4cb766f2f6d Mon Sep 17 00:00:00 2001 From: smitsohu Date: Mon, 23 Nov 2020 18:24:14 +0100 Subject: use openat2 syscall when available --- src/firejail/util.c | 63 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/src/firejail/util.c b/src/firejail/util.c index 02befdc12..e8b35a64b 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,10 @@ #define O_PATH 010000000 #endif +#ifdef __NR_openat2 +#include +#endif + #define MAX_GROUPS 1024 #define MAXBUF 4098 #define EMPTY_STRING ("") @@ -1157,46 +1162,57 @@ void disable_file_path(const char *path, const char *file) { free(fname); } -// open file without following any symbolic link -// returns a file descriptor on success, or -1 if a symlink is found +// open an existing file without following any symbolic link int safe_fd(const char *path, int flags) { assert(path); - if (*path != '/') - goto errexit; - if (strstr(path, "..")) - goto errexit; - - int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); - if (parentfd == -1) - errExit("open"); + if (*path != '/' || strstr(path, "..")) { + fprintf(stderr, "Error: invalid path %s\n", path); + exit(1); + } + flags |= O_NOFOLLOW; int fd = -1; - char *last_tok = EMPTY_STRING; +#ifdef __NR_openat2 // kernel 5.6 or better + struct open_how oh; + memset(&oh, 0, sizeof(oh)); + oh.flags = flags; + oh.resolve = RESOLVE_NO_SYMLINKS; + fd = syscall(__NR_openat2, -1, path, &oh, sizeof(struct open_how)); + if (fd != -1 || errno != ENOSYS) + return fd; +#endif + + // openat2 syscall is not available, traverse path and + // check each component if it is a symbolic link or not char *dup = strdup(path); if (!dup) errExit("strdup"); char *tok = strtok(dup, "/"); if (!tok) { // root directory free(dup); - return parentfd; + return open("/", flags); } + char *last_tok = EMPTY_STRING; + int parentfd = open("/", O_PATH|O_CLOEXEC); + if (parentfd == -1) + errExit("open"); while(1) { - // open the element, assuming it is a directory; this fails with ENOTDIR if it is a symbolic link + // open path component, assuming it is a directory; this fails with ENOTDIR if it is a symbolic link // if token is a single dot, the previous directory is reopened fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); if (fd == -1) { - // if the following token is NULL, the current token is the final path element + // if the following token is NULL, the current token is the final path component // try again to open it, this time using the passed flags, and return -1 or the descriptor last_tok = tok; tok = strtok(NULL, "/"); if (!tok) - fd = openat(parentfd, last_tok, flags|O_NOFOLLOW); + fd = openat(parentfd, last_tok, flags); close(parentfd); free(dup); - return fd; // -1 if open failed + return fd; } - // move on to next path segment + // move on to next path component last_tok = tok; tok = strtok(NULL, "/"); if (!tok) @@ -1204,18 +1220,13 @@ int safe_fd(const char *path, int flags) { close(parentfd); parentfd = fd; } - - // we are here because the last path element exists and is of file type directory + // getting here when the last path component exists and is of file type directory // reopen it using the passed flags close(fd); - fd = openat(parentfd, last_tok, flags|O_NOFOLLOW); + fd = openat(parentfd, last_tok, flags); close(parentfd); free(dup); - return fd; // -1 if open failed - -errexit: - fprintf(stderr, "Error: cannot open \"%s\": invalid path\n", path); - exit(1); + return fd; } int has_handler(pid_t pid, int signal) { @@ -1321,7 +1332,7 @@ static int has_link(const char *dir) { assert(dir); int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); if (fd == -1) { - if (errno == ENOTDIR && is_dir(dir)) + if ((errno == ELOOP || errno == ENOTDIR) && is_dir(dir)) return 1; } else -- cgit v1.2.3-70-g09d2