diff options
author | 2016-11-02 07:49:01 -0400 | |
---|---|---|
committer | 2016-11-02 07:49:01 -0400 | |
commit | 72b93c5761b5e42c5742e192f46bac1696c36f4c (patch) | |
tree | 3951e01a771ea3e8f11b8364991bb47f752f011f /src/firejail/seccomp.c | |
parent | fixed /run/firejail/mnt problem introduced recently (diff) | |
download | firejail-72b93c5761b5e42c5742e192f46bac1696c36f4c.tar.gz firejail-72b93c5761b5e42c5742e192f46bac1696c36f4c.tar.zst firejail-72b93c5761b5e42c5742e192f46bac1696c36f4c.zip |
major cleanup
Diffstat (limited to 'src/firejail/seccomp.c')
-rw-r--r-- | src/firejail/seccomp.c | 873 |
1 files changed, 143 insertions, 730 deletions
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 69be04a03..74d29fc9d 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c | |||
@@ -22,760 +22,203 @@ | |||
22 | #include "firejail.h" | 22 | #include "firejail.h" |
23 | #include "../include/seccomp.h" | 23 | #include "../include/seccomp.h" |
24 | 24 | ||
25 | #define SECSIZE 128 // initial filter size | 25 | int seccomp_load(const char *fname) { |
26 | static struct sock_filter *sfilter = NULL; | 26 | assert(fname); |
27 | static int sfilter_alloc_size = 0; | ||
28 | static int sfilter_index = 0; | ||
29 | |||
30 | // debug filter | ||
31 | void filter_debug(void) { | ||
32 | // start filter | ||
33 | struct sock_filter filter[] = { | ||
34 | VALIDATE_ARCHITECTURE, | ||
35 | EXAMINE_SYSCALL | ||
36 | }; | ||
37 | 27 | ||
38 | // print sizes | 28 | // check file |
39 | printf("SECCOMP Filter:\n"); | 29 | struct stat s; |
40 | if (sfilter == NULL) { | 30 | if (stat(fname, &s) == -1) { |
41 | printf("SECCOMP filter not allocated\n"); | 31 | fprintf(stderr, "Error: cannot read protocol filter file\n"); |
42 | return; | 32 | exit(1); |
43 | } | ||
44 | if (sfilter_index < 4) | ||
45 | return; | ||
46 | |||
47 | // test the start of the filter | ||
48 | if (memcmp(sfilter, filter, sizeof(filter)) == 0) { | ||
49 | printf(" VALIDATE_ARCHITECTURE\n"); | ||
50 | printf(" EXAMINE_SYSCAL\n"); | ||
51 | } | 33 | } |
52 | 34 | int size = s.st_size; | |
53 | // loop trough blacklists | 35 | unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); |
54 | int i = 4; | 36 | //printf("size %d, entries %d\n", s.st_size, entries); |
55 | while (i < sfilter_index) { | 37 | |
56 | // minimal parsing! | 38 | // read filter |
57 | unsigned char *ptr = (unsigned char *) &sfilter[i]; | 39 | struct sock_filter filter[entries]; |
58 | int *nr = (int *) (ptr + 4); | 40 | memset(&filter[0], 0, sizeof(filter)); |
59 | if (*ptr == 0x15 && *(ptr +14) == 0xff && *(ptr + 15) == 0x7f ) { | 41 | int src = open(fname, O_RDONLY); |
60 | printf(" WHITELIST %d %s\n", *nr, syscall_find_nr(*nr)); | 42 | int rd = 0; |
61 | i += 2; | 43 | while (rd < size) { |
62 | } | 44 | int rv = read(src, (unsigned char *) filter + rd, size - rd); |
63 | else if (*ptr == 0x15 && *(ptr +14) == 0 && *(ptr + 15) == 0) { | 45 | if (rv == -1) { |
64 | printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr)); | 46 | fprintf(stderr, "Error: cannot read %s file\n", fname); |
65 | i += 2; | 47 | exit(1); |
66 | } | ||
67 | else if (*ptr == 0x15 && *(ptr +14) == 0x5 && *(ptr + 15) == 0) { | ||
68 | int err = *(ptr + 13) << 8 | *(ptr + 12); | ||
69 | printf(" ERRNO %d %s %d %s\n", *nr, syscall_find_nr(*nr), err, errno_find_nr(err)); | ||
70 | i += 2; | ||
71 | } | ||
72 | else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) { | ||
73 | printf(" KILL_PROCESS\n"); | ||
74 | i++; | ||
75 | } | ||
76 | else if (*ptr == 0x06 && *(ptr +6) == 0xff && *(ptr + 7) == 0x7f ) { | ||
77 | printf(" RETURN_ALLOW\n"); | ||
78 | i++; | ||
79 | } | ||
80 | else { | ||
81 | printf(" UNKNOWN ENTRY!!!\n"); | ||
82 | i++; | ||
83 | } | 48 | } |
49 | rd += rv; | ||
84 | } | 50 | } |
85 | } | 51 | close(src); |
86 | |||
87 | // initialize filter | ||
88 | static void filter_init(void) { | ||
89 | if (sfilter) { | ||
90 | assert(0); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | // if (arg_debug) | ||
95 | // printf("Initialize seccomp filter\n"); | ||
96 | // allocate a filter of SECSIZE | ||
97 | sfilter = malloc(sizeof(struct sock_filter) * SECSIZE); | ||
98 | if (!sfilter) | ||
99 | errExit("malloc"); | ||
100 | memset(sfilter, 0, sizeof(struct sock_filter) * SECSIZE); | ||
101 | sfilter_alloc_size = SECSIZE; | ||
102 | |||
103 | // copy the start entries | ||
104 | #if defined(__x86_64__) | ||
105 | #define X32_SYSCALL_BIT 0x40000000 | ||
106 | struct sock_filter filter[] = { | ||
107 | VALIDATE_ARCHITECTURE, | ||
108 | EXAMINE_SYSCALL, | ||
109 | // handle X32 ABI | ||
110 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), | ||
111 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), | ||
112 | RETURN_ERRNO(EPERM) | ||
113 | }; | ||
114 | #else | ||
115 | struct sock_filter filter[] = { | ||
116 | VALIDATE_ARCHITECTURE, | ||
117 | EXAMINE_SYSCALL | ||
118 | }; | ||
119 | #endif | ||
120 | sfilter_index = sizeof(filter) / sizeof(struct sock_filter); | ||
121 | memcpy(sfilter, filter, sizeof(filter)); | ||
122 | } | ||
123 | |||
124 | static void filter_realloc(void) { | ||
125 | assert(sfilter); | ||
126 | assert(sfilter_alloc_size); | ||
127 | assert(sfilter_index); | ||
128 | if (arg_debug) | ||
129 | printf("Allocating more seccomp filter entries\n"); | ||
130 | |||
131 | // allocate the new memory | ||
132 | struct sock_filter *old = sfilter; | ||
133 | sfilter = malloc(sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE)); | ||
134 | if (!sfilter) | ||
135 | errExit("malloc"); | ||
136 | memset(sfilter, 0, sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE)); | ||
137 | |||
138 | // copy old filter | ||
139 | memcpy(sfilter, old, sizeof(struct sock_filter) * sfilter_alloc_size); | ||
140 | sfilter_alloc_size += SECSIZE; | ||
141 | } | ||
142 | |||
143 | static void filter_add_whitelist(int syscall, int arg) { | ||
144 | (void) arg; | ||
145 | assert(sfilter); | ||
146 | assert(sfilter_alloc_size); | ||
147 | assert(sfilter_index); | ||
148 | // if (arg_debug) | ||
149 | // printf("Whitelisting syscall %d %s\n", syscall, syscall_find_nr(syscall)); | ||
150 | |||
151 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
152 | filter_realloc(); | ||
153 | |||
154 | struct sock_filter filter[] = { | ||
155 | WHITELIST(syscall) | ||
156 | }; | ||
157 | #if 0 | ||
158 | { | ||
159 | int i; | ||
160 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
161 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
162 | printf("%x, ", (*ptr) & 0xff); | ||
163 | printf("\n"); | ||
164 | } | ||
165 | #endif | ||
166 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
167 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
168 | } | ||
169 | 52 | ||
170 | static void filter_add_blacklist(int syscall, int arg) { | 53 | // install filter |
171 | (void) arg; | 54 | struct sock_fprog prog = { |
172 | assert(sfilter); | 55 | .len = entries, |
173 | assert(sfilter_alloc_size); | 56 | .filter = filter, |
174 | assert(sfilter_index); | ||
175 | // if (arg_debug) | ||
176 | // printf("Blacklisting syscall %d %s\n", syscall, syscall_find_nr(syscall)); | ||
177 | |||
178 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
179 | filter_realloc(); | ||
180 | |||
181 | struct sock_filter filter[] = { | ||
182 | BLACKLIST(syscall) | ||
183 | }; | ||
184 | #if 0 | ||
185 | { | ||
186 | int i; | ||
187 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
188 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
189 | printf("%x, ", (*ptr) & 0xff); | ||
190 | printf("\n"); | ||
191 | } | ||
192 | #endif | ||
193 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
194 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
195 | } | ||
196 | |||
197 | static void filter_add_errno(int syscall, int arg) { | ||
198 | assert(sfilter); | ||
199 | assert(sfilter_alloc_size); | ||
200 | assert(sfilter_index); | ||
201 | // if (arg_debug) | ||
202 | // printf("Errno syscall %d %d %s\n", syscall, arg, syscall_find_nr(syscall)); | ||
203 | |||
204 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
205 | filter_realloc(); | ||
206 | |||
207 | struct sock_filter filter[] = { | ||
208 | BLACKLIST_ERRNO(syscall, arg) | ||
209 | }; | ||
210 | #if 0 | ||
211 | { | ||
212 | int i; | ||
213 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
214 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
215 | printf("%x, ", (*ptr) & 0xff); | ||
216 | printf("\n"); | ||
217 | } | ||
218 | #endif | ||
219 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
220 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
221 | } | ||
222 | |||
223 | static void filter_end_blacklist(void) { | ||
224 | assert(sfilter); | ||
225 | assert(sfilter_alloc_size); | ||
226 | assert(sfilter_index); | ||
227 | // if (arg_debug) | ||
228 | // printf("Ending syscall filter\n"); | ||
229 | |||
230 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
231 | filter_realloc(); | ||
232 | |||
233 | struct sock_filter filter[] = { | ||
234 | RETURN_ALLOW | ||
235 | }; | ||
236 | #if 0 | ||
237 | { | ||
238 | int i; | ||
239 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
240 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
241 | printf("%x, ", (*ptr) & 0xff); | ||
242 | printf("\n"); | ||
243 | } | ||
244 | #endif | ||
245 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
246 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
247 | } | ||
248 | |||
249 | static void filter_end_whitelist(void) { | ||
250 | assert(sfilter); | ||
251 | assert(sfilter_alloc_size); | ||
252 | assert(sfilter_index); | ||
253 | if (arg_debug) | ||
254 | printf("Ending syscall filter\n"); | ||
255 | |||
256 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
257 | filter_realloc(); | ||
258 | |||
259 | struct sock_filter filter[] = { | ||
260 | KILL_PROCESS | ||
261 | }; | 57 | }; |
262 | #if 0 | ||
263 | { | ||
264 | int i; | ||
265 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
266 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
267 | printf("%x, ", (*ptr) & 0xff); | ||
268 | printf("\n"); | ||
269 | } | ||
270 | #endif | ||
271 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
272 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
273 | } | ||
274 | |||
275 | |||
276 | // save seccomp filter in /run/firejail/mnt/seccomp | ||
277 | static void write_seccomp_file(void) { | ||
278 | assert(sfilter); | ||
279 | |||
280 | int fd = open(RUN_SECCOMP_CFG, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); | ||
281 | if (fd == -1) | ||
282 | errExit("open"); | ||
283 | 58 | ||
284 | if (arg_debug) | 59 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { |
285 | printf("Save seccomp filter, size %u bytes\n", (unsigned) (sfilter_index * sizeof(struct sock_filter))); | 60 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); |
286 | errno = 0; | 61 | return 1; |
287 | ssize_t sz = write(fd, sfilter, sfilter_index * sizeof(struct sock_filter)); | ||
288 | if (sz != (ssize_t)(sfilter_index * sizeof(struct sock_filter))) { | ||
289 | fprintf(stderr, "Error: cannot save seccomp filter\n"); | ||
290 | exit(1); | ||
291 | } | 62 | } |
292 | SET_PERMS_FD(fd, 0, 0, S_IRUSR | S_IWUSR); | 63 | |
293 | close(fd); | 64 | return 0; |
294 | } | 65 | } |
295 | 66 | ||
296 | // read seccomp filter from /run/firejail/mnt/seccomp | ||
297 | static void read_seccomp_file(const char *fname) { | ||
298 | assert(sfilter == NULL && sfilter_index == 0); | ||
299 | 67 | ||
300 | // check file | ||
301 | struct stat s; | ||
302 | if (stat(fname, &s) == -1) { | ||
303 | fprintf(stderr, "Warning: seccomp file not found\n"); | ||
304 | return; | ||
305 | } | ||
306 | ssize_t sz = s.st_size; | ||
307 | if (sz == 0 || (sz % sizeof(struct sock_filter)) != 0) { | ||
308 | fprintf(stderr, "Error: invalid seccomp file\n"); | ||
309 | exit(1); | ||
310 | } | ||
311 | sfilter = malloc(sz); | ||
312 | if (!sfilter) | ||
313 | errExit("malloc"); | ||
314 | |||
315 | // read file | ||
316 | /* coverity[toctou] */ | ||
317 | int fd = open(fname,O_RDONLY); | ||
318 | if (fd == -1) | ||
319 | errExit("open"); | ||
320 | errno = 0; | ||
321 | ssize_t size = read(fd, sfilter, sz); | ||
322 | if (size != sz) { | ||
323 | fprintf(stderr, "Error: invalid seccomp file\n"); | ||
324 | exit(1); | ||
325 | } | ||
326 | sfilter_index = sz / sizeof(struct sock_filter); | ||
327 | 68 | ||
328 | if (arg_debug) | ||
329 | printf("Read seccomp filter, size %u bytes\n", (unsigned) (sfilter_index * sizeof(struct sock_filter))); | ||
330 | |||
331 | close(fd); | ||
332 | |||
333 | if (arg_debug) | ||
334 | filter_debug(); | ||
335 | } | ||
336 | 69 | ||
337 | // i386 filter installed on amd64 architectures | 70 | // i386 filter installed on amd64 architectures |
338 | void seccomp_filter_32(void) { | 71 | void seccomp_filter_32(void) { |
339 | // hardcoded syscall values | 72 | if (arg_debug) |
340 | struct sock_filter filter[] = { | 73 | printf("Build secondary 32-bit filter\n"); |
341 | VALIDATE_ARCHITECTURE_32, | ||
342 | EXAMINE_SYSCALL, | ||
343 | BLACKLIST(21), // mount | ||
344 | BLACKLIST(52), // umount2 | ||
345 | // todo: implement --allow-debuggers | ||
346 | BLACKLIST(26), // ptrace | ||
347 | BLACKLIST(283), // kexec_load | ||
348 | BLACKLIST(341), // name_to_handle_at | ||
349 | BLACKLIST(342), // open_by_handle_at | ||
350 | BLACKLIST(127), // create_module | ||
351 | BLACKLIST(128), // init_module | ||
352 | BLACKLIST(350), // finit_module | ||
353 | BLACKLIST(129), // delete_module | ||
354 | BLACKLIST(110), // iopl | ||
355 | BLACKLIST(101), // ioperm | ||
356 | BLACKLIST(289), // ioprio_set | ||
357 | BLACKLIST(87), // swapon | ||
358 | BLACKLIST(115), // swapoff | ||
359 | BLACKLIST(103), // syslog | ||
360 | BLACKLIST(347), // process_vm_readv | ||
361 | BLACKLIST(348), // process_vm_writev | ||
362 | BLACKLIST(135), // sysfs | ||
363 | BLACKLIST(149), // _sysctl | ||
364 | BLACKLIST(124), // adjtimex | ||
365 | BLACKLIST(343), // clock_adjtime | ||
366 | BLACKLIST(253), // lookup_dcookie | ||
367 | BLACKLIST(336), // perf_event_open | ||
368 | BLACKLIST(338), // fanotify_init | ||
369 | BLACKLIST(349), // kcmp | ||
370 | BLACKLIST(286), // add_key | ||
371 | BLACKLIST(287), // request_key | ||
372 | BLACKLIST(288), // keyctl | ||
373 | BLACKLIST(86), // uselib | ||
374 | BLACKLIST(51), // acct | ||
375 | BLACKLIST(123), // modify_ldt | ||
376 | BLACKLIST(217), // pivot_root | ||
377 | BLACKLIST(245), // io_setup | ||
378 | BLACKLIST(246), // io_destroy | ||
379 | BLACKLIST(247), // io_getevents | ||
380 | BLACKLIST(248), // io_submit | ||
381 | BLACKLIST(249), // io_cancel | ||
382 | BLACKLIST(257), // remap_file_pages | ||
383 | BLACKLIST(274), // mbind | ||
384 | BLACKLIST(275), // get_mempolicy | ||
385 | BLACKLIST(276), // set_mempolicy | ||
386 | BLACKLIST(294), // migrate_pages | ||
387 | BLACKLIST(317), // move_pages | ||
388 | BLACKLIST(316), // vmsplice | ||
389 | BLACKLIST(61), // chroot | ||
390 | BLACKLIST(88), // reboot | ||
391 | BLACKLIST(169), // nfsservctl | ||
392 | BLACKLIST(130), // get_kernel_syms | ||
393 | |||
394 | RETURN_ALLOW | ||
395 | }; | ||
396 | 74 | ||
397 | struct sock_fprog prog = { | 75 | // build the seccomp filter as a regular user |
398 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | 76 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, |
399 | .filter = filter, | 77 | PATH_FSECCOMP, "secondary", "32", RUN_SECCOMP_I386); |
400 | }; | 78 | if (rv) |
79 | exit(rv); | ||
401 | 80 | ||
402 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 81 | if (seccomp_load(RUN_SECCOMP_I386) == 0) { |
403 | ; | 82 | if (arg_debug) |
404 | } | 83 | printf("Dual i386/amd64 seccomp filter configured\n"); |
405 | else if (arg_debug) { | ||
406 | printf("Dual i386/amd64 seccomp filter configured\n"); | ||
407 | } | 84 | } |
408 | } | 85 | } |
409 | 86 | ||
410 | // amd64 filter installed on i386 architectures | 87 | // amd64 filter installed on i386 architectures |
411 | void seccomp_filter_64(void) { | 88 | void seccomp_filter_64(void) { |
412 | // hardcoded syscall values | 89 | if (arg_debug) |
413 | struct sock_filter filter[] = { | 90 | printf("Build secondary 64-bit filter\n"); |
414 | VALIDATE_ARCHITECTURE_64, | ||
415 | EXAMINE_SYSCALL, | ||
416 | BLACKLIST(165), // mount | ||
417 | BLACKLIST(166), // umount2 | ||
418 | // todo: implement --allow-debuggers | ||
419 | BLACKLIST(101), // ptrace | ||
420 | BLACKLIST(246), // kexec_load | ||
421 | BLACKLIST(304), // open_by_handle_at | ||
422 | BLACKLIST(303), // name_to_handle_at | ||
423 | BLACKLIST(174), // create_module | ||
424 | BLACKLIST(175), // init_module | ||
425 | BLACKLIST(313), // finit_module | ||
426 | BLACKLIST(176), // delete_module | ||
427 | BLACKLIST(172), // iopl | ||
428 | BLACKLIST(173), // ioperm | ||
429 | BLACKLIST(251), // ioprio_set | ||
430 | BLACKLIST(167), // swapon | ||
431 | BLACKLIST(168), // swapoff | ||
432 | BLACKLIST(103), // syslog | ||
433 | BLACKLIST(310), // process_vm_readv | ||
434 | BLACKLIST(311), // process_vm_writev | ||
435 | BLACKLIST(139), // sysfs | ||
436 | BLACKLIST(156), // _sysctl | ||
437 | BLACKLIST(159), // adjtimex | ||
438 | BLACKLIST(305), // clock_adjtime | ||
439 | BLACKLIST(212), // lookup_dcookie | ||
440 | BLACKLIST(298), // perf_event_open | ||
441 | BLACKLIST(300), // fanotify_init | ||
442 | BLACKLIST(312), // kcmp | ||
443 | BLACKLIST(248), // add_key | ||
444 | BLACKLIST(249), // request_key | ||
445 | BLACKLIST(250), // keyctl | ||
446 | BLACKLIST(134), // uselib | ||
447 | BLACKLIST(163), // acct | ||
448 | BLACKLIST(154), // modify_ldt | ||
449 | BLACKLIST(155), // pivot_root | ||
450 | BLACKLIST(206), // io_setup | ||
451 | BLACKLIST(207), // io_destroy | ||
452 | BLACKLIST(208), // io_getevents | ||
453 | BLACKLIST(209), // io_submit | ||
454 | BLACKLIST(210), // io_cancel | ||
455 | BLACKLIST(216), // remap_file_pages | ||
456 | BLACKLIST(237), // mbind | ||
457 | BLACKLIST(239), // get_mempolicy | ||
458 | BLACKLIST(238), // set_mempolicy | ||
459 | BLACKLIST(256), // migrate_pages | ||
460 | BLACKLIST(279), // move_pages | ||
461 | BLACKLIST(278), // vmsplice | ||
462 | BLACKLIST(161), // chroot | ||
463 | BLACKLIST(184), // tuxcall | ||
464 | BLACKLIST(169), // reboot | ||
465 | BLACKLIST(180), // nfsservctl | ||
466 | BLACKLIST(177), // get_kernel_syms | ||
467 | |||
468 | RETURN_ALLOW | ||
469 | }; | ||
470 | 91 | ||
471 | struct sock_fprog prog = { | 92 | // build the seccomp filter as a regular user |
472 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | 93 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, |
473 | .filter = filter, | 94 | PATH_FSECCOMP, "secondary", "64", RUN_SECCOMP_AMD64); |
474 | }; | 95 | if (rv) |
96 | exit(rv); | ||
475 | 97 | ||
476 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 98 | if (seccomp_load(RUN_SECCOMP_AMD64) == 0) { |
477 | ; | 99 | if (arg_debug) |
478 | } | 100 | printf("Dual i386/amd64 seccomp filter configured\n"); |
479 | else if (arg_debug) { | ||
480 | printf("Dual i386/amd64 seccomp filter configured\n"); | ||
481 | } | 101 | } |
482 | } | 102 | } |
483 | 103 | ||
484 | 104 | ||
485 | // drop filter for seccomp option | 105 | // drop filter for seccomp option |
486 | int seccomp_filter_drop(int enforce_seccomp) { | 106 | int seccomp_filter_drop(int enforce_seccomp) { |
487 | filter_init(); | ||
488 | |||
489 | // default seccomp | 107 | // default seccomp |
490 | if (cfg.seccomp_list_drop == NULL) { | 108 | if (cfg.seccomp_list_drop == NULL && cfg.seccomp_list == NULL) { |
491 | #if defined(__x86_64__) | 109 | #if defined(__x86_64__) |
492 | seccomp_filter_32(); | 110 | seccomp_filter_32(); |
493 | #endif | 111 | #endif |
494 | #if defined(__i386__) | 112 | #if defined(__i386__) |
495 | seccomp_filter_64(); | 113 | seccomp_filter_64(); |
496 | #endif | 114 | #endif |
497 | 115 | if (arg_debug) | |
498 | #ifdef SYS_mount | 116 | printf("Build default seccomp filter\n"); |
499 | filter_add_blacklist(SYS_mount, 0); | 117 | // build the seccomp filter as a regular user |
500 | #endif | 118 | int rv; |
501 | #ifdef SYS_umount2 | 119 | if (arg_allow_debuggers) |
502 | filter_add_blacklist(SYS_umount2, 0); | 120 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, |
503 | #endif | 121 | PATH_FSECCOMP, "default", RUN_SECCOMP_CFG, "allow-debuggers"); |
504 | 122 | else | |
505 | if (!arg_allow_debuggers) { | 123 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, |
506 | #ifdef SYS_ptrace | 124 | PATH_FSECCOMP, "default", RUN_SECCOMP_CFG); |
507 | filter_add_blacklist(SYS_ptrace, 0); | 125 | if (rv) |
508 | #endif | 126 | exit(rv); |
509 | } | ||
510 | |||
511 | #ifdef SYS_kexec_load | ||
512 | filter_add_blacklist(SYS_kexec_load, 0); | ||
513 | #endif | ||
514 | #ifdef SYS_kexec_file_load | ||
515 | filter_add_blacklist(SYS_kexec_file_load, 0); | ||
516 | #endif | ||
517 | #ifdef SYS_open_by_handle_at | ||
518 | filter_add_blacklist(SYS_open_by_handle_at, 0); | ||
519 | #endif | ||
520 | #ifdef SYS_name_to_handle_at | ||
521 | filter_add_blacklist(SYS_name_to_handle_at, 0); | ||
522 | #endif | ||
523 | #ifdef SYS_init_module | ||
524 | filter_add_blacklist(SYS_init_module, 0); | ||
525 | #endif | ||
526 | #ifdef SYS_finit_module // introduced in 2013 | ||
527 | filter_add_blacklist(SYS_finit_module, 0); | ||
528 | #endif | ||
529 | #ifdef SYS_create_module | ||
530 | filter_add_blacklist(SYS_create_module, 0); | ||
531 | #endif | ||
532 | #ifdef SYS_delete_module | ||
533 | filter_add_blacklist(SYS_delete_module, 0); | ||
534 | #endif | ||
535 | #ifdef SYS_iopl | ||
536 | filter_add_blacklist(SYS_iopl, 0); | ||
537 | #endif | ||
538 | #ifdef SYS_ioperm | ||
539 | filter_add_blacklist(SYS_ioperm, 0); | ||
540 | #endif | ||
541 | #ifdef SYS_ioprio_set | ||
542 | filter_add_blacklist(SYS_ioprio_set, 0); | ||
543 | #endif | ||
544 | #ifdef SYS_ni_syscall // new io permissions call on arm devices | ||
545 | filter_add_blacklist(SYS_ni_syscall, 0); | ||
546 | #endif | ||
547 | #ifdef SYS_swapon | ||
548 | filter_add_blacklist(SYS_swapon, 0); | ||
549 | #endif | ||
550 | #ifdef SYS_swapoff | ||
551 | filter_add_blacklist(SYS_swapoff, 0); | ||
552 | #endif | ||
553 | #ifdef SYS_syslog | ||
554 | filter_add_blacklist(SYS_syslog, 0); | ||
555 | #endif | ||
556 | if (!arg_allow_debuggers) { | ||
557 | #ifdef SYS_process_vm_readv | ||
558 | filter_add_blacklist(SYS_process_vm_readv, 0); | ||
559 | #endif | ||
560 | } | ||
561 | |||
562 | #ifdef SYS_process_vm_writev | ||
563 | filter_add_blacklist(SYS_process_vm_writev, 0); | ||
564 | #endif | ||
565 | |||
566 | // mknod removed in 0.9.29 - it brakes Zotero extension | ||
567 | //#ifdef SYS_mknod | ||
568 | // filter_add_blacklist(SYS_mknod, 0); | ||
569 | //#endif | ||
570 | |||
571 | // new syscalls in 0.9,23 | ||
572 | #ifdef SYS_sysfs | ||
573 | filter_add_blacklist(SYS_sysfs, 0); | ||
574 | #endif | ||
575 | #ifdef SYS__sysctl | ||
576 | filter_add_blacklist(SYS__sysctl, 0); | ||
577 | #endif | ||
578 | #ifdef SYS_adjtimex | ||
579 | filter_add_blacklist(SYS_adjtimex, 0); | ||
580 | #endif | ||
581 | #ifdef SYS_clock_adjtime | ||
582 | filter_add_blacklist(SYS_clock_adjtime, 0); | ||
583 | #endif | ||
584 | #ifdef SYS_lookup_dcookie | ||
585 | filter_add_blacklist(SYS_lookup_dcookie, 0); | ||
586 | #endif | ||
587 | #ifdef SYS_perf_event_open | ||
588 | filter_add_blacklist(SYS_perf_event_open, 0); | ||
589 | #endif | ||
590 | #ifdef SYS_fanotify_init | ||
591 | filter_add_blacklist(SYS_fanotify_init, 0); | ||
592 | #endif | ||
593 | #ifdef SYS_kcmp | ||
594 | filter_add_blacklist(SYS_kcmp, 0); | ||
595 | #endif | ||
596 | |||
597 | // 0.9.32 | ||
598 | #ifdef SYS_add_key | ||
599 | filter_add_blacklist(SYS_add_key, 0); | ||
600 | #endif | ||
601 | #ifdef SYS_request_key | ||
602 | filter_add_blacklist(SYS_request_key, 0); | ||
603 | #endif | ||
604 | #ifdef SYS_keyctl | ||
605 | filter_add_blacklist(SYS_keyctl, 0); | ||
606 | #endif | ||
607 | #ifdef SYS_uselib | ||
608 | filter_add_blacklist(SYS_uselib, 0); | ||
609 | #endif | ||
610 | #ifdef SYS_acct | ||
611 | filter_add_blacklist(SYS_acct, 0); | ||
612 | #endif | ||
613 | #ifdef SYS_modify_ldt | ||
614 | filter_add_blacklist(SYS_modify_ldt, 0); | ||
615 | #endif | ||
616 | //#ifdef SYS_unshare | ||
617 | // filter_add_blacklist(SYS_unshare, 0); | ||
618 | //#endif | ||
619 | #ifdef SYS_pivot_root | ||
620 | filter_add_blacklist(SYS_pivot_root, 0); | ||
621 | #endif | ||
622 | //#ifdef SYS_quotactl | ||
623 | // filter_add_blacklist(SYS_quotactl, 0); | ||
624 | //#endif | ||
625 | #ifdef SYS_io_setup | ||
626 | filter_add_blacklist(SYS_io_setup, 0); | ||
627 | #endif | ||
628 | #ifdef SYS_io_destroy | ||
629 | filter_add_blacklist(SYS_io_destroy, 0); | ||
630 | #endif | ||
631 | #ifdef SYS_io_getevents | ||
632 | filter_add_blacklist(SYS_io_getevents, 0); | ||
633 | #endif | ||
634 | #ifdef SYS_io_submit | ||
635 | filter_add_blacklist(SYS_io_submit, 0); | ||
636 | #endif | ||
637 | #ifdef SYS_io_cancel | ||
638 | filter_add_blacklist(SYS_io_cancel, 0); | ||
639 | #endif | ||
640 | #ifdef SYS_remap_file_pages | ||
641 | filter_add_blacklist(SYS_remap_file_pages, 0); | ||
642 | #endif | ||
643 | #ifdef SYS_mbind | ||
644 | filter_add_blacklist(SYS_mbind, 0); | ||
645 | #endif | ||
646 | #ifdef SYS_get_mempolicy | ||
647 | filter_add_blacklist(SYS_get_mempolicy, 0); | ||
648 | #endif | ||
649 | #ifdef SYS_set_mempolicy | ||
650 | filter_add_blacklist(SYS_set_mempolicy, 0); | ||
651 | #endif | ||
652 | #ifdef SYS_migrate_pages | ||
653 | filter_add_blacklist(SYS_migrate_pages, 0); | ||
654 | #endif | ||
655 | #ifdef SYS_move_pages | ||
656 | filter_add_blacklist(SYS_move_pages, 0); | ||
657 | #endif | ||
658 | #ifdef SYS_vmsplice | ||
659 | filter_add_blacklist(SYS_vmsplice, 0); | ||
660 | #endif | ||
661 | #ifdef SYS_chroot | ||
662 | filter_add_blacklist(SYS_chroot, 0); | ||
663 | #endif | ||
664 | //#ifdef SYS_set_robust_list | ||
665 | // filter_add_blacklist(SYS_set_robust_list, 0); | ||
666 | //#endif | ||
667 | //#ifdef SYS_get_robust_list | ||
668 | // filter_add_blacklist(SYS_get_robust_list, 0); | ||
669 | //#endif | ||
670 | |||
671 | // CHECK_SECCOMP(seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(clone), 1, | ||
672 | // SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER))); | ||
673 | |||
674 | // 0.9.39 | ||
675 | #ifdef SYS_tuxcall | ||
676 | filter_add_blacklist(SYS_tuxcall, 0); | ||
677 | #endif | ||
678 | #ifdef SYS_reboot | ||
679 | filter_add_blacklist(SYS_reboot, 0); | ||
680 | #endif | ||
681 | #ifdef SYS_nfsservctl | ||
682 | filter_add_blacklist(SYS_nfsservctl, 0); | ||
683 | #endif | ||
684 | #ifdef SYS_get_kernel_syms | ||
685 | filter_add_blacklist(SYS_get_kernel_syms, 0); | ||
686 | #endif | ||
687 | |||
688 | } | 127 | } |
689 | 128 | ||
690 | // default seccomp filter with additional drop list | 129 | // default seccomp filter with additional drop list |
691 | if (cfg.seccomp_list && cfg.seccomp_list_drop == NULL) { | 130 | else if (cfg.seccomp_list && cfg.seccomp_list_drop == NULL) { |
692 | if (syscall_check_list(cfg.seccomp_list, filter_add_blacklist, 0)) { | 131 | #if defined(__x86_64__) |
693 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | 132 | seccomp_filter_32(); |
133 | #endif | ||
134 | #if defined(__i386__) | ||
135 | seccomp_filter_64(); | ||
136 | #endif | ||
137 | if (arg_debug) | ||
138 | printf("Build default+drop seccomp filter\n"); | ||
139 | if (strlen(cfg.seccomp_list) == 0) { | ||
140 | fprintf(stderr, "Error: empty syscall lists are not allowed\n"); | ||
694 | exit(1); | 141 | exit(1); |
695 | } | 142 | } |
143 | |||
144 | // build the seccomp filter as a regular user | ||
145 | int rv; | ||
146 | if (arg_allow_debuggers) | ||
147 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 6, | ||
148 | PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, cfg.seccomp_list, "allow-debuggers"); | ||
149 | else | ||
150 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, | ||
151 | PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, cfg.seccomp_list); | ||
152 | if (rv) | ||
153 | exit(rv); | ||
696 | } | 154 | } |
697 | // drop list | 155 | |
156 | // drop list without defaults - secondary filters are not installed | ||
698 | else if (cfg.seccomp_list == NULL && cfg.seccomp_list_drop) { | 157 | else if (cfg.seccomp_list == NULL && cfg.seccomp_list_drop) { |
699 | if (syscall_check_list(cfg.seccomp_list_drop, filter_add_blacklist, 0)) { | 158 | if (arg_debug) |
700 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | 159 | printf("Build drop seccomp filter\n"); |
160 | if (strlen(cfg.seccomp_list_drop) == 0) { | ||
161 | fprintf(stderr, "Error: empty syscall lists are not allowed\n"); | ||
701 | exit(1); | 162 | exit(1); |
702 | } | 163 | } |
703 | } | ||
704 | |||
705 | |||
706 | filter_end_blacklist(); | ||
707 | if (arg_debug) | ||
708 | filter_debug(); | ||
709 | |||
710 | // save seccomp filter in /run/firejail/mnt/seccomp | ||
711 | // in order to use it in --join operations | ||
712 | write_seccomp_file(); | ||
713 | |||
714 | |||
715 | struct sock_fprog prog = { | ||
716 | .len = sfilter_index, | ||
717 | .filter = sfilter, | ||
718 | }; | ||
719 | 164 | ||
720 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 165 | // build the seccomp filter as a regular user |
721 | if (enforce_seccomp) { | 166 | int rv; |
722 | fprintf(stderr, "Error: a seccomp-enabled Linux kernel is required, exiting...\n"); | 167 | if (arg_allow_debuggers) |
723 | exit(1); | 168 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, |
724 | } | 169 | PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop, "allow-debuggers"); |
725 | else | 170 | else |
726 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | 171 | rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, |
172 | PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop); | ||
727 | 173 | ||
728 | return 1; | 174 | if (rv) |
175 | exit(rv); | ||
176 | } | ||
177 | else { | ||
178 | assert(0); | ||
729 | } | 179 | } |
730 | 180 | ||
731 | return 0; | 181 | // load the filter |
182 | if (seccomp_load(RUN_SECCOMP_CFG) == 0) { | ||
183 | if (arg_debug) | ||
184 | printf("seccomp filter configured\n"); | ||
185 | } | ||
186 | else if (enforce_seccomp) { | ||
187 | fprintf(stderr, "Error: a seccomp-enabled Linux kernel is required, exiting...\n"); | ||
188 | exit(1); | ||
189 | } | ||
190 | |||
191 | if (arg_debug) | ||
192 | sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, | ||
193 | PATH_FSECCOMP, "print", RUN_SECCOMP_CFG); | ||
194 | |||
195 | return seccomp_load(RUN_SECCOMP_CFG); | ||
732 | } | 196 | } |
733 | 197 | ||
734 | // keep filter for seccomp option | 198 | // keep filter for seccomp option |
735 | int seccomp_filter_keep(void) { | 199 | int seccomp_filter_keep(void) { |
736 | filter_init(); | 200 | if (arg_debug) |
737 | 201 | printf("Build drop seccomp filter\n"); | |
738 | // these 4 syscalls are used by firejail after the seccomp filter is initialized | 202 | if (strlen(cfg.seccomp_list_keep) == 0) { |
739 | filter_add_whitelist(SYS_setuid, 0); | 203 | fprintf(stderr, "Error: empty syscall lists are not allowed\n"); |
740 | filter_add_whitelist(SYS_setgid, 0); | 204 | exit(1); |
741 | filter_add_whitelist(SYS_setgroups, 0); | ||
742 | filter_add_whitelist(SYS_dup, 0); | ||
743 | |||
744 | // apply keep list | ||
745 | if (cfg.seccomp_list_keep) { | ||
746 | if (syscall_check_list(cfg.seccomp_list_keep, filter_add_whitelist, 0)) { | ||
747 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | ||
748 | exit(1); | ||
749 | } | ||
750 | } | 205 | } |
751 | 206 | ||
752 | filter_end_whitelist(); | 207 | // build the seccomp filter as a regular user |
208 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, | ||
209 | PATH_FSECCOMP, "keep", RUN_SECCOMP_CFG, cfg.seccomp_list_keep); | ||
210 | if (rv) | ||
211 | exit(rv); | ||
753 | if (arg_debug) | 212 | if (arg_debug) |
754 | filter_debug(); | 213 | printf("seccomp filter configured\n"); |
755 | |||
756 | // save seccomp filter in /run/firejail/mnt/seccomp | ||
757 | // in order to use it in --join operations | ||
758 | write_seccomp_file(); | ||
759 | |||
760 | 214 | ||
761 | struct sock_fprog prog = { | 215 | |
762 | .len = sfilter_index, | 216 | return seccomp_load(RUN_SECCOMP_CFG); |
763 | .filter = sfilter, | ||
764 | }; | ||
765 | |||
766 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
767 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
768 | return 1; | ||
769 | } | ||
770 | else if (arg_debug) { | ||
771 | printf("seccomp enabled\n"); | ||
772 | } | ||
773 | |||
774 | return 0; | ||
775 | } | 217 | } |
776 | 218 | ||
777 | // errno filter for seccomp option | 219 | // errno filter for seccomp option |
778 | int seccomp_filter_errno(void) { | 220 | int seccomp_filter_errno(void) { |
221 | #if 0 //todo: disabled temporarely, bring it back | ||
779 | int i; | 222 | int i; |
780 | int higest_errno = errno_highest_nr(); | 223 | int higest_errno = errno_highest_nr(); |
781 | filter_init(); | 224 | filter_init(); |
@@ -798,42 +241,11 @@ int seccomp_filter_errno(void) { | |||
798 | // save seccomp filter in /run/firejail/mnt/seccomp | 241 | // save seccomp filter in /run/firejail/mnt/seccomp |
799 | // in order to use it in --join operations | 242 | // in order to use it in --join operations |
800 | write_seccomp_file(); | 243 | write_seccomp_file(); |
801 | 244 | return seccomp_load(RUN_SECCOMP_CFG); | |
802 | struct sock_fprog prog = { | 245 | #else |
803 | .len = sfilter_index, | 246 | printf("*** --seccomp.<errno> is temporarily disabled, it will be brought back soon ***\n"); |
804 | .filter = sfilter, | ||
805 | }; | ||
806 | |||
807 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
808 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
809 | return 1; | ||
810 | } | ||
811 | else if (arg_debug) { | ||
812 | printf("seccomp enabled\n"); | ||
813 | } | ||
814 | |||
815 | return 0; | 247 | return 0; |
816 | } | 248 | #endif |
817 | |||
818 | |||
819 | |||
820 | void seccomp_set(void) { | ||
821 | // read seccomp filter from /runp/firejail/mnt/seccomp | ||
822 | read_seccomp_file(RUN_SECCOMP_CFG); | ||
823 | |||
824 | // apply filter | ||
825 | struct sock_fprog prog = { | ||
826 | .len = sfilter_index, | ||
827 | .filter = sfilter, | ||
828 | }; | ||
829 | |||
830 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
831 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
832 | return; | ||
833 | } | ||
834 | else if (arg_debug) { | ||
835 | printf("seccomp enabled\n"); | ||
836 | } | ||
837 | } | 249 | } |
838 | 250 | ||
839 | void seccomp_print_filter_name(const char *name) { | 251 | void seccomp_print_filter_name(const char *name) { |
@@ -890,10 +302,11 @@ void seccomp_print_filter(pid_t pid) { | |||
890 | exit(1); | 302 | exit(1); |
891 | } | 303 | } |
892 | 304 | ||
893 | // read and print the filter | 305 | // read and print the filter - run this as root, the user doesn't have access |
894 | read_seccomp_file(fname); | 306 | int rv = sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, |
895 | drop_privs(1); | 307 | PATH_FSECCOMP, "print", fname); |
896 | filter_debug(); | 308 | if (rv) |
309 | exit(rv); | ||
897 | free(fname); | 310 | free(fname); |
898 | 311 | ||
899 | exit(0); | 312 | exit(0); |