diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/firejail.h | 2 | ||||
-rw-r--r-- | src/firejail/fs.c | 36 | ||||
-rw-r--r-- | src/firejail/paths.c | 191 |
3 files changed, 144 insertions, 85 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index b7d2c4304..fbf83abb3 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -636,6 +636,8 @@ void run_symlink(int argc, char **argv); | |||
636 | 636 | ||
637 | // paths.c | 637 | // paths.c |
638 | char **build_paths(void); | 638 | char **build_paths(void); |
639 | unsigned int count_paths(void); | ||
640 | int program_in_path(const char *program); | ||
639 | 641 | ||
640 | // fs_mkdir.c | 642 | // fs_mkdir.c |
641 | void fs_mkdir(const char *name); | 643 | void fs_mkdir(const char *name); |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 27de337bb..c386f70cf 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -289,26 +289,35 @@ void fs_blacklist(void) { | |||
289 | 289 | ||
290 | // Process noblacklist command | 290 | // Process noblacklist command |
291 | if (strncmp(entry->data, "noblacklist ", 12) == 0) { | 291 | if (strncmp(entry->data, "noblacklist ", 12) == 0) { |
292 | char **paths = build_paths(); | 292 | char **enames; |
293 | 293 | int i; | |
294 | char *enames[sizeof(paths)+1] = {0}; | ||
295 | int i = 0; | ||
296 | 294 | ||
297 | if (strncmp(entry->data + 12, "${PATH}", 7) == 0) { | 295 | if (strncmp(entry->data + 12, "${PATH}", 7) == 0) { |
298 | // expand ${PATH} macro | 296 | // expand ${PATH} macro |
299 | while (paths[i] != NULL) { | 297 | char **paths = build_paths(); |
300 | if (asprintf(&enames[i], "%s%s", paths[i], entry->data + 19) == -1) | 298 | unsigned int npaths = count_paths(); |
299 | enames = calloc(npaths, sizeof(char *)); | ||
300 | if (!enames) | ||
301 | errExit("calloc"); | ||
302 | |||
303 | for (i = 0; paths[i]; i++) { | ||
304 | if (asprintf(&enames[i], "%s%s", paths[i], | ||
305 | entry->data + 19) == -1) | ||
301 | errExit("asprintf"); | 306 | errExit("asprintf"); |
302 | i++; | ||
303 | } | 307 | } |
304 | } else { | 308 | assert(enames[npaths-1] == 0); |
309 | |||
310 | } | ||
311 | else { | ||
305 | // expand ${HOME} macro if found or pass as is | 312 | // expand ${HOME} macro if found or pass as is |
313 | enames = calloc(2, sizeof(char *)); | ||
314 | if (!enames) | ||
315 | errExit("calloc"); | ||
306 | enames[0] = expand_home(entry->data + 12, homedir); | 316 | enames[0] = expand_home(entry->data + 12, homedir); |
307 | enames[1] = NULL; | 317 | assert(enames[1] == 0); |
308 | } | 318 | } |
309 | 319 | ||
310 | i = 0; | 320 | for (i = 0; enames[i]; i++) { |
311 | while (enames[i] != NULL) { | ||
312 | if (noblacklist_c >= noblacklist_m) { | 321 | if (noblacklist_c >= noblacklist_m) { |
313 | noblacklist_m *= 2; | 322 | noblacklist_m *= 2; |
314 | noblacklist = realloc(noblacklist, sizeof(*noblacklist) * noblacklist_m); | 323 | noblacklist = realloc(noblacklist, sizeof(*noblacklist) * noblacklist_m); |
@@ -316,12 +325,9 @@ void fs_blacklist(void) { | |||
316 | errExit("failed increasing memory for noblacklist entries"); | 325 | errExit("failed increasing memory for noblacklist entries"); |
317 | } | 326 | } |
318 | noblacklist[noblacklist_c++] = enames[i]; | 327 | noblacklist[noblacklist_c++] = enames[i]; |
319 | i++; | ||
320 | } | 328 | } |
321 | 329 | ||
322 | while (enames[i] != NULL) { | 330 | free(enames); |
323 | free(enames[i]); | ||
324 | } | ||
325 | 331 | ||
326 | entry = entry->next; | 332 | entry = entry->next; |
327 | continue; | 333 | continue; |
diff --git a/src/firejail/paths.c b/src/firejail/paths.c index 69c4b359b..454255717 100644 --- a/src/firejail/paths.c +++ b/src/firejail/paths.c | |||
@@ -18,83 +18,134 @@ | |||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | #include "firejail.h" | 20 | #include "firejail.h" |
21 | #include <sys/stat.h> | ||
21 | 22 | ||
22 | static char **paths = NULL; | 23 | static char **paths = 0; |
23 | static int path_cnt = 0; | 24 | static unsigned int path_cnt = 0; |
24 | static char initialized = 0; | 25 | static unsigned int longest_path_elt = 0; |
25 | 26 | ||
26 | static void add_path(const char *path) { | 27 | static void init_paths(void) { |
27 | assert(paths); | 28 | char *path = getenv("PATH"); |
28 | assert(path_cnt); | 29 | char *p; |
29 | 30 | if (!path) { | |
30 | // filter out duplicates | 31 | path = "/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin"; |
31 | int i; | 32 | setenv("PATH", path, 1); |
32 | int empty = 0; | ||
33 | for (i = 0; i < path_cnt; i++) { | ||
34 | if (paths[i] && strcmp(path, paths[i]) == 0) { | ||
35 | return; | ||
36 | } | ||
37 | if (!paths[i]) { | ||
38 | empty = i; | ||
39 | break; | ||
40 | } | ||
41 | } | 33 | } |
42 | 34 | path = strdup(path); | |
43 | paths[empty] = strdup(path); | 35 | if (!path) |
44 | if (!paths[empty]) | ||
45 | errExit("strdup"); | 36 | errExit("strdup"); |
37 | |||
38 | // size the paths array | ||
39 | for (p = path; *p; p++) | ||
40 | if (*p == ':') | ||
41 | path_cnt++; | ||
42 | path_cnt += 2; // one because we were counting fenceposts, one for the NULL at the end | ||
43 | |||
44 | paths = calloc(path_cnt, sizeof(char *)); | ||
45 | if (!paths) | ||
46 | errExit("calloc"); | ||
47 | |||
48 | // fill in 'paths' with pointers to elements of 'path' | ||
49 | char *elt; | ||
50 | unsigned int i = 0, j; | ||
51 | unsigned int len; | ||
52 | while ((elt = strsep(&path, ":")) != 0) { | ||
53 | // skip any entry that is not absolute | ||
54 | if (elt[0] != '/') | ||
55 | goto skip; | ||
56 | |||
57 | // strip trailing slashes (this also prevents '/' from being a path entry). | ||
58 | len = strlen(elt); | ||
59 | while (len > 0 && elt[len-1] == '/') | ||
60 | elt[--len] = '\0'; | ||
61 | if (len == 0) | ||
62 | goto skip; | ||
63 | |||
64 | // filter out duplicate entries | ||
65 | for (j = 0; j < i; j++) | ||
66 | if (strcmp(elt, paths[j]) == 0) | ||
67 | goto skip; | ||
68 | |||
69 | paths[i++] = elt; | ||
70 | if (len > longest_path_elt) | ||
71 | longest_path_elt = len; | ||
72 | |||
73 | skip:; | ||
74 | } | ||
75 | |||
76 | assert(paths[i] == 0); | ||
77 | // path_cnt may be too big now, if entries were skipped above | ||
78 | path_cnt = i+1; | ||
46 | } | 79 | } |
47 | 80 | ||
81 | |||
48 | char **build_paths(void) { | 82 | char **build_paths(void) { |
49 | if (initialized) { | 83 | if (!paths) |
50 | assert(paths); | 84 | init_paths(); |
51 | return paths; | 85 | assert(paths); |
52 | } | 86 | return paths; |
53 | initialized = 1; | 87 | } |
54 | 88 | ||
55 | int cnt = 5; // 4 default paths + 1 NULL to end the array | 89 | // Note: the NULL element at the end of 'paths' is included in this count. |
56 | char *path1 = getenv("PATH"); | 90 | unsigned int count_paths(void) { |
57 | if (path1) { | 91 | if (!path_cnt) |
58 | char *path2 = strdup(path1); | 92 | init_paths(); |
59 | if (!path2) | 93 | assert(path_cnt); |
60 | errExit("strdup"); | 94 | return path_cnt; |
61 | 95 | } | |
62 | // use path2 to count the entries | 96 | |
63 | char *ptr = strtok(path2, ":"); | 97 | // Return 1 if PROGRAM exists in $PATH and is runnable by the |
64 | while (ptr) { | 98 | // invoking user (not root). |
65 | cnt++; | 99 | // In other words, tests "will execvp(PROGRAM, ...) succeed?" |
66 | ptr = strtok(NULL, ":"); | 100 | int program_in_path(const char *program) { |
67 | } | 101 | assert(program && *program); |
68 | free(path2); | 102 | assert(strchr(program, '/') == 0); |
69 | path_cnt = cnt; | 103 | assert(strcmp(program, ".") != 0); |
70 | 104 | assert(strcmp(program, "..") != 0); | |
71 | // allocate paths array | 105 | |
72 | paths = malloc(sizeof(char *) * cnt); | 106 | if (!paths) |
73 | if (!paths) | 107 | init_paths(); |
74 | errExit("malloc"); | 108 | assert(paths); |
75 | memset(paths, 0, sizeof(char *) * cnt); | 109 | |
76 | 110 | size_t proglen = strlen(program); | |
77 | // add default paths | 111 | char *scratch = malloc(longest_path_elt + proglen + 2); |
78 | add_path("/usr/local/bin"); | 112 | if (!scratch) |
79 | add_path("/usr/bin"); | 113 | errExit("malloc"); |
80 | add_path("/bin"); | 114 | |
81 | add_path("/usr/local/sbin"); | 115 | int found = 0; |
82 | add_path("/usr/sbin"); | 116 | size_t dlen; |
83 | add_path("/sbin"); | 117 | char **p; |
84 | 118 | for (p = paths; *p; p++) { | |
85 | path2 = strdup(path1); | 119 | char *dir = *p; |
86 | if (!path2) | 120 | dlen = strlen(dir); |
87 | errExit("strdup"); | 121 | |
88 | 122 | // init_paths should ensure that this is true; as long | |
89 | // use path2 to count the entries | 123 | // as it is true, 'scratch' has enough space for "$p/$program". |
90 | ptr = strtok(path2, ":"); | 124 | assert(dlen <= longest_path_elt); |
91 | while (ptr) { | 125 | |
92 | cnt++; | 126 | memcpy(scratch, dir, dlen); |
93 | add_path(ptr); | 127 | scratch[dlen++] = '/'; |
94 | ptr = strtok(NULL, ":"); | 128 | |
129 | // copy proglen+1 bytes to copy the nul terminator at | ||
130 | // the end of 'program'. | ||
131 | memcpy(scratch + dlen, program, proglen+1); | ||
132 | |||
133 | if (access(scratch, X_OK) == 0) { | ||
134 | // must also verify that this is a regular file | ||
135 | // ('x' permission means something different for directories). | ||
136 | // exec follows symlinks, so use stat, not lstat. | ||
137 | struct stat st; | ||
138 | if (stat(scratch, &st)) { | ||
139 | perror(scratch); | ||
140 | exit(1); | ||
141 | } | ||
142 | if (S_ISREG(st.st_mode)) { | ||
143 | found = 1; | ||
144 | break; | ||
145 | } | ||
95 | } | 146 | } |
96 | free(path2); | ||
97 | } | 147 | } |
98 | 148 | ||
99 | return paths; | 149 | free(scratch); |
150 | return found; | ||
100 | } | 151 | } |