diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firecfg/firecfg.config | 1 | ||||
-rw-r--r-- | src/firejail/firejail.h | 9 | ||||
-rw-r--r-- | src/firejail/fs_lib.c | 202 | ||||
-rw-r--r-- | src/firejail/main.c | 19 | ||||
-rw-r--r-- | src/firejail/preproc.c | 2 | ||||
-rw-r--r-- | src/firejail/profile.c | 25 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 15 | ||||
-rw-r--r-- | src/firejail/seccomp.c | 23 | ||||
-rw-r--r-- | src/fseccomp/fseccomp.h | 3 | ||||
-rw-r--r-- | src/fseccomp/main.c | 3 | ||||
-rw-r--r-- | src/fseccomp/seccomp.c | 54 | ||||
-rw-r--r-- | src/fseccomp/seccomp_file.c | 2 | ||||
-rw-r--r-- | src/man/firejail.txt | 34 |
13 files changed, 376 insertions, 16 deletions
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config index eb611034f..a6472a604 100644 --- a/src/firecfg/firecfg.config +++ b/src/firecfg/firecfg.config | |||
@@ -239,6 +239,7 @@ supertux2 | |||
239 | synfigstudio | 239 | synfigstudio |
240 | telegram | 240 | telegram |
241 | Telegram | 241 | Telegram |
242 | telegram-desktop | ||
242 | thunderbird | 243 | thunderbird |
243 | totem | 244 | totem |
244 | tracker | 245 | tracker |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index b90c8d8ed..1a26396a7 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -48,15 +48,18 @@ | |||
48 | #define RUN_SRV_DIR "/run/firejail/mnt/srv" | 48 | #define RUN_SRV_DIR "/run/firejail/mnt/srv" |
49 | #define RUN_BIN_DIR "/run/firejail/mnt/bin" | 49 | #define RUN_BIN_DIR "/run/firejail/mnt/bin" |
50 | #define RUN_PULSE_DIR "/run/firejail/mnt/pulse" | 50 | #define RUN_PULSE_DIR "/run/firejail/mnt/pulse" |
51 | #define RUN_LIB_DIR "/run/firejail/mnt/lib" | ||
51 | 52 | ||
52 | #define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" // protocol filter | 53 | #define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" // protocol filter |
53 | #define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" // configured filter | 54 | #define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" // configured filter |
54 | #define RUN_SECCOMP_AMD64 "/run/firejail/mnt/seccomp.amd64" // amd64 filter installed on i386 architectures | 55 | #define RUN_SECCOMP_AMD64 "/run/firejail/mnt/seccomp.amd64" // amd64 filter installed on i386 architectures |
55 | #define RUN_SECCOMP_I386 "/run/firejail/mnt/seccomp.i386" // i386 filter installed on amd64 architectures | 56 | #define RUN_SECCOMP_I386 "/run/firejail/mnt/seccomp.i386" // i386 filter installed on amd64 architectures |
57 | #define RUN_SECCOMP_MDWX "/run/firejail/mnt/seccomp.mdwx" // filter for memory-deny-write-execute | ||
56 | #define PATH_SECCOMP_DEFAULT (LIBDIR "/firejail/seccomp") // default filter built during make | 58 | #define PATH_SECCOMP_DEFAULT (LIBDIR "/firejail/seccomp") // default filter built during make |
57 | #define PATH_SECCOMP_DEFAULT_DEBUG (LIBDIR "/firejail/seccomp.debug") // default filter built during make | 59 | #define PATH_SECCOMP_DEFAULT_DEBUG (LIBDIR "/firejail/seccomp.debug") // default filter built during make |
58 | #define PATH_SECCOMP_AMD64 (LIBDIR "/firejail/seccomp.amd64") // amd64 filter built during make | 60 | #define PATH_SECCOMP_AMD64 (LIBDIR "/firejail/seccomp.amd64") // amd64 filter built during make |
59 | #define PATH_SECCOMP_I386 (LIBDIR "/firejail/seccomp.i386") // i386 filter built during make | 61 | #define PATH_SECCOMP_I386 (LIBDIR "/firejail/seccomp.i386") // i386 filter built during make |
62 | #define PATH_SECCOMP_MDWX (LIBDIR "/firejail/seccomp.mdwx") // filter for memory-deny-write-execute built during make | ||
60 | 63 | ||
61 | 64 | ||
62 | #define RUN_DEV_DIR "/run/firejail/mnt/dev" | 65 | #define RUN_DEV_DIR "/run/firejail/mnt/dev" |
@@ -207,6 +210,7 @@ typedef struct config_t { | |||
207 | char *opt_private_keep; // keep list for private opt directory | 210 | char *opt_private_keep; // keep list for private opt directory |
208 | char *srv_private_keep; // keep list for private srv directory | 211 | char *srv_private_keep; // keep list for private srv directory |
209 | char *bin_private_keep; // keep list for private bin directory | 212 | char *bin_private_keep; // keep list for private bin directory |
213 | char *lib_private_keep; // keep list for private bin directory | ||
210 | char *cwd; // current working directory | 214 | char *cwd; // current working directory |
211 | char *overlay_dir; | 215 | char *overlay_dir; |
212 | char *private_template; // template dir for tmpfs home | 216 | char *private_template; // template dir for tmpfs home |
@@ -328,6 +332,7 @@ extern int arg_private_opt; // private opt directory | |||
328 | extern int arg_private_srv; // private srv directory | 332 | extern int arg_private_srv; // private srv directory |
329 | extern int arg_private_bin; // private bin directory | 333 | extern int arg_private_bin; // private bin directory |
330 | extern int arg_private_tmp; // private tmp directory | 334 | extern int arg_private_tmp; // private tmp directory |
335 | extern int arg_private_lib; // private lib directory | ||
331 | extern int arg_scan; // arp-scan all interfaces | 336 | extern int arg_scan; // arp-scan all interfaces |
332 | extern int arg_whitelist; // whitelist commad | 337 | extern int arg_whitelist; // whitelist commad |
333 | extern int arg_nosound; // disable sound | 338 | extern int arg_nosound; // disable sound |
@@ -352,6 +357,7 @@ extern int arg_allusers; // all user home directories visible | |||
352 | extern int arg_machineid; // preserve /etc/machine-id | 357 | extern int arg_machineid; // preserve /etc/machine-id |
353 | extern int arg_disable_mnt; // disable /mnt and /media | 358 | extern int arg_disable_mnt; // disable /mnt and /media |
354 | extern int arg_noprofile; // use default.profile if none other found/specified | 359 | extern int arg_noprofile; // use default.profile if none other found/specified |
360 | extern int arg_memory_deny_write_execute; // block writable and executable memory | ||
355 | 361 | ||
356 | extern int login_shell; | 362 | extern int login_shell; |
357 | extern int parent_to_child_fds[2]; | 363 | extern int parent_to_child_fds[2]; |
@@ -625,6 +631,9 @@ void pulseaudio_disable(void); | |||
625 | // fs_bin.c | 631 | // fs_bin.c |
626 | void fs_private_bin_list(void); | 632 | void fs_private_bin_list(void); |
627 | 633 | ||
634 | // fs_lib.c | ||
635 | void fs_private_lib(void); | ||
636 | |||
628 | // protocol.c | 637 | // protocol.c |
629 | void protocol_filter_save(void); | 638 | void protocol_filter_save(void); |
630 | void protocol_filter_load(const char *fname); | 639 | void protocol_filter_load(const char *fname); |
diff --git a/src/firejail/fs_lib.c b/src/firejail/fs_lib.c new file mode 100644 index 000000000..cc60a330f --- /dev/null +++ b/src/firejail/fs_lib.c | |||
@@ -0,0 +1,202 @@ | |||
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 <elf.h> | ||
22 | #include <fcntl.h> | ||
23 | #include <sys/mman.h> | ||
24 | #include <sys/mount.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <sys/types.h> | ||
27 | #include <unistd.h> | ||
28 | |||
29 | #ifdef __LP64__ | ||
30 | #define Elf_Ehdr Elf64_Ehdr | ||
31 | #define Elf_Phdr Elf64_Phdr | ||
32 | #define Elf_Shdr Elf64_Shdr | ||
33 | #define Elf_Dyn Elf64_Dyn | ||
34 | #else | ||
35 | #define Elf_Ehdr Elf32_Ehdr | ||
36 | #define Elf_Phdr Elf32_Phdr | ||
37 | #define Elf_Shdr Elf32_Shdr | ||
38 | #define Elf_Dyn Elf32_Dyn | ||
39 | #endif | ||
40 | |||
41 | static const char * const lib_paths[] = { | ||
42 | "/lib", | ||
43 | "/lib/x86_64-linux-gnu", | ||
44 | "/lib64", | ||
45 | "/usr/lib", | ||
46 | "/usr/lib/x86_64-linux-gnu", | ||
47 | LIBDIR, | ||
48 | "/usr/local/lib", | ||
49 | NULL | ||
50 | }; | ||
51 | |||
52 | static void copy_libs_for_lib(const char *lib, const char *private_run_dir); | ||
53 | |||
54 | static void duplicate(const char *fname, const char *private_run_dir) { | ||
55 | if (arg_debug) | ||
56 | printf("copying %s to private %s\n", fname, private_run_dir); | ||
57 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", fname, private_run_dir); | ||
58 | } | ||
59 | |||
60 | static void copy_libs_for_exe(const char *exe, const char *private_run_dir) { | ||
61 | if (arg_debug) | ||
62 | printf("copy libs for %s\n", exe); | ||
63 | int f; | ||
64 | f = open(exe, O_RDONLY); | ||
65 | if (f < 0) | ||
66 | return; | ||
67 | struct stat s; | ||
68 | char *base = NULL; | ||
69 | if (fstat(f, &s) == -1) | ||
70 | goto error_close; | ||
71 | base = mmap(0, s.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, f, 0); | ||
72 | if (base == MAP_FAILED) | ||
73 | goto error_close; | ||
74 | |||
75 | Elf_Ehdr *ebuf = (Elf_Ehdr *)base; | ||
76 | if (strncmp((const char *)ebuf->e_ident, ELFMAG, SELFMAG) != 0) | ||
77 | goto close; | ||
78 | |||
79 | Elf_Phdr *pbuf = (Elf_Phdr *)(base + sizeof(*ebuf)); | ||
80 | while (ebuf->e_phnum-- > 0) { | ||
81 | switch (pbuf->p_type) { | ||
82 | case PT_INTERP: | ||
83 | // dynamic loader ld-linux.so | ||
84 | duplicate(base + pbuf->p_offset, private_run_dir); | ||
85 | break; | ||
86 | } | ||
87 | pbuf++; | ||
88 | } | ||
89 | |||
90 | Elf_Shdr *sbuf = (Elf_Shdr *)(base + ebuf->e_shoff); | ||
91 | |||
92 | // Find strings section | ||
93 | char *strbase = NULL; | ||
94 | int sections = ebuf->e_shnum; | ||
95 | while (sections-- > 0) { | ||
96 | if (sbuf->sh_type == SHT_STRTAB) { | ||
97 | strbase = base + sbuf->sh_offset; | ||
98 | break; | ||
99 | } | ||
100 | sbuf++; | ||
101 | } | ||
102 | if (strbase == NULL) | ||
103 | goto error_close; | ||
104 | |||
105 | // Find dynamic section | ||
106 | sections = ebuf->e_shnum; | ||
107 | while (sections-- > 0) { | ||
108 | if (sbuf->sh_type == SHT_DYNAMIC) { | ||
109 | // Find DT_NEEDED tags | ||
110 | Elf_Dyn *dbuf = (Elf_Dyn *)(base + sbuf->sh_offset); | ||
111 | while (sbuf->sh_size >= sizeof(*dbuf)) { | ||
112 | if (dbuf->d_tag == DT_NEEDED) { | ||
113 | const char *lib = strbase + dbuf->d_un.d_ptr; | ||
114 | copy_libs_for_lib(lib, private_run_dir); | ||
115 | } | ||
116 | sbuf->sh_size -= sizeof(*dbuf); | ||
117 | dbuf++; | ||
118 | } | ||
119 | } | ||
120 | sbuf++; | ||
121 | } | ||
122 | goto close; | ||
123 | |||
124 | error_close: | ||
125 | perror("copy libs"); | ||
126 | close: | ||
127 | if (base) | ||
128 | munmap(base, s.st_size); | ||
129 | close(f); | ||
130 | } | ||
131 | |||
132 | static void copy_libs_for_lib(const char *lib, const char *private_run_dir) { | ||
133 | for (int i = 0; lib_paths[i]; i++) { | ||
134 | char *fname; | ||
135 | if (asprintf(&fname, "%s/%s", lib_paths[i], lib) == -1) | ||
136 | errExit("asprintf"); | ||
137 | if (access(fname, R_OK) == 0) { | ||
138 | char *dst; | ||
139 | if (asprintf(&dst, "%s/%s", private_run_dir, lib) == -1) | ||
140 | errExit("asprintf"); | ||
141 | |||
142 | if (access(dst, R_OK) != 0) { | ||
143 | duplicate(fname, private_run_dir); | ||
144 | // libs may need other libs | ||
145 | copy_libs_for_exe(fname, private_run_dir); | ||
146 | } | ||
147 | free(dst); | ||
148 | free(fname); | ||
149 | return; | ||
150 | } | ||
151 | free(fname); | ||
152 | } | ||
153 | errExit("library not found"); | ||
154 | } | ||
155 | |||
156 | void fs_private_lib(void) { | ||
157 | char *private_list = cfg.lib_private_keep; | ||
158 | |||
159 | // create /run/firejail/mnt/lib directory | ||
160 | mkdir_attr(RUN_LIB_DIR, 0755, 0, 0); | ||
161 | |||
162 | // copy the libs in the new lib directory for the main exe | ||
163 | if (cfg.original_program_index > 0) | ||
164 | copy_libs_for_exe(cfg.original_argv[cfg.original_program_index], RUN_LIB_DIR); | ||
165 | |||
166 | // for the shell | ||
167 | if (!arg_shell_none) | ||
168 | copy_libs_for_exe(cfg.shell, RUN_LIB_DIR); | ||
169 | |||
170 | // for the listed libs | ||
171 | if (private_list && *private_list != '\0') { | ||
172 | if (arg_debug) | ||
173 | printf("Copying extra files (%s) in the new lib directory:\n", private_list); | ||
174 | |||
175 | char *dlist = strdup(private_list); | ||
176 | if (!dlist) | ||
177 | errExit("strdup"); | ||
178 | |||
179 | char *ptr = strtok(dlist, ","); | ||
180 | copy_libs_for_lib(ptr, RUN_LIB_DIR); | ||
181 | |||
182 | while ((ptr = strtok(NULL, ",")) != NULL) | ||
183 | copy_libs_for_lib(ptr, RUN_LIB_DIR); | ||
184 | free(dlist); | ||
185 | fs_logger_print(); | ||
186 | } | ||
187 | |||
188 | if (arg_debug) | ||
189 | printf("Mount-bind %s on top of /lib /lib64 /usr/lib\n", RUN_LIB_DIR); | ||
190 | if (mount(RUN_LIB_DIR, "/lib", NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
191 | mount(NULL, "/lib", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
192 | errExit("mount bind"); | ||
193 | fs_logger("mount /lib"); | ||
194 | if (mount(RUN_LIB_DIR, "/lib64", NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
195 | mount(NULL, "/lib64", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
196 | errExit("mount bind"); | ||
197 | fs_logger("mount /lib64"); | ||
198 | if (mount(RUN_LIB_DIR, "/usr/lib", NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
199 | mount(NULL, "/usr/lib", NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) | ||
200 | errExit("mount bind"); | ||
201 | fs_logger("mount /usr/lib"); | ||
202 | } | ||
diff --git a/src/firejail/main.c b/src/firejail/main.c index c055a1537..ff57a5693 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -85,6 +85,7 @@ int arg_private_opt = 0; // private opt directory | |||
85 | int arg_private_srv = 0; // private srv directory | 85 | int arg_private_srv = 0; // private srv directory |
86 | int arg_private_bin = 0; // private bin directory | 86 | int arg_private_bin = 0; // private bin directory |
87 | int arg_private_tmp = 0; // private tmp directory | 87 | int arg_private_tmp = 0; // private tmp directory |
88 | int arg_private_lib = 0; // private lib directory | ||
88 | int arg_scan = 0; // arp-scan all interfaces | 89 | int arg_scan = 0; // arp-scan all interfaces |
89 | int arg_whitelist = 0; // whitelist commad | 90 | int arg_whitelist = 0; // whitelist commad |
90 | int arg_nosound = 0; // disable sound | 91 | int arg_nosound = 0; // disable sound |
@@ -110,6 +111,7 @@ int arg_allow_private_blacklist = 0; // blacklist things in private directorie | |||
110 | int arg_writable_var_log = 0; // writable /var/log | 111 | int arg_writable_var_log = 0; // writable /var/log |
111 | int arg_disable_mnt = 0; // disable /mnt and /media | 112 | int arg_disable_mnt = 0; // disable /mnt and /media |
112 | int arg_noprofile = 0; // use default.profile if none other found/specified | 113 | int arg_noprofile = 0; // use default.profile if none other found/specified |
114 | int arg_memory_deny_write_execute = 0; // block writable and executable memory | ||
113 | 115 | ||
114 | int login_shell = 0; | 116 | int login_shell = 0; |
115 | 117 | ||
@@ -1144,6 +1146,12 @@ int main(int argc, char **argv) { | |||
1144 | else | 1146 | else |
1145 | exit_err_feature("seccomp"); | 1147 | exit_err_feature("seccomp"); |
1146 | } | 1148 | } |
1149 | else if (strcmp(argv[i], "--memory-deny-write-execute") == 0) { | ||
1150 | if (checkcfg(CFG_SECCOMP)) | ||
1151 | arg_memory_deny_write_execute = 1; | ||
1152 | else | ||
1153 | exit_err_feature("seccomp"); | ||
1154 | } | ||
1147 | #endif | 1155 | #endif |
1148 | else if (strcmp(argv[i], "--caps") == 0) | 1156 | else if (strcmp(argv[i], "--caps") == 0) |
1149 | arg_caps_default_filter = 1; | 1157 | arg_caps_default_filter = 1; |
@@ -1622,6 +1630,17 @@ int main(int argc, char **argv) { | |||
1622 | cfg.bin_private_keep = argv[i] + 14; | 1630 | cfg.bin_private_keep = argv[i] + 14; |
1623 | arg_private_bin = 1; | 1631 | arg_private_bin = 1; |
1624 | } | 1632 | } |
1633 | else if (strncmp(argv[i], "--private-lib", 13) == 0) { | ||
1634 | // extract private lib list (if any) | ||
1635 | if (argv[i][13] == '=') { | ||
1636 | if (cfg.lib_private_keep) { | ||
1637 | if (argv[i][14] != '\0' && asprintf(&cfg.lib_private_keep, "%s,%s", cfg.lib_private_keep, argv[i] + 14) < 0) | ||
1638 | errExit("asprintf"); | ||
1639 | } else | ||
1640 | cfg.lib_private_keep = argv[i] + 14; | ||
1641 | } | ||
1642 | arg_private_lib = 1; | ||
1643 | } | ||
1625 | else if (strcmp(argv[i], "--private-tmp") == 0) { | 1644 | else if (strcmp(argv[i], "--private-tmp") == 0) { |
1626 | arg_private_tmp = 1; | 1645 | arg_private_tmp = 1; |
1627 | } | 1646 | } |
diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c index ef93368bf..9c474415d 100644 --- a/src/firejail/preproc.c +++ b/src/firejail/preproc.c | |||
@@ -83,6 +83,8 @@ void preproc_mount_mnt_dir(void) { | |||
83 | else | 83 | else |
84 | copy_file(PATH_SECCOMP_DEFAULT, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); // root needed | 84 | copy_file(PATH_SECCOMP_DEFAULT, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); // root needed |
85 | 85 | ||
86 | if (arg_memory_deny_write_execute) | ||
87 | copy_file(PATH_SECCOMP_MDWX, RUN_SECCOMP_MDWX, getuid(), getgid(), 0644); // root needed | ||
86 | // as root, create an empty RUN_SECCOMP_PROTOCOL file | 88 | // as root, create an empty RUN_SECCOMP_PROTOCOL file |
87 | create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); | 89 | create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); |
88 | if (set_perms(RUN_SECCOMP_PROTOCOL, getuid(), getgid(), 0644)) | 90 | if (set_perms(RUN_SECCOMP_PROTOCOL, getuid(), getgid(), 0644)) |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 18891ac58..972f5932d 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -595,6 +595,17 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
595 | return 0; | 595 | return 0; |
596 | } | 596 | } |
597 | 597 | ||
598 | // memory deny write&execute | ||
599 | if (strcmp(ptr, "memory-deny-write-execute") == 0) { | ||
600 | #ifdef HAVE_SECCOMP | ||
601 | if (checkcfg(CFG_SECCOMP)) | ||
602 | arg_memory_deny_write_execute = 1; | ||
603 | else | ||
604 | warning_feature_disabled("seccomp"); | ||
605 | #endif | ||
606 | return 0; | ||
607 | } | ||
608 | |||
598 | // caps drop list | 609 | // caps drop list |
599 | if (strncmp(ptr, "caps.drop ", 10) == 0) { | 610 | if (strncmp(ptr, "caps.drop ", 10) == 0) { |
600 | arg_caps_drop = 1; | 611 | arg_caps_drop = 1; |
@@ -849,6 +860,20 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
849 | return 0; | 860 | return 0; |
850 | } | 861 | } |
851 | 862 | ||
863 | // private /lib list of files | ||
864 | if (strncmp(ptr, "private-lib", 11) == 0) { | ||
865 | if (ptr[11] == ' ') { | ||
866 | if (cfg.lib_private_keep) { | ||
867 | if (ptr[12] != '\0' && asprintf(&cfg.lib_private_keep, "%s,%s", cfg.lib_private_keep, ptr + 12) < 0) | ||
868 | errExit("asprintf"); | ||
869 | } else { | ||
870 | cfg.lib_private_keep = ptr + 12; | ||
871 | } | ||
872 | } | ||
873 | arg_private_lib = 1; | ||
874 | return 0; | ||
875 | } | ||
876 | |||
852 | 877 | ||
853 | #ifdef HAVE_OVERLAYFS | 878 | #ifdef HAVE_OVERLAYFS |
854 | if (strncmp(ptr, "overlay-named ", 14) == 0) { | 879 | if (strncmp(ptr, "overlay-named ", 14) == 0) { |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 55733d5d7..6c0fdebe3 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -811,6 +811,16 @@ int sandbox(void* sandbox_arg) { | |||
811 | } | 811 | } |
812 | } | 812 | } |
813 | 813 | ||
814 | if (arg_private_lib) { | ||
815 | if (cfg.chrootdir) | ||
816 | fwarning("private-lib feature is disabled in chroot\n"); | ||
817 | else if (arg_overlay) | ||
818 | fwarning("private-lib feature is disabled in overlay\n"); | ||
819 | else { | ||
820 | fs_private_lib(); | ||
821 | } | ||
822 | } | ||
823 | |||
814 | if (arg_private_tmp) { | 824 | if (arg_private_tmp) { |
815 | if (cfg.chrootdir) | 825 | if (cfg.chrootdir) |
816 | fwarning("private-tmp feature is disabled in chroot\n"); | 826 | fwarning("private-tmp feature is disabled in chroot\n"); |
@@ -991,6 +1001,11 @@ int sandbox(void* sandbox_arg) { | |||
991 | else | 1001 | else |
992 | seccomp_filter_drop(enforce_seccomp); | 1002 | seccomp_filter_drop(enforce_seccomp); |
993 | } | 1003 | } |
1004 | if (arg_memory_deny_write_execute) { | ||
1005 | if (arg_debug) | ||
1006 | printf("Install memory write&execute filter\n"); | ||
1007 | seccomp_load(RUN_SECCOMP_MDWX); // install filter | ||
1008 | } | ||
994 | #endif | 1009 | #endif |
995 | 1010 | ||
996 | //**************************************** | 1011 | //**************************************** |
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 29f928ee7..6e0fc0919 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c | |||
@@ -19,6 +19,7 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | #ifdef HAVE_SECCOMP | 21 | #ifdef HAVE_SECCOMP |
22 | #include <sys/mman.h> | ||
22 | #include "firejail.h" | 23 | #include "firejail.h" |
23 | #include "../include/seccomp.h" | 24 | #include "../include/seccomp.h" |
24 | 25 | ||
@@ -64,24 +65,14 @@ int seccomp_load(const char *fname) { | |||
64 | int size = lseek(fd, 0, SEEK_END); | 65 | int size = lseek(fd, 0, SEEK_END); |
65 | if (size == -1) | 66 | if (size == -1) |
66 | goto errexit; | 67 | goto errexit; |
67 | if (lseek(fd, 0 , SEEK_SET) == -1) | ||
68 | goto errexit; | ||
69 | unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); | 68 | unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); |
70 | if (arg_debug) | 69 | if (arg_debug) |
71 | printf("configuring %d seccomp entries from %s\n", entries, fname); | 70 | printf("configuring %d seccomp entries from %s\n", entries, fname); |
72 | 71 | ||
73 | // read filter | 72 | // read filter |
74 | struct sock_filter *filter = malloc(size); | 73 | struct sock_filter *filter = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); |
75 | if (filter == NULL) | 74 | if (filter == MAP_FAILED) |
76 | goto errexit; | 75 | goto errexit; |
77 | memset(filter, 0, size); | ||
78 | int rd = 0; | ||
79 | while (rd < size) { | ||
80 | int rv = read(fd, (unsigned char *) filter + rd, size - rd); | ||
81 | if (rv == -1) | ||
82 | goto errexit; | ||
83 | rd += rv; | ||
84 | } | ||
85 | 76 | ||
86 | // close file | 77 | // close file |
87 | close(fd); | 78 | close(fd); |
@@ -91,14 +82,16 @@ int seccomp_load(const char *fname) { | |||
91 | .len = entries, | 82 | .len = entries, |
92 | .filter = filter, | 83 | .filter = filter, |
93 | }; | 84 | }; |
85 | int r = 0; | ||
94 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 86 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { |
95 | if (!err_printed) | 87 | if (!err_printed) |
96 | fwarning("seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | 88 | fwarning("seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); |
97 | err_printed = 1; | 89 | err_printed = 1; |
98 | return 1; | 90 | r = 1; |
99 | } | 91 | } |
100 | 92 | ||
101 | return 0; | 93 | munmap(filter, size); |
94 | return r; | ||
102 | 95 | ||
103 | errexit: | 96 | errexit: |
104 | fprintf(stderr, "Error: cannot read %s\n", fname); | 97 | fprintf(stderr, "Error: cannot read %s\n", fname); |
@@ -194,7 +187,7 @@ int seccomp_filter_drop(int enforce_seccomp) { | |||
194 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, | 187 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, |
195 | PATH_FSECCOMP, "print", RUN_SECCOMP_CFG); | 188 | PATH_FSECCOMP, "print", RUN_SECCOMP_CFG); |
196 | 189 | ||
197 | return seccomp_load(RUN_SECCOMP_CFG); | 190 | return 0; |
198 | } | 191 | } |
199 | 192 | ||
200 | // keep filter for seccomp option | 193 | // keep filter for seccomp option |
diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h index 1e4881e9c..157b71011 100644 --- a/src/fseccomp/fseccomp.h +++ b/src/fseccomp/fseccomp.h | |||
@@ -48,6 +48,7 @@ void seccomp_secondary_64(const char *fname); | |||
48 | void seccomp_secondary_32(const char *fname); | 48 | void seccomp_secondary_32(const char *fname); |
49 | 49 | ||
50 | // seccomp_file.c | 50 | // seccomp_file.c |
51 | void write_to_file(int fd, const void *data, int size); | ||
51 | void filter_init(int fd); | 52 | void filter_init(int fd); |
52 | void filter_add_whitelist(int fd, int syscall, int arg); | 53 | void filter_add_whitelist(int fd, int syscall, int arg); |
53 | void filter_add_blacklist(int fd, int syscall, int arg); | 54 | void filter_add_blacklist(int fd, int syscall, int arg); |
@@ -64,6 +65,8 @@ void seccomp_drop(const char *fname, char *list, int allow_debuggers); | |||
64 | void seccomp_default_drop(const char *fname, char *list, int allow_debuggers); | 65 | void seccomp_default_drop(const char *fname, char *list, int allow_debuggers); |
65 | // whitelisted filter | 66 | // whitelisted filter |
66 | void seccomp_keep(const char *fname, char *list); | 67 | void seccomp_keep(const char *fname, char *list); |
68 | // block writable and executable memory | ||
69 | void memory_deny_write_execute(const char *fname); | ||
67 | 70 | ||
68 | // seccomp_print | 71 | // seccomp_print |
69 | void filter_print(const char *fname); | 72 | void filter_print(const char *fname); |
diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c index e322b5bbb..3d95d5bb2 100644 --- a/src/fseccomp/main.c +++ b/src/fseccomp/main.c | |||
@@ -35,6 +35,7 @@ static void usage(void) { | |||
35 | printf("\tfseccomp default drop file list\n"); | 35 | printf("\tfseccomp default drop file list\n"); |
36 | printf("\tfseccomp default drop file list allow-debuggers\n"); | 36 | printf("\tfseccomp default drop file list allow-debuggers\n"); |
37 | printf("\tfseccomp keep file list\n"); | 37 | printf("\tfseccomp keep file list\n"); |
38 | printf("\tfseccomp memory-deny-write-execute file\n"); | ||
38 | printf("\tfseccomp print file\n"); | 39 | printf("\tfseccomp print file\n"); |
39 | } | 40 | } |
40 | 41 | ||
@@ -87,6 +88,8 @@ printf("\n"); | |||
87 | seccomp_default_drop(argv[3], argv[4], 1); | 88 | seccomp_default_drop(argv[3], argv[4], 1); |
88 | else if (argc == 4 && strcmp(argv[1], "keep") == 0) | 89 | else if (argc == 4 && strcmp(argv[1], "keep") == 0) |
89 | seccomp_keep(argv[2], argv[3]); | 90 | seccomp_keep(argv[2], argv[3]); |
91 | else if (argc == 3 && strcmp(argv[1], "memory-deny-write-execute") == 0) | ||
92 | memory_deny_write_execute(argv[2]); | ||
90 | else if (argc == 3 && strcmp(argv[1], "print") == 0) | 93 | else if (argc == 3 && strcmp(argv[1], "print") == 0) |
91 | filter_print(argv[2]); | 94 | filter_print(argv[2]); |
92 | else { | 95 | else { |
diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c index 4f8de8c5e..7d2ccbbce 100644 --- a/src/fseccomp/seccomp.c +++ b/src/fseccomp/seccomp.c | |||
@@ -19,7 +19,10 @@ | |||
19 | */ | 19 | */ |
20 | #include "fseccomp.h" | 20 | #include "fseccomp.h" |
21 | #include "../include/seccomp.h" | 21 | #include "../include/seccomp.h" |
22 | #include <sys/mman.h> | ||
23 | #include <sys/shm.h> | ||
22 | #include <sys/syscall.h> | 24 | #include <sys/syscall.h> |
25 | #include <sys/types.h> | ||
23 | 26 | ||
24 | static void add_default_list(int fd, int allow_debuggers) { | 27 | static void add_default_list(int fd, int allow_debuggers) { |
25 | #ifdef SYS_mount | 28 | #ifdef SYS_mount |
@@ -428,3 +431,54 @@ void seccomp_keep(const char *fname, char *list) { | |||
428 | // close file | 431 | // close file |
429 | close(fd); | 432 | close(fd); |
430 | } | 433 | } |
434 | |||
435 | void memory_deny_write_execute(const char *fname) { | ||
436 | // open file | ||
437 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
438 | if (fd < 0) { | ||
439 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
440 | exit(1); | ||
441 | } | ||
442 | |||
443 | filter_init(fd); | ||
444 | |||
445 | // build filter | ||
446 | static const struct sock_filter filter[] = { | ||
447 | #ifndef __x86_64__ | ||
448 | // block old multiplexing mmap syscall for i386 | ||
449 | BLACKLIST(SYS_mmap), | ||
450 | #endif | ||
451 | // block mmap(,,x|PROT_WRITE|PROT_EXEC) so W&X memory can't be created | ||
452 | #ifndef __x86_64__ | ||
453 | // mmap2 is used for mmap on i386 these days | ||
454 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_mmap2, 0, 5), | ||
455 | #else | ||
456 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_mmap, 0, 5), | ||
457 | #endif | ||
458 | EXAMINE_ARGUMENT(2), | ||
459 | BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_WRITE|PROT_EXEC), | ||
460 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_WRITE|PROT_EXEC, 0, 1), | ||
461 | KILL_PROCESS, | ||
462 | RETURN_ALLOW, | ||
463 | // block mprotect(,,PROT_EXEC) so writable memory can't be turned into executable | ||
464 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_mprotect, 0, 5), | ||
465 | EXAMINE_ARGUMENT(2), | ||
466 | BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), | ||
467 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), | ||
468 | KILL_PROCESS, | ||
469 | RETURN_ALLOW, | ||
470 | // block shmat(,,x|SHM_EXEC) so W&X shared memory can't be created | ||
471 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_shmat, 0, 5), | ||
472 | EXAMINE_ARGUMENT(2), | ||
473 | BPF_STMT(BPF_ALU+BPF_AND+BPF_K, SHM_EXEC), | ||
474 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SHM_EXEC, 0, 1), | ||
475 | KILL_PROCESS, | ||
476 | RETURN_ALLOW | ||
477 | }; | ||
478 | write_to_file(fd, filter, sizeof(filter)); | ||
479 | |||
480 | filter_end_blacklist(fd); | ||
481 | |||
482 | // close file | ||
483 | close(fd); | ||
484 | } | ||
diff --git a/src/fseccomp/seccomp_file.c b/src/fseccomp/seccomp_file.c index c74de9faf..16ffd5302 100644 --- a/src/fseccomp/seccomp_file.c +++ b/src/fseccomp/seccomp_file.c | |||
@@ -21,7 +21,7 @@ | |||
21 | #include "../include/seccomp.h" | 21 | #include "../include/seccomp.h" |
22 | #include <sys/syscall.h> | 22 | #include <sys/syscall.h> |
23 | 23 | ||
24 | static void write_to_file(int fd, void *data, int size) { | 24 | void write_to_file(int fd, const void *data, int size) { |
25 | assert(data); | 25 | assert(data); |
26 | assert(size); | 26 | assert(size); |
27 | 27 | ||
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index cd47800c5..3a5e8560c 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -740,6 +740,12 @@ Example: | |||
740 | $ firejail \-\-machine-id | 740 | $ firejail \-\-machine-id |
741 | 741 | ||
742 | .TP | 742 | .TP |
743 | \fB\-\-memory-deny-write-execute | ||
744 | Install a seccomp filter to block attempts to create memory mappings | ||
745 | that are both writable and executable, to change mappings to be | ||
746 | executable or to create executable shared memory. | ||
747 | |||
748 | .TP | ||
743 | \fB\-\-mtu=number | 749 | \fB\-\-mtu=number |
744 | Assign a MTU value to the last network interface defined by a \-\-net option. | 750 | Assign a MTU value to the last network interface defined by a \-\-net option. |
745 | .br | 751 | .br |
@@ -1232,6 +1238,34 @@ $ ls /bin | |||
1232 | bash cat ls sed | 1238 | bash cat ls sed |
1233 | 1239 | ||
1234 | .TP | 1240 | .TP |
1241 | \fB\-\-private-lib=file,file | ||
1242 | Build a new /lib in a temporary filesystem. For command to be executed, | ||
1243 | the shell (if \-\-shell=none is not used), and the listed libraries | ||
1244 | find out dynamic libraries and copy them to the /lib directory. | ||
1245 | If no listed file is found, /lib directory will be empty and no programs will be able to execute. | ||
1246 | The same directory is also bind-mounted over /lib64 and /usr/lib. | ||
1247 | All modifications are discarded when the sandbox is closed. | ||
1248 | .br | ||
1249 | |||
1250 | .br | ||
1251 | Example: | ||
1252 | .br | ||
1253 | $ firejail \-\-noprofile \-\-shell=none \-\-private-lib= \-\-private-bin=ls /bin/ls /lib /bin | ||
1254 | .br | ||
1255 | Parent pid 15733, child pid 15734 | ||
1256 | .br | ||
1257 | Child process initialized in 69.61 ms | ||
1258 | .br | ||
1259 | /bin: | ||
1260 | .br | ||
1261 | ls | ||
1262 | .br | ||
1263 | .br | ||
1264 | /lib: | ||
1265 | .br | ||
1266 | ld-linux-x86-64.so.2 libc.so.6 libdl.so.2 libpcre.so.3 libpthread.so.0 libselinux.so.1 | ||
1267 | |||
1268 | .TP | ||
1235 | \fB\-\-private-dev | 1269 | \fB\-\-private-dev |
1236 | Create a new /dev directory. Only dri, null, full, zero, tty, pts, ptmx, random, snd, urandom, video, log and shm devices are available. | 1270 | Create a new /dev directory. Only dri, null, full, zero, tty, pts, ptmx, random, snd, urandom, video, log and shm devices are available. |
1237 | .br | 1271 | .br |