diff options
author | 2017-08-04 11:14:04 -0400 | |
---|---|---|
committer | 2017-08-04 11:14:04 -0400 | |
commit | a9babdfbce0a88dd7a21584a514b1479bc4c98f6 (patch) | |
tree | 4966fc0ec278654b57a3bc9f865022fb1f21f49a /src/fldd/main.c | |
parent | Merge branch 'master' of https://github.com/netblue30/firejail (diff) | |
download | firejail-a9babdfbce0a88dd7a21584a514b1479bc4c98f6.tar.gz firejail-a9babdfbce0a88dd7a21584a514b1479bc4c98f6.tar.zst firejail-a9babdfbce0a88dd7a21584a514b1479bc4c98f6.zip |
private-lib: add src/fldd
Diffstat (limited to 'src/fldd/main.c')
-rw-r--r-- | src/fldd/main.c | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/src/fldd/main.c b/src/fldd/main.c new file mode 100644 index 000000000..8b54242a4 --- /dev/null +++ b/src/fldd/main.c | |||
@@ -0,0 +1,262 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2017 netblue30 (netblue30@yahoo.com) | ||
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 | |||
23 | #include <elf.h> | ||
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 | |||
31 | #ifdef __LP64__ | ||
32 | #define Elf_Ehdr Elf64_Ehdr | ||
33 | #define Elf_Phdr Elf64_Phdr | ||
34 | #define Elf_Shdr Elf64_Shdr | ||
35 | #define Elf_Dyn Elf64_Dyn | ||
36 | #else | ||
37 | #define Elf_Ehdr Elf32_Ehdr | ||
38 | #define Elf_Phdr Elf32_Phdr | ||
39 | #define Elf_Shdr Elf32_Shdr | ||
40 | #define Elf_Dyn Elf32_Dyn | ||
41 | #endif | ||
42 | |||
43 | static int arg_quiet = 0; | ||
44 | static void copy_libs_for_lib(const char *lib); | ||
45 | |||
46 | static const char * const lib_paths[] = { | ||
47 | "/lib", | ||
48 | "/lib/x86_64-linux-gnu", | ||
49 | "/lib64", | ||
50 | "/usr/lib", | ||
51 | "/usr/lib/x86_64-linux-gnu", | ||
52 | LIBDIR, | ||
53 | "/usr/local/lib", | ||
54 | NULL | ||
55 | }; // Note: this array is duplicated in src/firejail/fs_lib.c | ||
56 | |||
57 | |||
58 | typedef struct storage_t { | ||
59 | struct storage_t *next; | ||
60 | const char *name; | ||
61 | } Storage; | ||
62 | static Storage *head; | ||
63 | |||
64 | // return 1 if found | ||
65 | static int storage_find(const char *name) { | ||
66 | Storage *ptr = head; | ||
67 | |||
68 | while (ptr) { | ||
69 | if (strcmp(ptr->name, name) == 0) | ||
70 | return 1; | ||
71 | ptr = ptr->next; | ||
72 | } | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static void storage_add(const char *name) { | ||
78 | if (storage_find(name)) | ||
79 | return; | ||
80 | |||
81 | Storage *s = malloc(sizeof(Storage)); | ||
82 | if (!s) | ||
83 | errExit("malloc"); | ||
84 | s->next = head; | ||
85 | head = s; | ||
86 | s->name = strdup(name); | ||
87 | if (!s->name) | ||
88 | errExit("strdup"); | ||
89 | } | ||
90 | |||
91 | |||
92 | static void storage_print(int fd) { | ||
93 | Storage *ptr = head; | ||
94 | |||
95 | while (ptr) { | ||
96 | dprintf(fd, "%s\n", ptr->name); | ||
97 | ptr = ptr->next; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | |||
102 | |||
103 | static void copy_libs_for_exe(const char *exe) { | ||
104 | int f; | ||
105 | f = open(exe, O_RDONLY); | ||
106 | if (f < 0) { | ||
107 | if (!arg_quiet) | ||
108 | fprintf(stderr, "Warning fldd: cannot open %s\n", exe); | ||
109 | return; | ||
110 | } | ||
111 | |||
112 | struct stat s; | ||
113 | char *base = NULL; | ||
114 | if (fstat(f, &s) == -1) | ||
115 | goto error_close; | ||
116 | base = mmap(0, s.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, f, 0); | ||
117 | if (base == MAP_FAILED) | ||
118 | goto error_close; | ||
119 | |||
120 | |||
121 | Elf_Ehdr *ebuf = (Elf_Ehdr *)base; | ||
122 | if (strncmp((const char *)ebuf->e_ident, ELFMAG, SELFMAG) != 0) { | ||
123 | if (!arg_quiet) | ||
124 | fprintf(stderr, "Warning fldd: %s is not an ELF executable or library\n", exe); | ||
125 | goto close; | ||
126 | } | ||
127 | |||
128 | Elf_Phdr *pbuf = (Elf_Phdr *)(base + sizeof(*ebuf)); | ||
129 | while (ebuf->e_phnum-- > 0) { | ||
130 | switch (pbuf->p_type) { | ||
131 | case PT_INTERP: | ||
132 | // dynamic loader ld-linux.so | ||
133 | storage_add(base + pbuf->p_offset); | ||
134 | break; | ||
135 | } | ||
136 | pbuf++; | ||
137 | } | ||
138 | |||
139 | Elf_Shdr *sbuf = (Elf_Shdr *)(base + ebuf->e_shoff); | ||
140 | |||
141 | // Find strings section | ||
142 | char *strbase = NULL; | ||
143 | int sections = ebuf->e_shnum; | ||
144 | while (sections-- > 0) { | ||
145 | if (sbuf->sh_type == SHT_STRTAB) { | ||
146 | strbase = base + sbuf->sh_offset; | ||
147 | break; | ||
148 | } | ||
149 | sbuf++; | ||
150 | } | ||
151 | if (strbase == NULL) | ||
152 | goto error_close; | ||
153 | |||
154 | // Find dynamic section | ||
155 | sections = ebuf->e_shnum; | ||
156 | while (sections-- > 0) { | ||
157 | // TODO: running fldd on large gui programs (fldd /usr/bin/transmission-qt) | ||
158 | // crash on accessing memory location sbuf->sh_type if sbuf->sh_type in the previous section was 0 (SHT_NULL) | ||
159 | // for now we just exit the while loop - this is probably incorrect | ||
160 | // printf("sbuf %p #%s#, sections %d, type %u\n", sbuf, exe, sections, sbuf->sh_type); | ||
161 | if (sbuf->sh_type == SHT_NULL) | ||
162 | break; | ||
163 | if (sbuf->sh_type == SHT_DYNAMIC) { | ||
164 | // Find DT_NEEDED tags | ||
165 | Elf_Dyn *dbuf = (Elf_Dyn *)(base + sbuf->sh_offset); | ||
166 | while (sbuf->sh_size >= sizeof(*dbuf)) { | ||
167 | if (dbuf->d_tag == DT_NEEDED) { | ||
168 | const char *lib = strbase + dbuf->d_un.d_ptr; | ||
169 | copy_libs_for_lib(lib); | ||
170 | } | ||
171 | sbuf->sh_size -= sizeof(*dbuf); | ||
172 | dbuf++; | ||
173 | } | ||
174 | } | ||
175 | sbuf++; | ||
176 | } | ||
177 | goto close; | ||
178 | |||
179 | error_close: | ||
180 | perror("copy libs"); | ||
181 | close: | ||
182 | if (base) | ||
183 | munmap(base, s.st_size); | ||
184 | |||
185 | close(f); | ||
186 | } | ||
187 | |||
188 | static void copy_libs_for_lib(const char *lib) { | ||
189 | int i; | ||
190 | for (i = 0; lib_paths[i]; i++) { | ||
191 | char *fname; | ||
192 | if (asprintf(&fname, "%s/%s", lib_paths[i], lib) == -1) | ||
193 | errExit("asprintf"); | ||
194 | if (access(fname, R_OK) == 0) { | ||
195 | if (!storage_find(fname)) { | ||
196 | storage_add(fname); | ||
197 | // libs may need other libs | ||
198 | copy_libs_for_exe(fname); | ||
199 | } | ||
200 | free(fname); | ||
201 | return; | ||
202 | } | ||
203 | free(fname); | ||
204 | } | ||
205 | errExit("library not found"); | ||
206 | } | ||
207 | |||
208 | static void usage(void) { | ||
209 | printf("Usage: fldd program [file]\n"); | ||
210 | printf("print a list of libraries used by program or store it in the file.\n"); | ||
211 | } | ||
212 | |||
213 | int main(int argc, char **argv) { | ||
214 | #if 0 | ||
215 | { | ||
216 | //system("cat /proc/self/status"); | ||
217 | int i; | ||
218 | for (i = 0; i < argc; i++) | ||
219 | printf("*%s* ", argv[i]); | ||
220 | printf("\n"); | ||
221 | } | ||
222 | #endif | ||
223 | if (argc < 2) { | ||
224 | fprintf(stderr, "Error fldd: invalid arguments\n"); | ||
225 | usage(); | ||
226 | exit(1); | ||
227 | } | ||
228 | |||
229 | |||
230 | // check program access | ||
231 | if (access(argv[1], R_OK)) { | ||
232 | fprintf(stderr, "Error fldd: cannot access %s\n", argv[1]); | ||
233 | exit(1); | ||
234 | } | ||
235 | |||
236 | char *quiet = getenv("FIREJAIL_QUIET"); | ||
237 | if (quiet && strcmp(quiet, "yes") == 0) | ||
238 | arg_quiet = 1; | ||
239 | |||
240 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { | ||
241 | usage(); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | int fd = STDOUT_FILENO; | ||
246 | // attempt to open the file | ||
247 | if (argc == 3) { | ||
248 | fd = open(argv[2], O_CREAT | O_TRUNC | O_WRONLY, 0644); | ||
249 | if (!fd) { | ||
250 | fprintf(stderr, "Error fldd: invalid arguments\n"); | ||
251 | usage(); | ||
252 | exit(1); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | copy_libs_for_exe(argv[1]); | ||
257 | storage_print(fd); | ||
258 | if (argc == 3) | ||
259 | close(fd); | ||
260 | |||
261 | return 0; | ||
262 | } | ||