diff options
Diffstat (limited to 'src/firejail/util.c')
-rw-r--r-- | src/firejail/util.c | 63 |
1 files 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 @@ | |||
29 | #include <sys/ioctl.h> | 29 | #include <sys/ioctl.h> |
30 | #include <termios.h> | 30 | #include <termios.h> |
31 | #include <sys/wait.h> | 31 | #include <sys/wait.h> |
32 | #include <sys/syscall.h> | ||
32 | #include <limits.h> | 33 | #include <limits.h> |
33 | 34 | ||
34 | #include <fcntl.h> | 35 | #include <fcntl.h> |
@@ -36,6 +37,10 @@ | |||
36 | #define O_PATH 010000000 | 37 | #define O_PATH 010000000 |
37 | #endif | 38 | #endif |
38 | 39 | ||
40 | #ifdef __NR_openat2 | ||
41 | #include <linux/openat2.h> | ||
42 | #endif | ||
43 | |||
39 | #define MAX_GROUPS 1024 | 44 | #define MAX_GROUPS 1024 |
40 | #define MAXBUF 4098 | 45 | #define MAXBUF 4098 |
41 | #define EMPTY_STRING ("") | 46 | #define EMPTY_STRING ("") |
@@ -1157,46 +1162,57 @@ void disable_file_path(const char *path, const char *file) { | |||
1157 | free(fname); | 1162 | free(fname); |
1158 | } | 1163 | } |
1159 | 1164 | ||
1160 | // open file without following any symbolic link | 1165 | // open an existing file without following any symbolic link |
1161 | // returns a file descriptor on success, or -1 if a symlink is found | ||
1162 | int safe_fd(const char *path, int flags) { | 1166 | int safe_fd(const char *path, int flags) { |
1163 | assert(path); | 1167 | assert(path); |
1164 | if (*path != '/') | 1168 | if (*path != '/' || strstr(path, "..")) { |
1165 | goto errexit; | 1169 | fprintf(stderr, "Error: invalid path %s\n", path); |
1166 | if (strstr(path, "..")) | 1170 | exit(1); |
1167 | goto errexit; | 1171 | } |
1168 | 1172 | flags |= O_NOFOLLOW; | |
1169 | int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); | ||
1170 | if (parentfd == -1) | ||
1171 | errExit("open"); | ||
1172 | int fd = -1; | 1173 | int fd = -1; |
1173 | 1174 | ||
1174 | char *last_tok = EMPTY_STRING; | 1175 | #ifdef __NR_openat2 // kernel 5.6 or better |
1176 | struct open_how oh; | ||
1177 | memset(&oh, 0, sizeof(oh)); | ||
1178 | oh.flags = flags; | ||
1179 | oh.resolve = RESOLVE_NO_SYMLINKS; | ||
1180 | fd = syscall(__NR_openat2, -1, path, &oh, sizeof(struct open_how)); | ||
1181 | if (fd != -1 || errno != ENOSYS) | ||
1182 | return fd; | ||
1183 | #endif | ||
1184 | |||
1185 | // openat2 syscall is not available, traverse path and | ||
1186 | // check each component if it is a symbolic link or not | ||
1175 | char *dup = strdup(path); | 1187 | char *dup = strdup(path); |
1176 | if (!dup) | 1188 | if (!dup) |
1177 | errExit("strdup"); | 1189 | errExit("strdup"); |
1178 | char *tok = strtok(dup, "/"); | 1190 | char *tok = strtok(dup, "/"); |
1179 | if (!tok) { // root directory | 1191 | if (!tok) { // root directory |
1180 | free(dup); | 1192 | free(dup); |
1181 | return parentfd; | 1193 | return open("/", flags); |
1182 | } | 1194 | } |
1195 | char *last_tok = EMPTY_STRING; | ||
1196 | int parentfd = open("/", O_PATH|O_CLOEXEC); | ||
1197 | if (parentfd == -1) | ||
1198 | errExit("open"); | ||
1183 | 1199 | ||
1184 | while(1) { | 1200 | while(1) { |
1185 | // open the element, assuming it is a directory; this fails with ENOTDIR if it is a symbolic link | 1201 | // open path component, assuming it is a directory; this fails with ENOTDIR if it is a symbolic link |
1186 | // if token is a single dot, the previous directory is reopened | 1202 | // if token is a single dot, the previous directory is reopened |
1187 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 1203 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
1188 | if (fd == -1) { | 1204 | if (fd == -1) { |
1189 | // if the following token is NULL, the current token is the final path element | 1205 | // if the following token is NULL, the current token is the final path component |
1190 | // try again to open it, this time using the passed flags, and return -1 or the descriptor | 1206 | // try again to open it, this time using the passed flags, and return -1 or the descriptor |
1191 | last_tok = tok; | 1207 | last_tok = tok; |
1192 | tok = strtok(NULL, "/"); | 1208 | tok = strtok(NULL, "/"); |
1193 | if (!tok) | 1209 | if (!tok) |
1194 | fd = openat(parentfd, last_tok, flags|O_NOFOLLOW); | 1210 | fd = openat(parentfd, last_tok, flags); |
1195 | close(parentfd); | 1211 | close(parentfd); |
1196 | free(dup); | 1212 | free(dup); |
1197 | return fd; // -1 if open failed | 1213 | return fd; |
1198 | } | 1214 | } |
1199 | // move on to next path segment | 1215 | // move on to next path component |
1200 | last_tok = tok; | 1216 | last_tok = tok; |
1201 | tok = strtok(NULL, "/"); | 1217 | tok = strtok(NULL, "/"); |
1202 | if (!tok) | 1218 | if (!tok) |
@@ -1204,18 +1220,13 @@ int safe_fd(const char *path, int flags) { | |||
1204 | close(parentfd); | 1220 | close(parentfd); |
1205 | parentfd = fd; | 1221 | parentfd = fd; |
1206 | } | 1222 | } |
1207 | 1223 | // getting here when the last path component exists and is of file type directory | |
1208 | // we are here because the last path element exists and is of file type directory | ||
1209 | // reopen it using the passed flags | 1224 | // reopen it using the passed flags |
1210 | close(fd); | 1225 | close(fd); |
1211 | fd = openat(parentfd, last_tok, flags|O_NOFOLLOW); | 1226 | fd = openat(parentfd, last_tok, flags); |
1212 | close(parentfd); | 1227 | close(parentfd); |
1213 | free(dup); | 1228 | free(dup); |
1214 | return fd; // -1 if open failed | 1229 | return fd; |
1215 | |||
1216 | errexit: | ||
1217 | fprintf(stderr, "Error: cannot open \"%s\": invalid path\n", path); | ||
1218 | exit(1); | ||
1219 | } | 1230 | } |
1220 | 1231 | ||
1221 | int has_handler(pid_t pid, int signal) { | 1232 | int has_handler(pid_t pid, int signal) { |
@@ -1321,7 +1332,7 @@ static int has_link(const char *dir) { | |||
1321 | assert(dir); | 1332 | assert(dir); |
1322 | int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 1333 | int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
1323 | if (fd == -1) { | 1334 | if (fd == -1) { |
1324 | if (errno == ENOTDIR && is_dir(dir)) | 1335 | if ((errno == ELOOP || errno == ENOTDIR) && is_dir(dir)) |
1325 | return 1; | 1336 | return 1; |
1326 | } | 1337 | } |
1327 | else | 1338 | else |