diff options
author | 2020-03-14 00:07:06 +0200 | |
---|---|---|
committer | 2020-03-28 11:24:25 +0000 | |
commit | 88eadbf31fe25dcd7c224a5d92f71c79ccf6c9d3 (patch) | |
tree | 6b4d2a805a2900755bfc857586a10948b3c8395e /src/fseccomp/seccomp.c | |
parent | Added compatibility with BetterDiscord (#3300) (diff) | |
download | firejail-88eadbf31fe25dcd7c224a5d92f71c79ccf6c9d3.tar.gz firejail-88eadbf31fe25dcd7c224a5d92f71c79ccf6c9d3.tar.zst firejail-88eadbf31fe25dcd7c224a5d92f71c79ccf6c9d3.zip |
seccomp: allow defining separate filters for 32-bit arch
System calls (names and numbers) are not exactly the same for 32 bit
and 64 bit architectures. Let's allow defining separate filters for
32-bit arch using seccomp.32, seccomp.32.drop, seccomp.32.keep. This
is useful for mixed 64/32 bit application environments like Steam and
Wine.
Implement protocol and mdwx filtering also for 32 bit arch. It's still
better to block secondary archs completely if not needed.
Lists of supported system calls are also updated.
Warn if preload libraries would be needed due to trace, tracelog or
postexecseccomp (seccomp.drop=execve etc), because a 32-bit dynamic
linker does not understand the 64 bit preload libraries.
Closes #3267.
Signed-off-by: Topi Miettinen <toiwoton@gmail.com>
Diffstat (limited to 'src/fseccomp/seccomp.c')
-rw-r--r-- | src/fseccomp/seccomp.c | 143 |
1 files changed, 115 insertions, 28 deletions
diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c index 29aa2f2f5..0db7b5954 100644 --- a/src/fseccomp/seccomp.c +++ b/src/fseccomp/seccomp.c | |||
@@ -24,12 +24,12 @@ | |||
24 | #include <sys/syscall.h> | 24 | #include <sys/syscall.h> |
25 | #include <sys/types.h> | 25 | #include <sys/types.h> |
26 | 26 | ||
27 | static void add_default_list(int fd, int allow_debuggers) { | 27 | static void add_default_list(int fd, int allow_debuggers, bool native) { |
28 | int r; | 28 | int r; |
29 | if (!allow_debuggers) | 29 | if (!allow_debuggers) |
30 | r = syscall_check_list("@default-nodebuggers", filter_add_blacklist, fd, 0, NULL); | 30 | r = syscall_check_list("@default-nodebuggers", filter_add_blacklist, fd, 0, NULL, native); |
31 | else | 31 | else |
32 | r = syscall_check_list("@default", filter_add_blacklist, fd, 0, NULL); | 32 | r = syscall_check_list("@default", filter_add_blacklist, fd, 0, NULL, native); |
33 | 33 | ||
34 | assert(r == 0); | 34 | assert(r == 0); |
35 | //#ifdef SYS_mknod - emoved in 0.9.29 - it breaks Zotero extension | 35 | //#ifdef SYS_mknod - emoved in 0.9.29 - it breaks Zotero extension |
@@ -46,7 +46,7 @@ static void add_default_list(int fd, int allow_debuggers) { | |||
46 | } | 46 | } |
47 | 47 | ||
48 | // default list | 48 | // default list |
49 | void seccomp_default(const char *fname, int allow_debuggers) { | 49 | void seccomp_default(const char *fname, int allow_debuggers, bool native) { |
50 | assert(fname); | 50 | assert(fname); |
51 | 51 | ||
52 | // open file | 52 | // open file |
@@ -57,8 +57,8 @@ void seccomp_default(const char *fname, int allow_debuggers) { | |||
57 | } | 57 | } |
58 | 58 | ||
59 | // build filter (no post-exec filter needed because default list is fine for us) | 59 | // build filter (no post-exec filter needed because default list is fine for us) |
60 | filter_init(fd); | 60 | filter_init(fd, native); |
61 | add_default_list(fd, allow_debuggers); | 61 | add_default_list(fd, allow_debuggers, native); |
62 | filter_end_blacklist(fd); | 62 | filter_end_blacklist(fd); |
63 | 63 | ||
64 | // close file | 64 | // close file |
@@ -66,7 +66,7 @@ void seccomp_default(const char *fname, int allow_debuggers) { | |||
66 | } | 66 | } |
67 | 67 | ||
68 | // drop list | 68 | // drop list |
69 | void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers) { | 69 | void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers, bool native) { |
70 | assert(fname1); | 70 | assert(fname1); |
71 | assert(fname2); | 71 | assert(fname2); |
72 | (void) allow_debuggers; // todo: to implemnet it | 72 | (void) allow_debuggers; // todo: to implemnet it |
@@ -79,15 +79,15 @@ void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_ | |||
79 | } | 79 | } |
80 | 80 | ||
81 | // build pre-exec filter: don't blacklist any syscalls in @default-keep | 81 | // build pre-exec filter: don't blacklist any syscalls in @default-keep |
82 | filter_init(fd); | 82 | filter_init(fd, native); |
83 | 83 | ||
84 | // allow exceptions in form of !syscall | 84 | // allow exceptions in form of !syscall |
85 | syscall_check_list(list, filter_add_whitelist_for_excluded, fd, 0, NULL); | 85 | syscall_check_list(list, filter_add_whitelist_for_excluded, fd, 0, NULL, native); |
86 | 86 | ||
87 | char *prelist, *postlist; | 87 | char *prelist, *postlist; |
88 | syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist); | 88 | syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist, native); |
89 | if (prelist) | 89 | if (prelist) |
90 | if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL)) { | 90 | if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL, native)) { |
91 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | 91 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); |
92 | exit(1); | 92 | exit(1); |
93 | } | 93 | } |
@@ -106,8 +106,8 @@ void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_ | |||
106 | } | 106 | } |
107 | 107 | ||
108 | // build post-exec filter: blacklist remaining syscalls | 108 | // build post-exec filter: blacklist remaining syscalls |
109 | filter_init(fd); | 109 | filter_init(fd, native); |
110 | if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL)) { | 110 | if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL, native)) { |
111 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | 111 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); |
112 | exit(1); | 112 | exit(1); |
113 | } | 113 | } |
@@ -118,7 +118,7 @@ void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_ | |||
118 | } | 118 | } |
119 | 119 | ||
120 | // default+drop | 120 | // default+drop |
121 | void seccomp_default_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers) { | 121 | void seccomp_default_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers, bool native) { |
122 | assert(fname1); | 122 | assert(fname1); |
123 | assert(fname2); | 123 | assert(fname2); |
124 | 124 | ||
@@ -131,16 +131,16 @@ void seccomp_default_drop(const char *fname1, const char *fname2, char *list, in | |||
131 | 131 | ||
132 | // build pre-exec filter: blacklist @default, don't blacklist | 132 | // build pre-exec filter: blacklist @default, don't blacklist |
133 | // any listed syscalls in @default-keep | 133 | // any listed syscalls in @default-keep |
134 | filter_init(fd); | 134 | filter_init(fd, native); |
135 | 135 | ||
136 | // allow exceptions in form of !syscall | 136 | // allow exceptions in form of !syscall |
137 | syscall_check_list(list, filter_add_whitelist_for_excluded, fd, 0, NULL); | 137 | syscall_check_list(list, filter_add_whitelist_for_excluded, fd, 0, NULL, native); |
138 | 138 | ||
139 | add_default_list(fd, allow_debuggers); | 139 | add_default_list(fd, allow_debuggers, native); |
140 | char *prelist, *postlist; | 140 | char *prelist, *postlist; |
141 | syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist); | 141 | syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist, native); |
142 | if (prelist) | 142 | if (prelist) |
143 | if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL)) { | 143 | if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL, native)) { |
144 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | 144 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); |
145 | exit(1); | 145 | exit(1); |
146 | } | 146 | } |
@@ -160,8 +160,8 @@ void seccomp_default_drop(const char *fname1, const char *fname2, char *list, in | |||
160 | } | 160 | } |
161 | 161 | ||
162 | // build post-exec filter: blacklist remaining syscalls | 162 | // build post-exec filter: blacklist remaining syscalls |
163 | filter_init(fd); | 163 | filter_init(fd, native); |
164 | if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL)) { | 164 | if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL, native)) { |
165 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | 165 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); |
166 | exit(1); | 166 | exit(1); |
167 | } | 167 | } |
@@ -171,7 +171,7 @@ void seccomp_default_drop(const char *fname1, const char *fname2, char *list, in | |||
171 | close(fd); | 171 | close(fd); |
172 | } | 172 | } |
173 | 173 | ||
174 | void seccomp_keep(const char *fname1, const char *fname2, char *list) { | 174 | void seccomp_keep(const char *fname1, const char *fname2, char *list, bool native) { |
175 | (void) fname2; | 175 | (void) fname2; |
176 | 176 | ||
177 | // open file for pre-exec filter | 177 | // open file for pre-exec filter |
@@ -182,17 +182,17 @@ void seccomp_keep(const char *fname1, const char *fname2, char *list) { | |||
182 | } | 182 | } |
183 | 183 | ||
184 | // build pre-exec filter: whitelist also @default-keep | 184 | // build pre-exec filter: whitelist also @default-keep |
185 | filter_init(fd); | 185 | filter_init(fd, native); |
186 | 186 | ||
187 | // allow exceptions in form of !syscall | 187 | // allow exceptions in form of !syscall |
188 | syscall_check_list(list, filter_add_blacklist_for_excluded, fd, 0, NULL); | 188 | syscall_check_list(list, filter_add_blacklist_for_excluded, fd, 0, NULL, native); |
189 | 189 | ||
190 | // these syscalls are used by firejail after the seccomp filter is initialized | 190 | // these syscalls are used by firejail after the seccomp filter is initialized |
191 | int r; | 191 | int r; |
192 | r = syscall_check_list("@default-keep", filter_add_whitelist, fd, 0, NULL); | 192 | r = syscall_check_list("@default-keep", filter_add_whitelist, fd, 0, NULL, native); |
193 | assert(r == 0); | 193 | assert(r == 0); |
194 | 194 | ||
195 | if (syscall_check_list(list, filter_add_whitelist, fd, 0, NULL)) { | 195 | if (syscall_check_list(list, filter_add_whitelist, fd, 0, NULL, native)) { |
196 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | 196 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); |
197 | exit(1); | 197 | exit(1); |
198 | } | 198 | } |
@@ -206,6 +206,15 @@ void seccomp_keep(const char *fname1, const char *fname2, char *list) { | |||
206 | #if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__) | 206 | #if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__) |
207 | # define filter_syscall SYS_mmap | 207 | # define filter_syscall SYS_mmap |
208 | # undef block_syscall | 208 | # undef block_syscall |
209 | #if defined(__x86_64__) | ||
210 | // i386 syscalls | ||
211 | # define filter_syscall_32 192 | ||
212 | # define block_syscall_32 90 | ||
213 | # define mprotect_32 125 | ||
214 | # define pkey_mprotect_32 380 | ||
215 | # define shmat_32 397 | ||
216 | # define memfd_create_32 356 | ||
217 | #endif | ||
209 | #elif defined(__i386__) | 218 | #elif defined(__i386__) |
210 | # define filter_syscall SYS_mmap2 | 219 | # define filter_syscall SYS_mmap2 |
211 | # define block_syscall SYS_mmap | 220 | # define block_syscall SYS_mmap |
@@ -216,6 +225,12 @@ void seccomp_keep(const char *fname1, const char *fname2, char *list) { | |||
216 | # warning "Platform does not support seccomp memory-deny-write-execute filter yet" | 225 | # warning "Platform does not support seccomp memory-deny-write-execute filter yet" |
217 | # undef filter_syscall | 226 | # undef filter_syscall |
218 | # undef block_syscall | 227 | # undef block_syscall |
228 | # undef filter_syscall_32 | ||
229 | # undef block_syscall_32 | ||
230 | # undef mprotect_32 | ||
231 | # undef pkey_mprotect_32 | ||
232 | # undef shmat_32 | ||
233 | # undef memfd_create_32 | ||
219 | #endif | 234 | #endif |
220 | 235 | ||
221 | void memory_deny_write_execute(const char *fname) { | 236 | void memory_deny_write_execute(const char *fname) { |
@@ -226,10 +241,10 @@ void memory_deny_write_execute(const char *fname) { | |||
226 | exit(1); | 241 | exit(1); |
227 | } | 242 | } |
228 | 243 | ||
229 | filter_init(fd); | 244 | filter_init(fd, true); |
230 | 245 | ||
231 | // build filter | 246 | // build filter |
232 | static const struct sock_filter filter[] = { | 247 | struct sock_filter filter[] = { |
233 | #ifdef block_syscall | 248 | #ifdef block_syscall |
234 | // block old multiplexing mmap syscall for i386 | 249 | // block old multiplexing mmap syscall for i386 |
235 | BLACKLIST(block_syscall), | 250 | BLACKLIST(block_syscall), |
@@ -288,3 +303,75 @@ void memory_deny_write_execute(const char *fname) { | |||
288 | // close file | 303 | // close file |
289 | close(fd); | 304 | close(fd); |
290 | } | 305 | } |
306 | |||
307 | void memory_deny_write_execute_32(const char *fname) { | ||
308 | // open file | ||
309 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
310 | if (fd < 0) { | ||
311 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
312 | exit(1); | ||
313 | } | ||
314 | |||
315 | filter_init(fd, false); | ||
316 | |||
317 | // build filter | ||
318 | struct sock_filter filter[] = { | ||
319 | #if defined(__x86_64__) | ||
320 | #ifdef block_syscall_32 | ||
321 | // block old multiplexing mmap syscall for i386 | ||
322 | BLACKLIST(block_syscall_32), | ||
323 | #endif | ||
324 | #ifdef filter_syscall_32 | ||
325 | // block mmap(,,x|PROT_WRITE|PROT_EXEC) so W&X memory can't be created | ||
326 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, filter_syscall_32, 0, 5), | ||
327 | EXAMINE_ARGUMENT(2), | ||
328 | BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_WRITE|PROT_EXEC), | ||
329 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_WRITE|PROT_EXEC, 0, 1), | ||
330 | KILL_PROCESS, | ||
331 | RETURN_ALLOW, | ||
332 | #endif | ||
333 | #ifdef mprotect_32 | ||
334 | // block mprotect(,,PROT_EXEC) so writable memory can't be turned into executable | ||
335 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, mprotect_32, 0, 5), | ||
336 | EXAMINE_ARGUMENT(2), | ||
337 | BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), | ||
338 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), | ||
339 | KILL_PROCESS, | ||
340 | RETURN_ALLOW, | ||
341 | #endif | ||
342 | #ifdef pkey_mprotect_32 | ||
343 | // same for pkey_mprotect(,,PROT_EXEC), where available | ||
344 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, pkey_mprotect_32, 0, 5), | ||
345 | EXAMINE_ARGUMENT(2), | ||
346 | BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), | ||
347 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), | ||
348 | KILL_PROCESS, | ||
349 | RETURN_ALLOW, | ||
350 | #endif | ||
351 | |||
352 | #ifdef shmat_32 | ||
353 | // block shmat(,,x|SHM_EXEC) so W&X shared memory can't be created | ||
354 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, shmat_32, 0, 5), | ||
355 | EXAMINE_ARGUMENT(2), | ||
356 | BPF_STMT(BPF_ALU+BPF_AND+BPF_K, SHM_EXEC), | ||
357 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SHM_EXEC, 0, 1), | ||
358 | KILL_PROCESS, | ||
359 | RETURN_ALLOW, | ||
360 | #endif | ||
361 | #ifdef memfd_create_32 | ||
362 | // block memfd_create as it can be used to create | ||
363 | // arbitrary memory contents which can be later mapped | ||
364 | // as executable | ||
365 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, memfd_create_32, 0, 1), | ||
366 | KILL_PROCESS, | ||
367 | #endif | ||
368 | #endif | ||
369 | RETURN_ALLOW | ||
370 | }; | ||
371 | write_to_file(fd, filter, sizeof(filter)); | ||
372 | |||
373 | filter_end_blacklist(fd); | ||
374 | |||
375 | // close file | ||
376 | close(fd); | ||
377 | } | ||