diff options
Diffstat (limited to 'src/fldd/main.c')
-rw-r--r-- | src/fldd/main.c | 353 |
1 files changed, 0 insertions, 353 deletions
diff --git a/src/fldd/main.c b/src/fldd/main.c deleted file mode 100644 index 4658e82fb..000000000 --- a/src/fldd/main.c +++ /dev/null | |||
@@ -1,353 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 Firejail Authors | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include "../include/common.h" | ||
22 | #include "../include/ldd_utils.h" | ||
23 | |||
24 | #include <fcntl.h> | ||
25 | #include <sys/mman.h> | ||
26 | #include <sys/mount.h> | ||
27 | #include <sys/stat.h> | ||
28 | #include <sys/types.h> | ||
29 | #include <unistd.h> | ||
30 | #include <dirent.h> | ||
31 | |||
32 | |||
33 | static int arg_quiet = 0; | ||
34 | static void copy_libs_for_lib(const char *lib); | ||
35 | |||
36 | typedef struct storage_t { | ||
37 | struct storage_t *next; | ||
38 | const char *name; | ||
39 | } Storage; | ||
40 | static Storage *libs = NULL; | ||
41 | static Storage *lib_paths = NULL; | ||
42 | |||
43 | // return 1 if found | ||
44 | static int storage_find(Storage *ptr, const char *name) { | ||
45 | while (ptr) { | ||
46 | if (strcmp(ptr->name, name) == 0) | ||
47 | return 1; | ||
48 | ptr = ptr->next; | ||
49 | } | ||
50 | |||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | static void storage_add(Storage **head, const char *name) { | ||
55 | if (storage_find(*head, name)) | ||
56 | return; | ||
57 | |||
58 | Storage *s = malloc(sizeof(Storage)); | ||
59 | if (!s) | ||
60 | errExit("malloc"); | ||
61 | s->next = *head; | ||
62 | *head = s; | ||
63 | s->name = strdup(name); | ||
64 | if (!s->name) | ||
65 | errExit("strdup"); | ||
66 | } | ||
67 | |||
68 | |||
69 | static void storage_print(Storage *ptr, int fd) { | ||
70 | while (ptr) { | ||
71 | dprintf(fd, "%s\n", ptr->name); | ||
72 | ptr = ptr->next; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | static bool ptr_ok(const void *ptr, const void *base, const void *end, const char *name) { | ||
77 | bool r; | ||
78 | (void) name; | ||
79 | |||
80 | r = (ptr >= base && ptr < end); | ||
81 | return r; | ||
82 | } | ||
83 | |||
84 | |||
85 | static void parse_elf(const char *exe) { | ||
86 | int f; | ||
87 | f = open(exe, O_RDONLY); | ||
88 | if (f < 0) { | ||
89 | if (!arg_quiet) | ||
90 | fprintf(stderr, "Warning fldd: cannot open %s, skipping...\n", exe); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | struct stat s; | ||
95 | char *base = NULL, *end; | ||
96 | if (fstat(f, &s) == -1) | ||
97 | goto error_close; | ||
98 | base = mmap(0, s.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, f, 0); | ||
99 | if (base == MAP_FAILED) | ||
100 | goto error_close; | ||
101 | |||
102 | end = base + s.st_size; | ||
103 | |||
104 | Elf_Ehdr *ebuf = (Elf_Ehdr *)base; | ||
105 | if (strncmp((const char *)ebuf->e_ident, ELFMAG, SELFMAG) != 0) { | ||
106 | if (!arg_quiet) | ||
107 | fprintf(stderr, "Warning fldd: %s is not an ELF executable or library\n", exe); | ||
108 | goto close; | ||
109 | } | ||
110 | //unsigned char elfclass = ebuf->e_ident[EI_CLASS]; | ||
111 | //if (elfclass == ELFCLASS32) | ||
112 | //printf("%s 32bit\n", exe); | ||
113 | //else if (elfclass == ELFCLASS64) | ||
114 | //printf("%s 64bit\n", exe); | ||
115 | |||
116 | |||
117 | Elf_Phdr *pbuf = (Elf_Phdr *)(base + sizeof(*ebuf)); | ||
118 | while (ebuf->e_phnum-- > 0 && ptr_ok(pbuf, base, end, "pbuf")) { | ||
119 | switch (pbuf->p_type) { | ||
120 | case PT_INTERP: | ||
121 | // dynamic loader ld-linux.so | ||
122 | if (!ptr_ok(base + pbuf->p_offset, base, end, "base + pbuf->p_offset")) | ||
123 | goto close; | ||
124 | |||
125 | storage_add(&libs, base + pbuf->p_offset); | ||
126 | break; | ||
127 | } | ||
128 | pbuf++; | ||
129 | } | ||
130 | |||
131 | Elf_Shdr *sbuf = (Elf_Shdr *)(base + ebuf->e_shoff); | ||
132 | if (!ptr_ok(sbuf, base, end, "sbuf")) | ||
133 | goto close; | ||
134 | |||
135 | // Find strings section | ||
136 | char *strbase = NULL; | ||
137 | int sections = ebuf->e_shnum; | ||
138 | while (sections-- > 0 && ptr_ok(sbuf, base, end, "sbuf")) { | ||
139 | if (sbuf->sh_type == SHT_STRTAB) { | ||
140 | strbase = base + sbuf->sh_offset; | ||
141 | if (!ptr_ok(strbase, base, end, "strbase")) | ||
142 | goto close; | ||
143 | break; | ||
144 | } | ||
145 | sbuf++; | ||
146 | } | ||
147 | if (strbase == NULL) | ||
148 | goto error_close; | ||
149 | |||
150 | // Find dynamic section | ||
151 | sections = ebuf->e_shnum; | ||
152 | while (sections-- > 0 && ptr_ok(sbuf, base, end, "sbuf")) { | ||
153 | // TODO: running fldd on large gui programs (fldd /usr/bin/transmission-qt) | ||
154 | // crash on accessing memory location sbuf->sh_type if sbuf->sh_type in the previous section was 0 (SHT_NULL) | ||
155 | // for now we just exit the while loop - this is probably incorrect | ||
156 | // printf("sbuf %p #%s#, sections %d, type %u\n", sbuf, exe, sections, sbuf->sh_type); | ||
157 | if (!ptr_ok(sbuf, base, end, "sbuf")) | ||
158 | goto close; | ||
159 | |||
160 | if (sbuf->sh_type == SHT_NULL) | ||
161 | break; | ||
162 | if (sbuf->sh_type == SHT_DYNAMIC) { | ||
163 | Elf_Dyn *dbuf = (Elf_Dyn *)(base + sbuf->sh_offset); | ||
164 | if (!ptr_ok(dbuf, base, end, "dbuf")) | ||
165 | goto close; | ||
166 | // Find DT_RPATH/DT_RUNPATH tags first | ||
167 | unsigned long size = sbuf->sh_size; | ||
168 | while (size >= sizeof(*dbuf) && ptr_ok(dbuf, base, end, "dbuf")) { | ||
169 | if (dbuf->d_tag == DT_RPATH || dbuf->d_tag == DT_RUNPATH) { | ||
170 | const char *searchpath = strbase + dbuf->d_un.d_ptr; | ||
171 | if (!ptr_ok(searchpath, base, end, "searchpath")) | ||
172 | goto close; | ||
173 | storage_add(&lib_paths, searchpath); | ||
174 | } | ||
175 | size -= sizeof(*dbuf); | ||
176 | dbuf++; | ||
177 | } | ||
178 | // Find DT_NEEDED tags | ||
179 | dbuf = (Elf_Dyn *)(base + sbuf->sh_offset); | ||
180 | size = sbuf->sh_size; | ||
181 | while (size >= sizeof(*dbuf) && ptr_ok(dbuf, base, end, "dbuf")) { | ||
182 | if (dbuf->d_tag == DT_NEEDED) { | ||
183 | const char *lib = strbase + dbuf->d_un.d_ptr; | ||
184 | if (!ptr_ok(lib, base, end, "lib")) | ||
185 | goto close; | ||
186 | copy_libs_for_lib(lib); | ||
187 | } | ||
188 | size -= sizeof(*dbuf); | ||
189 | dbuf++; | ||
190 | } | ||
191 | } | ||
192 | sbuf++; | ||
193 | } | ||
194 | goto close; | ||
195 | |||
196 | error_close: | ||
197 | perror("copy libs"); | ||
198 | close: | ||
199 | if (base) | ||
200 | munmap(base, s.st_size); | ||
201 | |||
202 | close(f); | ||
203 | } | ||
204 | |||
205 | static void copy_libs_for_lib(const char *lib) { | ||
206 | Storage *lib_path; | ||
207 | for (lib_path = lib_paths; lib_path; lib_path = lib_path->next) { | ||
208 | char *fname; | ||
209 | if (asprintf(&fname, "%s/%s", lib_path->name, lib) == -1) | ||
210 | errExit("asprintf"); | ||
211 | if (access(fname, R_OK) == 0 && is_lib_64(fname)) { | ||
212 | if (!storage_find(libs, fname)) { | ||
213 | storage_add(&libs, fname); | ||
214 | // libs may need other libs | ||
215 | parse_elf(fname); | ||
216 | } | ||
217 | free(fname); | ||
218 | return; | ||
219 | } | ||
220 | free(fname); | ||
221 | } | ||
222 | |||
223 | // log a warning and continue | ||
224 | if (!arg_quiet) | ||
225 | fprintf(stderr, "Warning fldd: cannot find %s, skipping...\n", lib); | ||
226 | } | ||
227 | |||
228 | static void lib_paths_init(void) { | ||
229 | int i; | ||
230 | for (i = 0; default_lib_paths[i]; i++) | ||
231 | storage_add(&lib_paths, default_lib_paths[i]); | ||
232 | } | ||
233 | |||
234 | |||
235 | static void walk_directory(const char *dirname) { | ||
236 | assert(dirname); | ||
237 | |||
238 | DIR *dir = opendir(dirname); | ||
239 | if (dir) { | ||
240 | struct dirent *entry; | ||
241 | while ((entry = readdir(dir)) != NULL) { | ||
242 | if (strcmp(entry->d_name, ".") == 0) | ||
243 | continue; | ||
244 | if (strcmp(entry->d_name, "..") == 0) | ||
245 | continue; | ||
246 | |||
247 | // build full path | ||
248 | char *path; | ||
249 | if (asprintf(&path, "%s/%s", dirname, entry->d_name) == -1) | ||
250 | errExit("asprintf"); | ||
251 | |||
252 | // check regular so library | ||
253 | char *ptr = strstr(entry->d_name, ".so"); | ||
254 | if (ptr && is_lib_64(path)) { | ||
255 | if (*(ptr + 3) == '\0' || *(ptr + 3) == '.') { | ||
256 | parse_elf(path); | ||
257 | free(path); | ||
258 | continue; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | // check directory | ||
263 | // entry->d_type field is supported in glibc since version 2.19 (Feb 2014) | ||
264 | // we'll use stat to check for directories | ||
265 | struct stat s; | ||
266 | if (stat(path, &s) == -1) | ||
267 | errExit("stat"); | ||
268 | if (S_ISDIR(s.st_mode)) | ||
269 | walk_directory(path); | ||
270 | } | ||
271 | closedir(dir); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | |||
276 | |||
277 | static void usage(void) { | ||
278 | printf("Usage: fldd program_or_directory [file]\n"); | ||
279 | printf("Print a list of libraries used by program or store it in the file.\n"); | ||
280 | printf("Print a list of libraries used by all .so files in a directory or store it in the file.\n"); | ||
281 | } | ||
282 | |||
283 | int main(int argc, char **argv) { | ||
284 | #if 0 | ||
285 | { | ||
286 | //system("cat /proc/self/status"); | ||
287 | int i; | ||
288 | for (i = 0; i < argc; i++) | ||
289 | printf("*%s* ", argv[i]); | ||
290 | printf("\n"); | ||
291 | } | ||
292 | #endif | ||
293 | if (argc < 2) { | ||
294 | fprintf(stderr, "Error fldd: invalid arguments\n"); | ||
295 | usage(); | ||
296 | exit(1); | ||
297 | } | ||
298 | |||
299 | |||
300 | if (strcmp(argv[1], "--help") == 0) { | ||
301 | usage(); | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | // check program access | ||
306 | if (access(argv[1], R_OK)) { | ||
307 | fprintf(stderr, "Error fldd: cannot access %s\n", argv[1]); | ||
308 | exit(1); | ||
309 | } | ||
310 | |||
311 | char *quiet = getenv("FIREJAIL_QUIET"); | ||
312 | if (quiet && strcmp(quiet, "yes") == 0) | ||
313 | arg_quiet = 1; | ||
314 | |||
315 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { | ||
316 | usage(); | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | int fd = STDOUT_FILENO; | ||
321 | // attempt to open the file | ||
322 | if (argc == 3) { | ||
323 | fd = open(argv[2], O_CREAT | O_TRUNC | O_WRONLY, 0644); | ||
324 | if (!fd) { | ||
325 | fprintf(stderr, "Error fldd: invalid arguments\n"); | ||
326 | usage(); | ||
327 | exit(1); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | // initialize local storage | ||
332 | lib_paths_init(); | ||
333 | |||
334 | // process files | ||
335 | struct stat s; | ||
336 | if (stat(argv[1], &s) == -1) | ||
337 | errExit("stat"); | ||
338 | if (S_ISDIR(s.st_mode)) | ||
339 | walk_directory(argv[1]); | ||
340 | else { | ||
341 | if (is_lib_64(argv[1])) | ||
342 | parse_elf(argv[1]); | ||
343 | else | ||
344 | fprintf(stderr, "Warning fldd: %s is not a 64bit program/library\n", argv[1]); | ||
345 | } | ||
346 | |||
347 | |||
348 | // print libraries and exit | ||
349 | storage_print(libs, fd); | ||
350 | if (argc == 3) | ||
351 | close(fd); | ||
352 | return 0; | ||
353 | } | ||