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 fa32ffcc8..18f837d7e 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -1059,54 +1059,71 @@ void disable_file_path(const char *path, const char *file) { | |||
1059 | // user controlled paths. Passed flags are ignored if path is a top level directory. | 1059 | // user controlled paths. Passed flags are ignored if path is a top level directory. |
1060 | int safe_fd(const char *path, int flags) { | 1060 | int safe_fd(const char *path, int flags) { |
1061 | assert(path); | 1061 | assert(path); |
1062 | int fd = -1; | 1062 | |
1063 | // reject empty string, relative path | ||
1064 | if (*path != '/') | ||
1065 | goto errexit; | ||
1066 | // reject ".." | ||
1067 | if (strstr(path, "..")) | ||
1068 | goto errexit; | ||
1063 | 1069 | ||
1064 | // work with a copy of path | 1070 | // work with a copy of path |
1065 | char *dup = strdup(path); | 1071 | char *dup = strdup(path); |
1066 | if (dup == NULL) | 1072 | if (dup == NULL) |
1067 | errExit("strdup"); | 1073 | errExit("strdup"); |
1068 | // reject relative path and empty string | ||
1069 | if (*dup != '/') { | ||
1070 | fprintf(stderr, "Error: invalid pathname: %s\n", path); | ||
1071 | exit(1); | ||
1072 | } | ||
1073 | 1074 | ||
1074 | char *p = strrchr(dup, '/'); | 1075 | char *p = strrchr(dup, '/'); |
1075 | if (p == NULL) | 1076 | if (p == NULL) |
1076 | errExit("strrchr"); | 1077 | errExit("strrchr"); |
1077 | // reject trailing slash and root dir | 1078 | // reject trailing slash, root directory |
1078 | if (*(p + 1) == '\0') { | 1079 | if (*(p + 1) == '\0') |
1079 | fprintf(stderr, "Error: invalid pathname: %s\n", path); | 1080 | goto errexit; |
1080 | exit(1); | 1081 | // reject trailing dot |
1081 | } | 1082 | if (*(p + 1) == '.' && *(p + 2) == '\0') |
1083 | goto errexit; | ||
1084 | // if there is more than one path segment, keep the last one for later | ||
1085 | if (p != dup) | ||
1086 | *p = '\0'; | ||
1082 | 1087 | ||
1083 | int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); | 1088 | int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); |
1084 | if (parentfd == -1) | 1089 | if (parentfd == -1) |
1085 | errExit("open"); | 1090 | errExit("open"); |
1086 | 1091 | ||
1087 | // if there is more than one path segment, keep the last one for later | 1092 | // traverse the path and return -1 if a symlink is encountered |
1088 | if (p != dup) | 1093 | int entered = 0; |
1089 | *p = '\0'; | 1094 | int fd = -1; |
1090 | |||
1091 | // traverse the path, return -1 if a symlink is encountered | ||
1092 | char *tok = strtok(dup, "/"); | 1095 | char *tok = strtok(dup, "/"); |
1093 | if (tok == NULL) | ||
1094 | errExit("strtok"); | ||
1095 | while (tok) { | 1096 | while (tok) { |
1097 | // skip all "/./" | ||
1098 | if (strcmp(tok, ".") == 0) { | ||
1099 | tok = strtok(NULL, "/"); | ||
1100 | continue; | ||
1101 | } | ||
1102 | entered = 1; | ||
1103 | |||
1104 | // open the directory | ||
1096 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 1105 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
1097 | close(parentfd); | 1106 | close(parentfd); |
1098 | if (fd == -1) { | 1107 | if (fd == -1) { |
1099 | free(dup); | 1108 | free(dup); |
1100 | return -1; | 1109 | return -1; |
1101 | } | 1110 | } |
1111 | |||
1102 | parentfd = fd; | 1112 | parentfd = fd; |
1103 | tok = strtok(NULL, "/"); | 1113 | tok = strtok(NULL, "/"); |
1104 | } | 1114 | } |
1105 | if (p != dup) { | 1115 | if (p != dup) { |
1116 | // consistent flags for top level directories (////foo, /.///foo) | ||
1117 | if (!entered) | ||
1118 | flags = O_PATH|O_DIRECTORY|O_CLOEXEC; | ||
1106 | // open last path segment | 1119 | // open last path segment |
1107 | fd = openat(parentfd, p + 1, flags|O_NOFOLLOW); | 1120 | fd = openat(parentfd, p + 1, flags|O_NOFOLLOW); |
1108 | close(parentfd); | 1121 | close(parentfd); |
1109 | } | 1122 | } |
1110 | free(dup); | 1123 | free(dup); |
1111 | return fd; // -1 if open failed | 1124 | return fd; // -1 if open failed |
1125 | |||
1126 | errexit: | ||
1127 | fprintf(stderr, "Error: cannot open \"%s\", invalid filename\n", path); | ||
1128 | exit(1); | ||
1112 | } | 1129 | } |