aboutsummaryrefslogtreecommitdiffstats
path: root/src/fldd/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fldd/main.c')
-rw-r--r--src/fldd/main.c353
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
33static int arg_quiet = 0;
34static void copy_libs_for_lib(const char *lib);
35
36typedef struct storage_t {
37 struct storage_t *next;
38 const char *name;
39} Storage;
40static Storage *libs = NULL;
41static Storage *lib_paths = NULL;
42
43// return 1 if found
44static 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
54static 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
69static void storage_print(Storage *ptr, int fd) {
70 while (ptr) {
71 dprintf(fd, "%s\n", ptr->name);
72 ptr = ptr->next;
73 }
74}
75
76static 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
85static 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
205static 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
228static 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
235static 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
277static 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
283int main(int argc, char **argv) {
284#if 0
285{
286//system("cat /proc/self/status");
287int i;
288for (i = 0; i < argc; i++)
289 printf("*%s* ", argv[i]);
290printf("\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}