diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/fs_lib.c | 355 | ||||
-rw-r--r-- | src/firejail/fs_lib2.c | 311 |
2 files changed, 499 insertions, 167 deletions
diff --git a/src/firejail/fs_lib.c b/src/firejail/fs_lib.c index abd7cee1a..6826f906c 100644 --- a/src/firejail/fs_lib.c +++ b/src/firejail/fs_lib.c | |||
@@ -16,7 +16,7 @@ | |||
16 | * You should have received a copy of the GNU General Public License along | 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., | 17 | * with this program; if not, write to the Free Software Foundation, Inc., |
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/mount.h> | 21 | #include <sys/mount.h> |
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
@@ -35,39 +35,79 @@ static const char * const lib_paths[] = { | |||
35 | LIBDIR, | 35 | LIBDIR, |
36 | "/usr/local/lib", | 36 | "/usr/local/lib", |
37 | NULL | 37 | NULL |
38 | }; // Note: this array is duplicated in src/fldd/main.c | 38 | }; // Note: this array is duplicated in src/fldd/main.c |
39 | 39 | ||
40 | static void duplicate(const char *fname, const char *private_run_dir) { | 40 | extern void fslib_install_stdc(void); |
41 | if (arg_debug) | 41 | extern void fslib_install_system(void); |
42 | printf("copying %s to private %s\n", fname, private_run_dir); | 42 | |
43 | static int lib_cnt = 0; | ||
44 | static int dir_cnt = 0; | ||
45 | |||
46 | |||
47 | static char *build_dest_dir(const char *full_path) { | ||
48 | assert(full_path); | ||
49 | if (strstr(full_path, "/x86_64-linux-gnu/")) | ||
50 | return RUN_LIB_DIR "/x86_64-linux-gnu"; | ||
51 | return RUN_LIB_DIR; | ||
52 | } | ||
43 | 53 | ||
44 | // copy only root-owned files | 54 | // copy fname in private_run_dir |
55 | void fslib_duplicate(const char *full_path) { | ||
56 | assert(full_path); | ||
45 | struct stat s; | 57 | struct stat s; |
46 | if (stat(fname, &s) == 0 && s.st_uid == 0) | 58 | if (stat(full_path, &s) != 0 || s.st_uid != 0 || access(full_path, R_OK)) |
47 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", fname, private_run_dir); | 59 | return; |
60 | |||
61 | char *dest_dir = build_dest_dir(full_path); | ||
62 | |||
63 | // don't copy it if the file is already there | ||
64 | char *ptr = strrchr(full_path, '/'); | ||
65 | if (!ptr) | ||
66 | return; | ||
67 | ptr++; | ||
68 | if (*ptr == '\0') | ||
69 | return; | ||
70 | |||
71 | char *name; | ||
72 | if (asprintf(&name, "%s/%s", dest_dir, ptr) == -1) | ||
73 | errExit("asprintf"); | ||
74 | if (stat(name, &s) == 0) { | ||
75 | free(name); | ||
76 | return; | ||
77 | } | ||
78 | free(name); | ||
79 | |||
80 | if (arg_debug) | ||
81 | printf("copying %s to private %s\n", full_path, dest_dir); | ||
82 | |||
83 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", full_path, dest_dir); | ||
84 | lib_cnt++; | ||
48 | } | 85 | } |
49 | 86 | ||
50 | 87 | ||
51 | // requires full path for lib | 88 | // requires full path for lib |
52 | static void copy_libs(const char *lib, const char *private_run_dir, const char *output_file) { | 89 | // it could be a library or an executable |
90 | // lib is not copied, only libraries used by it | ||
91 | void fslib_copy_libs(const char *full_path) { | ||
53 | // if library/executable does not exist or the user does not have read access to it | 92 | // if library/executable does not exist or the user does not have read access to it |
54 | // print a warning and exit the function. | 93 | // print a warning and exit the function. |
55 | if (access(lib, R_OK)) { | 94 | if (access(full_path, R_OK)) { |
56 | fwarning("cannot find %s for private-lib, skipping...\n", lib); | 95 | if (arg_debug) |
96 | printf("cannot find %s for private-lib, skipping...\n", full_path); | ||
57 | return; | 97 | return; |
58 | } | 98 | } |
59 | 99 | ||
60 | // create an empty RUN_LIB_FILE and allow the user to write to it | 100 | // create an empty RUN_LIB_FILE and allow the user to write to it |
61 | unlink(output_file); // in case is there | 101 | unlink(RUN_LIB_FILE); // in case is there |
62 | create_empty_file_as_root(output_file, 0644); | 102 | create_empty_file_as_root(RUN_LIB_FILE, 0644); |
63 | if (chown(output_file, getuid(), getgid())) | 103 | if (chown(RUN_LIB_FILE, getuid(), getgid())) |
64 | errExit("chown"); | 104 | errExit("chown"); |
65 | 105 | ||
66 | // run fldd to extact the list of file | 106 | // run fldd to extact the list of files |
67 | sbox_run(SBOX_USER | SBOX_SECCOMP | SBOX_CAPS_NONE, 3, PATH_FLDD, lib, output_file); | 107 | sbox_run(SBOX_USER | SBOX_SECCOMP | SBOX_CAPS_NONE, 3, PATH_FLDD, full_path, RUN_LIB_FILE); |
68 | 108 | ||
69 | // open the list of libraries and install them on by one | 109 | // open the list of libraries and install them on by one |
70 | FILE *fp = fopen(output_file, "r"); | 110 | FILE *fp = fopen(RUN_LIB_FILE, "r"); |
71 | if (!fp) | 111 | if (!fp) |
72 | errExit("fopen"); | 112 | errExit("fopen"); |
73 | 113 | ||
@@ -77,18 +117,30 @@ static void copy_libs(const char *lib, const char *private_run_dir, const char * | |||
77 | char *ptr = strchr(buf, '\n'); | 117 | char *ptr = strchr(buf, '\n'); |
78 | if (ptr) | 118 | if (ptr) |
79 | *ptr = '\0'; | 119 | *ptr = '\0'; |
80 | duplicate(buf, private_run_dir); | 120 | fslib_duplicate(buf); |
81 | } | 121 | } |
82 | fclose(fp); | 122 | fclose(fp); |
83 | } | 123 | } |
84 | 124 | ||
85 | static void copy_directory(const char *full_path, const char *dir_name, const char *private_run_dir) { | ||
86 | char *dest; | ||
87 | if (asprintf(&dest, "%s/%s", private_run_dir, dir_name) == -1) | ||
88 | errExit("asprintf"); | ||
89 | 125 | ||
90 | // do nothing if the directory is already there | 126 | void fslib_copy_dir(const char *full_path) { |
127 | assert(full_path); | ||
128 | // do nothing if the directory does not exist or is not owned by root | ||
91 | struct stat s; | 129 | struct stat s; |
130 | if (stat(full_path, &s) != 0 || s.st_uid != 0 || !S_ISDIR(s.st_mode) || access(full_path, R_OK)) | ||
131 | return; | ||
132 | |||
133 | char *dir_name = strrchr(full_path, '/'); | ||
134 | assert(dir_name); | ||
135 | dir_name++; | ||
136 | assert(*dir_name != '\0'); | ||
137 | |||
138 | |||
139 | |||
140 | // do nothing if the directory is already there | ||
141 | char *dest; | ||
142 | if (asprintf(&dest, "%s/%s", build_dest_dir(full_path), dir_name) == -1) | ||
143 | errExit("asprintf"); | ||
92 | if (stat(dest, &s) == 0) { | 144 | if (stat(dest, &s) == 0) { |
93 | free(dest); | 145 | free(dest); |
94 | return; | 146 | return; |
@@ -98,120 +150,84 @@ static void copy_directory(const char *full_path, const char *dir_name, const ch | |||
98 | mkdir_attr(dest, 0755, 0, 0); | 150 | mkdir_attr(dest, 0755, 0, 0); |
99 | 151 | ||
100 | if (mount(full_path, dest, NULL, MS_BIND|MS_REC, NULL) < 0 || | 152 | if (mount(full_path, dest, NULL, MS_BIND|MS_REC, NULL) < 0 || |
101 | mount(NULL, dest, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | 153 | mount(NULL, dest, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) |
102 | errExit("mount bind"); | 154 | errExit("mount bind"); |
103 | fs_logger2("clone", full_path); | 155 | fs_logger2("clone", full_path); |
104 | fs_logger2("mount", full_path); | 156 | fs_logger2("mount", full_path); |
157 | dir_cnt++; | ||
105 | free(dest); | 158 | free(dest); |
106 | } | 159 | } |
107 | 160 | ||
161 | |||
108 | // return 1 if the file is valid | 162 | // return 1 if the file is valid |
109 | static char *valid_file(const char *lib) { | 163 | static char *valid_file(const char *lib) { |
110 | // filename check | 164 | // filename check |
111 | int len = strlen(lib); | 165 | int len = strlen(lib); |
112 | if (strcspn(lib, "\\&!?\"'<>%^(){}[];,*") != (size_t)len || | 166 | if (strcspn(lib, "\\&!?\"'<>%^(){}[];,*") != (size_t)len || |
113 | strstr(lib, "..")) { | 167 | strstr(lib, "..")) { |
114 | fprintf(stderr, "Error: \"%s\" is an invalid library\n", lib); | 168 | fprintf(stderr, "Error: \"%s\" is an invalid library\n", lib); |
115 | exit(1); | 169 | exit(1); |
116 | } | 170 | } |
117 | 171 | ||
118 | // find the library | 172 | // find the library |
119 | int i; | 173 | int i; |
120 | for (i = 0; lib_paths[i]; i++) { | 174 | for (i = 0; lib_paths[i]; i++) { |
121 | char *fname; | 175 | char *fname; |
122 | if (asprintf(&fname, "%s/%s", lib_paths[i], lib) == -1) | 176 | if (asprintf(&fname, "%s/%s", lib_paths[i], lib) == -1) |
123 | errExit("asprintf"); | 177 | errExit("asprintf"); |
124 | 178 | ||
125 | // existing file owned by root | 179 | // existing file owned by root, read access |
126 | struct stat s; | 180 | struct stat s; |
127 | if (stat(fname, &s) == 0 && s.st_uid == 0) { | 181 | if (stat(fname, &s) == 0 && s.st_uid == 0 && !access(fname, R_OK)) { |
128 | return fname; | 182 | return fname; |
129 | } | 183 | } |
130 | free(fname); | 184 | free(fname); |
131 | } | 185 | } |
132 | 186 | ||
133 | fwarning("%s library not found, skipping...\n", lib); | 187 | fwarning("%s library not found, skipping...\n", lib); |
134 | return NULL; | 188 | return NULL; |
135 | } | 189 | } |
136 | 190 | ||
137 | // standard libc libraries based on Debian's libc6 package | 191 | |
138 | // selinux seems to be linked in most command line utilities | 192 | static void mount_directories(void) { |
139 | // locale (/usr/lib/locale) - without it, the program will default to "C" locale | 193 | if (arg_debug) |
140 | typedef struct liblist_t { | 194 | printf("Mount-bind %s on top of /lib /lib64 /usr/lib\n", RUN_LIB_DIR); |
141 | const char *name; | 195 | |
142 | int len; | 196 | if (is_dir("/lib")) { |
143 | } LibList; | 197 | if (mount(RUN_LIB_DIR, "/lib", NULL, MS_BIND|MS_REC, NULL) < 0 || |
144 | 198 | mount(NULL, "/lib", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | |
145 | static LibList libc_list[] = { | 199 | errExit("mount bind"); |
146 | // { "locale", 0 }, hardcoded! | 200 | fs_logger2("tmpfs", "/lib"); |
147 | { "libselinux.so.", 0 }, | 201 | fs_logger("mount /lib"); |
148 | { "ld-linux-x86-64.so.", 0 }, | ||
149 | { "libanl.so.", 0 }, | ||
150 | { "libc.so.", 0 }, | ||
151 | { "libcidn.so.", 0 }, | ||
152 | { "libcrypt.so.", 0 }, | ||
153 | { "libdl.so.", 0 }, | ||
154 | { "libm.so.", 0 }, | ||
155 | { "libmemusage.so", 0 }, | ||
156 | { "libmvec.so.", 0 }, | ||
157 | { "libnsl.so.", 0 }, | ||
158 | { "libnss_compat.so.", 0 }, | ||
159 | { "libnss_dns.so.", 0 }, | ||
160 | { "libnss_files.so.", 0 }, | ||
161 | { "libnss_hesiod.so.", 0 }, | ||
162 | { "libnss_nisplus.so.", 0 }, | ||
163 | { "libnss_nis.so.", 0 }, | ||
164 | { "libpthread.so.", 0 }, | ||
165 | { "libresolv.so.", 0 }, | ||
166 | { "librt.so.", 0 }, | ||
167 | { "libthread_db.so.", 0 }, | ||
168 | { "libutil.so.", 0 }, | ||
169 | { NULL, 0} | ||
170 | }; | ||
171 | |||
172 | static int find(const char *name) { | ||
173 | assert(name); | ||
174 | |||
175 | int i = 0; | ||
176 | while (libc_list[i].name) { | ||
177 | if (libc_list[i].len == 0) | ||
178 | libc_list[i].len = strlen(libc_list[i].name); | ||
179 | if (strncmp(name, libc_list[i].name, libc_list[i].len) == 0) | ||
180 | return 1; | ||
181 | i++; | ||
182 | } | 202 | } |
183 | return 0; | ||
184 | } | ||
185 | 203 | ||
186 | // compare the files in dirname against liblist above | 204 | if (is_dir("/lib64")) { |
187 | static void walk_directory(const char *dirname, const char *destdir) { | 205 | if (mount(RUN_LIB_DIR, "/lib64", NULL, MS_BIND|MS_REC, NULL) < 0 || |
188 | assert(dirname); | 206 | mount(NULL, "/lib64", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) |
189 | assert(destdir); | 207 | errExit("mount bind"); |
190 | 208 | fs_logger2("tmpfs", "/lib64"); | |
191 | DIR *dir = opendir(dirname); | 209 | fs_logger("mount /lib64"); |
192 | if (dir) { | 210 | } |
193 | struct dirent *entry; | 211 | |
194 | while ((entry = readdir(dir)) != NULL) { | 212 | if (is_dir("/usr/lib")) { |
195 | if (strcmp(entry->d_name, ".") == 0) | 213 | if (mount(RUN_LIB_DIR, "/usr/lib", NULL, MS_BIND|MS_REC, NULL) < 0 || |
196 | continue; | 214 | mount(NULL, "/usr/lib", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) |
197 | if (strcmp(entry->d_name, "..") == 0) | 215 | errExit("mount bind"); |
198 | continue; | 216 | fs_logger2("tmpfs", "/usr/lib"); |
199 | 217 | fs_logger("mount /usr/lib"); | |
200 | if (find(entry->d_name)) { | 218 | } |
201 | char *fname; | 219 | |
202 | if (asprintf(&fname, "%s/%s", dirname, entry->d_name) == -1) | 220 | // for amd64 only - we'll deal with i386 later |
203 | errExit("asprintf"); | 221 | if (is_dir("/lib32")) { |
204 | 222 | if (mount(RUN_RO_DIR, "/lib32", "none", MS_BIND, "mode=400,gid=0") < 0) | |
205 | if (is_dir(fname)) | 223 | errExit("disable file"); |
206 | copy_directory(fname, entry->d_name, RUN_LIB_DIR); | 224 | fs_logger("blacklist-nolog /lib32"); |
207 | else | 225 | } |
208 | duplicate(fname, destdir); | 226 | if (is_dir("/libx32")) { |
209 | } | 227 | if (mount(RUN_RO_DIR, "/libx32", "none", MS_BIND, "mode=400,gid=0") < 0) |
210 | } | 228 | errExit("disable file"); |
211 | closedir(dir); | 229 | fs_logger("blacklist-nolog /libx32"); |
212 | } | 230 | } |
213 | else | ||
214 | fprintf(stderr, "Error: cannot open %s in order to set --private-lib\n", dirname); | ||
215 | } | 231 | } |
216 | 232 | ||
217 | void fs_private_lib(void) { | 233 | void fs_private_lib(void) { |
@@ -219,39 +235,35 @@ void fs_private_lib(void) { | |||
219 | fwarning("private-lib feature is currently available only on amd64 platforms\n"); | 235 | fwarning("private-lib feature is currently available only on amd64 platforms\n"); |
220 | return; | 236 | return; |
221 | #endif | 237 | #endif |
222 | |||
223 | char *private_list = cfg.lib_private_keep; | 238 | char *private_list = cfg.lib_private_keep; |
224 | if (arg_debug) | 239 | if (arg_debug) |
225 | printf("Starting private-lib processing: program %s, shell %s\n", | 240 | printf("Starting private-lib processing: program %s, shell %s\n", |
226 | (cfg.original_program_index > 0)? cfg.original_argv[cfg.original_program_index]: "none", | 241 | (cfg.original_program_index > 0)? cfg.original_argv[cfg.original_program_index]: "none", |
227 | (arg_shell_none)? "none": cfg.shell); | 242 | (arg_shell_none)? "none": cfg.shell); |
228 | 243 | ||
229 | // create /run/firejail/mnt/lib directory | 244 | // create /run/firejail/mnt/lib directory |
230 | mkdir_attr(RUN_LIB_DIR, 0755, 0, 0); | 245 | mkdir_attr(RUN_LIB_DIR, 0755, 0, 0); |
231 | 246 | ||
232 | struct stat s; | 247 | // install standard C libraries |
233 | if (stat("/lib/x86_64-linux-gnu", &s) == 0) { | 248 | fslib_install_stdc(); |
234 | mkdir_attr(RUN_LIB_DIR "/x86_64-linux-gnu", 0755, 0, 0); | 249 | |
235 | walk_directory("/lib/x86_64-linux-gnu", RUN_LIB_DIR "/x86_64-linux-gnu"); | 250 | timetrace_start(); |
236 | } | ||
237 | if (stat("/usr/lib/locale", &s) == 0) | ||
238 | copy_directory("/usr/lib/locale", "locale", RUN_LIB_DIR); | ||
239 | 251 | ||
240 | // copy the libs in the new lib directory for the main exe | 252 | // copy the libs in the new lib directory for the main exe |
241 | if (cfg.original_program_index > 0) | 253 | if (cfg.original_program_index > 0) |
242 | copy_libs(cfg.original_argv[cfg.original_program_index], RUN_LIB_DIR, RUN_LIB_FILE); | 254 | fslib_copy_libs(cfg.original_argv[cfg.original_program_index]); |
243 | 255 | ||
244 | // for the shell | 256 | // for the shell |
245 | if (!arg_shell_none) { | 257 | if (!arg_shell_none) { |
246 | copy_libs(cfg.shell, RUN_LIB_DIR, RUN_LIB_FILE); | 258 | fslib_copy_libs(cfg.shell); |
247 | // a shell is useless without ls command | 259 | // a shell is useless without ls command |
248 | copy_libs("/bin/ls", RUN_LIB_DIR, RUN_LIB_FILE); | 260 | fslib_copy_libs("/bin/ls"); |
249 | } | 261 | } |
250 | 262 | ||
251 | // for the listed libs | 263 | // for the listed libs |
252 | if (private_list && *private_list != '\0') { | 264 | if (private_list && *private_list != '\0') { |
253 | if (arg_debug) | 265 | if (arg_debug) |
254 | printf("Copying extra files (%s) in the new lib directory:\n", private_list); | 266 | printf("Copying extra files (%s) in the new lib directory\n", private_list); |
255 | 267 | ||
256 | char *dlist = strdup(private_list); | 268 | char *dlist = strdup(private_list); |
257 | if (!dlist) | 269 | if (!dlist) |
@@ -261,10 +273,10 @@ void fs_private_lib(void) { | |||
261 | char *lib = valid_file(ptr); | 273 | char *lib = valid_file(ptr); |
262 | if (lib) { | 274 | if (lib) { |
263 | if (is_dir(lib)) | 275 | if (is_dir(lib)) |
264 | copy_directory(lib, ptr, RUN_LIB_DIR); | 276 | fslib_copy_dir(lib); |
265 | else { | 277 | else { |
266 | duplicate(lib, RUN_LIB_DIR); | 278 | fslib_duplicate(lib); |
267 | copy_libs(lib, RUN_LIB_DIR, RUN_LIB_FILE); | 279 | fslib_copy_libs(lib); |
268 | } | 280 | } |
269 | free(lib); | 281 | free(lib); |
270 | } | 282 | } |
@@ -273,10 +285,10 @@ void fs_private_lib(void) { | |||
273 | lib = valid_file(ptr); | 285 | lib = valid_file(ptr); |
274 | if (lib) { | 286 | if (lib) { |
275 | if (is_dir(lib)) | 287 | if (is_dir(lib)) |
276 | copy_directory(lib, ptr, RUN_LIB_DIR); | 288 | fslib_copy_dir(lib); |
277 | else { | 289 | else { |
278 | duplicate(lib, RUN_LIB_DIR); | 290 | fslib_duplicate(lib); |
279 | copy_libs(lib, RUN_LIB_DIR, RUN_LIB_FILE); | 291 | fslib_copy_libs(lib); |
280 | } | 292 | } |
281 | free(lib); | 293 | free(lib); |
282 | } | 294 | } |
@@ -295,54 +307,63 @@ void fs_private_lib(void) { | |||
295 | char *ptr = strchr(buf, '\n'); | 307 | char *ptr = strchr(buf, '\n'); |
296 | if (ptr) | 308 | if (ptr) |
297 | *ptr = '\0'; | 309 | *ptr = '\0'; |
298 | copy_libs(buf, RUN_LIB_DIR, RUN_LIB_FILE); | 310 | |
311 | // copy libraries for this program | ||
312 | fslib_copy_libs(buf); | ||
313 | |||
314 | // load program data from /usr/lib/program or from /usr/lib/x86_64-linux-gnu | ||
315 | ptr = strrchr(buf, '/'); | ||
316 | if (ptr && *(ptr + 1) != '\0') { | ||
317 | ptr++; | ||
318 | |||
319 | // /usr/lib/program | ||
320 | char *name; | ||
321 | if (asprintf(&name, "/usr/lib/%s", ptr) == -1) | ||
322 | errExit("asprintf"); | ||
323 | struct stat s; | ||
324 | if (is_dir(name)) { | ||
325 | fslib_copy_dir(name); | ||
326 | fslib_copy_libs(name); | ||
327 | } | ||
328 | free(name); | ||
329 | |||
330 | // /usr/lib/x86_linux-gnu - debian & frriends | ||
331 | if (asprintf(&name, "/usr/lib/x86_64-linux-gnu/%s", ptr) == -1) | ||
332 | errExit("asprintf"); | ||
333 | if (is_dir(name)) { | ||
334 | fslib_copy_dir(name); | ||
335 | fslib_copy_libs(name); | ||
336 | } | ||
337 | free(name); | ||
338 | |||
339 | // /usr/lib64 - CentOS, Fedora | ||
340 | if (asprintf(&name, "/usr/lib64/%s", ptr) == -1) | ||
341 | errExit("asprintf"); | ||
342 | if (is_dir(name)) { | ||
343 | fslib_copy_dir(name); | ||
344 | fslib_copy_libs(name); | ||
345 | } | ||
346 | free(name); | ||
347 | } | ||
299 | } | 348 | } |
300 | } | 349 | } |
301 | fclose(fp); | 350 | fclose(fp); |
302 | } | 351 | } |
352 | if (!arg_quiet) | ||
353 | fprintf(stderr, "Program libraries installed in %0.2f ms\n", timetrace_end()); | ||
354 | |||
355 | // install the reset of the system libraries | ||
356 | fslib_install_system(); | ||
357 | |||
358 | if (!arg_quiet) | ||
359 | fprintf(stderr, "Installed %d libraries and %d directories\n", lib_cnt, dir_cnt); | ||
303 | 360 | ||
304 | // for our trace and tracelog libs | 361 | // for our trace and tracelog libs |
305 | if (arg_trace) | 362 | if (arg_trace) |
306 | duplicate(LIBDIR "/firejail/libtrace.so", RUN_LIB_DIR); | 363 | fslib_duplicate(LIBDIR "/firejail/libtrace.so"); |
307 | else if (arg_tracelog) | 364 | else if (arg_tracelog) |
308 | duplicate(LIBDIR "/firejail/libtracelog.so", RUN_LIB_DIR); | 365 | fslib_duplicate(LIBDIR "/firejail/libtracelog.so"); |
309 | 366 | ||
310 | if (arg_debug) | 367 | // mount lib filesystem |
311 | printf("Mount-bind %s on top of /lib /lib64 /usr/lib\n", RUN_LIB_DIR); | 368 | mount_directories(); |
312 | |||
313 | if (is_dir("/lib")) { | ||
314 | if (mount(RUN_LIB_DIR, "/lib", NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
315 | mount(NULL, "/lib", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
316 | errExit("mount bind"); | ||
317 | fs_logger2("tmpfs", "/lib"); | ||
318 | fs_logger("mount /lib"); | ||
319 | } | ||
320 | |||
321 | if (is_dir("/lib64")) { | ||
322 | if (mount(RUN_LIB_DIR, "/lib64", NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
323 | mount(NULL, "/lib64", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
324 | errExit("mount bind"); | ||
325 | fs_logger2("tmpfs", "/lib64"); | ||
326 | fs_logger("mount /lib64"); | ||
327 | } | ||
328 | |||
329 | if (is_dir("/usr/lib")) { | ||
330 | if (mount(RUN_LIB_DIR, "/usr/lib", NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
331 | mount(NULL, "/usr/lib", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
332 | errExit("mount bind"); | ||
333 | fs_logger2("tmpfs", "/usr/lib"); | ||
334 | fs_logger("mount /usr/lib"); | ||
335 | } | ||
336 | |||
337 | // for amd64 only - we'll deal with i386 later | ||
338 | if (is_dir("/lib32")) { | ||
339 | if (mount(RUN_RO_DIR, "/lib32", "none", MS_BIND, "mode=400,gid=0") < 0) | ||
340 | errExit("disable file"); | ||
341 | fs_logger("blacklist-nolog /lib32"); | ||
342 | } | ||
343 | if (is_dir("/libx32")) { | ||
344 | if (mount(RUN_RO_DIR, "/libx32", "none", MS_BIND, "mode=400,gid=0") < 0) | ||
345 | errExit("disable file"); | ||
346 | fs_logger("blacklist-nolog /libx32"); | ||
347 | } | ||
348 | } | 369 | } |
diff --git a/src/firejail/fs_lib2.c b/src/firejail/fs_lib2.c new file mode 100644 index 000000000..4c2c15ebd --- /dev/null +++ b/src/firejail/fs_lib2.c | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 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 | #include "firejail.h" | ||
21 | #include <dirent.h> | ||
22 | #include <sys/stat.h> | ||
23 | |||
24 | extern void fslib_duplicate(const char *full_path); | ||
25 | extern void fslib_copy_libs(const char *full_path); | ||
26 | extern void fslib_copy_dir(const char *full_path); | ||
27 | |||
28 | //*************************************************************** | ||
29 | // Standard C library | ||
30 | //*************************************************************** | ||
31 | // standard libc libraries based on Debian's libc6 package | ||
32 | // selinux seems to be linked in most command line utilities | ||
33 | // locale (/usr/lib/locale) - without it, the program will default to "C" locale | ||
34 | typedef struct liblist_t { | ||
35 | const char *name; | ||
36 | int len; | ||
37 | } LibList; | ||
38 | |||
39 | static LibList libc_list[] = { | ||
40 | { "libselinux.so.", 0 }, | ||
41 | { "ld-linux-x86-64.so.", 0 }, | ||
42 | { "libanl.so.", 0 }, | ||
43 | { "libc.so.", 0 }, | ||
44 | { "libcidn.so.", 0 }, | ||
45 | { "libcrypt.so.", 0 }, | ||
46 | { "libdl.so.", 0 }, | ||
47 | { "libm.so.", 0 }, | ||
48 | { "libmemusage.so", 0 }, | ||
49 | { "libmvec.so.", 0 }, | ||
50 | { "libnsl.so.", 0 }, | ||
51 | { "libnss_compat.so.", 0 }, | ||
52 | { "libnss_dns.so.", 0 }, | ||
53 | { "libnss_files.so.", 0 }, | ||
54 | { "libnss_hesiod.so.", 0 }, | ||
55 | { "libnss_nisplus.so.", 0 }, | ||
56 | { "libnss_nis.so.", 0 }, | ||
57 | { "libpthread.so.", 0 }, | ||
58 | { "libresolv.so.", 0 }, | ||
59 | { "librt.so.", 0 }, | ||
60 | { "libthread_db.so.", 0 }, | ||
61 | { "libutil.so.", 0 }, | ||
62 | { NULL, 0} | ||
63 | }; | ||
64 | |||
65 | static int find_libc_list(const char *name) { | ||
66 | assert(name); | ||
67 | |||
68 | int i = 0; | ||
69 | while (libc_list[i].name) { | ||
70 | if (libc_list[i].len == 0) | ||
71 | libc_list[i].len = strlen(libc_list[i].name); | ||
72 | if (strncmp(name, libc_list[i].name, libc_list[i].len) == 0) | ||
73 | return 1; | ||
74 | i++; | ||
75 | } | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | // compare the files in dirname against liblist above | ||
80 | static void stdc(const char *dirname) { | ||
81 | assert(dirname); | ||
82 | |||
83 | DIR *dir = opendir(dirname); | ||
84 | if (dir) { | ||
85 | struct dirent *entry; | ||
86 | while ((entry = readdir(dir)) != NULL) { | ||
87 | if (strcmp(entry->d_name, ".") == 0) | ||
88 | continue; | ||
89 | if (strcmp(entry->d_name, "..") == 0) | ||
90 | continue; | ||
91 | |||
92 | if (find_libc_list(entry->d_name)) { | ||
93 | char *fname; | ||
94 | if (asprintf(&fname, "%s/%s", dirname, entry->d_name) == -1) | ||
95 | errExit("asprintf"); | ||
96 | |||
97 | fslib_duplicate(fname); | ||
98 | } | ||
99 | } | ||
100 | closedir(dir); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | void fslib_install_stdc(void) { | ||
105 | // install standard C libraries | ||
106 | struct stat s; | ||
107 | char *stdclib = "/lib64"; // CentOS, Fedora, Arch | ||
108 | |||
109 | if (stat("/lib/x86_64-linux-gnu", &s) == 0) { // Debian & friends | ||
110 | mkdir_attr(RUN_LIB_DIR "/x86_64-linux-gnu", 0755, 0, 0); | ||
111 | stdclib = "/lib/x86_64-linux-gnu"; | ||
112 | } | ||
113 | |||
114 | timetrace_start(); | ||
115 | stdc(stdclib); | ||
116 | |||
117 | // install locale | ||
118 | if (stat("/usr/lib/locale", &s) == 0) | ||
119 | fslib_copy_dir("/usr/lib/locale"); | ||
120 | |||
121 | if (!arg_quiet) | ||
122 | fprintf(stderr, "Standard C library installed in %0.2f ms\n", timetrace_end()); | ||
123 | } | ||
124 | |||
125 | void fslib_install_locale(void); | ||
126 | |||
127 | |||
128 | //*************************************************************** | ||
129 | // various system libraries | ||
130 | //*************************************************************** | ||
131 | |||
132 | // look for library in the new filesystem, and install one or two more directories, dir1 and dir2 | ||
133 | typedef struct syslib_t { | ||
134 | const char *library; // look in the system for this library | ||
135 | int len; // length of library string, 0 by default | ||
136 | int found; // library found, 0 by default | ||
137 | const char *dir1; // directory to install | ||
138 | const char *dir2; // directory to install | ||
139 | const char *message; // message to print on the screen | ||
140 | } SysLib; | ||
141 | |||
142 | SysLib syslibs[] = { | ||
143 | #if 0 | ||
144 | { | ||
145 | "", // library | ||
146 | 0, 0, // len and found flag | ||
147 | "", // dir1 | ||
148 | "", // dir2 | ||
149 | "" // message | ||
150 | }, | ||
151 | #endif | ||
152 | { // pixmaps - libraries used by GTK to display application menu icons | ||
153 | "libgdk_pixbuf-2.0", // library | ||
154 | 0, 0, // len and found flag | ||
155 | "gdk-pixbuf-2.0", // dir1 | ||
156 | "", // dir2 | ||
157 | "GdkPixbuf" // message | ||
158 | }, | ||
159 | { // GTK2 | ||
160 | "libgtk-x11-2.0", // library | ||
161 | 0, 0, // len and found flag | ||
162 | "gtk-2.0", // dir1 | ||
163 | "libgtk2.0-0", // dir2 | ||
164 | "GTK2" // message | ||
165 | }, | ||
166 | { // GTK3 | ||
167 | "libgtk-3", // library | ||
168 | 0, 0, // len and found flag | ||
169 | "gtk-3.0", // dir1 | ||
170 | "libgtk-3-0", // dir2 | ||
171 | "GTK3" // message | ||
172 | }, | ||
173 | { // Pango - text internationalization, found on older GTK2-based systems | ||
174 | "libpango", // library | ||
175 | 0, 0, // len and found flag | ||
176 | "pango", // dir1 | ||
177 | "", // dir2 | ||
178 | "Pango" // message | ||
179 | }, | ||
180 | { // Library for handling GObject introspection data on GTK systems | ||
181 | "libgirepository-1.0", // library | ||
182 | 0, 0, // len and found flag | ||
183 | "girepository-1.0", // dir1 | ||
184 | "", // dir2 | ||
185 | "GIRepository" // message | ||
186 | }, | ||
187 | { // Enchant speller | ||
188 | "libenchant.so.", // library | ||
189 | 0, 0, // len and found flag | ||
190 | "enchant", // dir1 | ||
191 | "", // dir2 | ||
192 | "Enchant (speller)" // message | ||
193 | }, | ||
194 | { | ||
195 | "libQt5", // library | ||
196 | 0, 0, // len and found flag | ||
197 | "qt5", // dir1 | ||
198 | "gdk-pixbuf-2.0", // dir2 | ||
199 | "Qt5, GdkPixbuf" // message | ||
200 | }, | ||
201 | { | ||
202 | "libQtCore", // library | ||
203 | 0, 0, // len and found flag | ||
204 | "qt4", // dir1 | ||
205 | "gdk-pixbuf-2.0", // dir2 | ||
206 | "Qt4" // message | ||
207 | }, | ||
208 | |||
209 | { // NULL terminated list | ||
210 | NULL, // library | ||
211 | 0, 0, // len and found flag | ||
212 | "", // dir1 | ||
213 | "", // dir2 | ||
214 | "" // message | ||
215 | } | ||
216 | }; | ||
217 | |||
218 | void fslib_install_system(void) { | ||
219 | // look for installed libraries | ||
220 | DIR *dir = opendir(RUN_LIB_DIR "/x86_64-linux-gnu"); | ||
221 | if (!dir) | ||
222 | dir = opendir(RUN_LIB_DIR); | ||
223 | |||
224 | if (dir) { | ||
225 | struct dirent *entry; | ||
226 | while ((entry = readdir(dir)) != NULL) { | ||
227 | if (strcmp(entry->d_name, ".") == 0) | ||
228 | continue; | ||
229 | if (strcmp(entry->d_name, "..") == 0) | ||
230 | continue; | ||
231 | |||
232 | SysLib *ptr = &syslibs[0]; | ||
233 | while (ptr->library) { | ||
234 | if (ptr->len == 0) | ||
235 | ptr->len = strlen(ptr->library); | ||
236 | |||
237 | if (strncmp(entry->d_name, ptr->library, ptr->len) == 0) { | ||
238 | ptr->found = 1; | ||
239 | break; | ||
240 | } | ||
241 | |||
242 | ptr++; | ||
243 | } | ||
244 | |||
245 | } | ||
246 | closedir(dir); | ||
247 | } | ||
248 | else | ||
249 | assert(0); | ||
250 | |||
251 | // install required directories | ||
252 | SysLib *ptr = &syslibs[0]; | ||
253 | while (ptr->library) { | ||
254 | if (ptr->found) { | ||
255 | assert(*ptr->message != '\0'); | ||
256 | timetrace_start(); | ||
257 | |||
258 | // bring in all libraries | ||
259 | assert(ptr->dir1); | ||
260 | char *name; | ||
261 | // Debian & friends | ||
262 | if (asprintf(&name, "/usr/lib/x86_64-linux-gnu/%s", ptr->dir1) == -1) | ||
263 | errExit("asprintf"); | ||
264 | if (access(name, R_OK) == 0) { | ||
265 | fslib_copy_libs(name); | ||
266 | fslib_copy_dir(name); | ||
267 | } | ||
268 | else { | ||
269 | free(name); | ||
270 | // CentOS, Fedora, Arch | ||
271 | if (asprintf(&name, "/usr/lib64/%s", ptr->dir1) == -1) | ||
272 | errExit("asprintf"); | ||
273 | if (access(name, R_OK) == 0) { | ||
274 | fslib_copy_libs(name); | ||
275 | fslib_copy_dir(name); | ||
276 | } | ||
277 | } | ||
278 | free(name); | ||
279 | |||
280 | if (*ptr->dir2 != '\0') { | ||
281 | // Debian & friends | ||
282 | if (asprintf(&name, "/usr/lib/x86_64-linux-gnu/%s", ptr->dir2) == -1) | ||
283 | errExit("asprintf"); | ||
284 | if (access(name, R_OK) == 0) { | ||
285 | fslib_copy_libs(name); | ||
286 | fslib_copy_dir(name); | ||
287 | } | ||
288 | else { | ||
289 | free(name); | ||
290 | // CentOS, Fedora, Arch | ||
291 | if (asprintf(&name, "/usr/lib64/%s", ptr->dir2) == -1) | ||
292 | errExit("asprintf"); | ||
293 | if (access(name, R_OK) == 0) { | ||
294 | fslib_copy_libs(name); | ||
295 | fslib_copy_dir(name); | ||
296 | } | ||
297 | } | ||
298 | free(name); | ||
299 | } | ||
300 | |||
301 | if (!arg_quiet) | ||
302 | fprintf(stderr, "%s installed in %0.2f ms\n", ptr->message, timetrace_end()); | ||
303 | } | ||
304 | ptr++; | ||
305 | } | ||
306 | } | ||
307 | |||
308 | |||
309 | |||
310 | |||
311 | |||