diff options
author | Matias Wadman <mattias.wadman@gmail.com> | 2015-09-23 22:44:48 +0200 |
---|---|---|
committer | Matias Wadman <mattias.wadman@gmail.com> | 2015-09-23 22:44:48 +0200 |
commit | 081d1fbf2ac9cd547ac68e8494eace3434926f12 (patch) | |
tree | 54ef2b7a1f97f17170a46adc7b0a9d8ed8a7013a | |
parent | profile work (diff) | |
download | firejail-081d1fbf2.tar.gz firejail-081d1fbf2.tar.zst firejail-081d1fbf2.zip |
Add seccomp errno filter support
-rw-r--r-- | src/firejail/errno.c | 214 | ||||
-rw-r--r-- | src/firejail/firejail.h | 9 | ||||
-rw-r--r-- | src/firejail/main.c | 43 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 2 | ||||
-rw-r--r-- | src/firejail/seccomp.c | 144 | ||||
-rw-r--r-- | src/firejail/syscall.c | 6 | ||||
-rw-r--r-- | src/man/firejail.txt | 9 | ||||
-rw-r--r-- | src/tools/extract_errnos.sh | 4 |
8 files changed, 393 insertions, 38 deletions
diff --git a/src/firejail/errno.c b/src/firejail/errno.c new file mode 100644 index 000000000..c6c5f8f6a --- /dev/null +++ b/src/firejail/errno.c | |||
@@ -0,0 +1,214 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 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 | |||
21 | #ifdef HAVE_SECCOMP | ||
22 | #include "firejail.h" | ||
23 | #include <errno.h> | ||
24 | #include <attr/xattr.h> | ||
25 | |||
26 | typedef struct { | ||
27 | char *name; | ||
28 | int nr; | ||
29 | } ErrnoEntry; | ||
30 | |||
31 | static ErrnoEntry errnolist[] = { | ||
32 | // | ||
33 | // code generated using tools/extract-errnos | ||
34 | // | ||
35 | "EPERM", EPERM, | ||
36 | "ENOENT", ENOENT, | ||
37 | "ESRCH", ESRCH, | ||
38 | "EINTR", EINTR, | ||
39 | "EIO", EIO, | ||
40 | "ENXIO", ENXIO, | ||
41 | "E2BIG", E2BIG, | ||
42 | "ENOEXEC", ENOEXEC, | ||
43 | "EBADF", EBADF, | ||
44 | "ECHILD", ECHILD, | ||
45 | "EAGAIN", EAGAIN, | ||
46 | "ENOMEM", ENOMEM, | ||
47 | "EACCES", EACCES, | ||
48 | "EFAULT", EFAULT, | ||
49 | "ENOTBLK", ENOTBLK, | ||
50 | "EBUSY", EBUSY, | ||
51 | "EEXIST", EEXIST, | ||
52 | "EXDEV", EXDEV, | ||
53 | "ENODEV", ENODEV, | ||
54 | "ENOTDIR", ENOTDIR, | ||
55 | "EISDIR", EISDIR, | ||
56 | "EINVAL", EINVAL, | ||
57 | "ENFILE", ENFILE, | ||
58 | "EMFILE", EMFILE, | ||
59 | "ENOTTY", ENOTTY, | ||
60 | "ETXTBSY", ETXTBSY, | ||
61 | "EFBIG", EFBIG, | ||
62 | "ENOSPC", ENOSPC, | ||
63 | "ESPIPE", ESPIPE, | ||
64 | "EROFS", EROFS, | ||
65 | "EMLINK", EMLINK, | ||
66 | "EPIPE", EPIPE, | ||
67 | "EDOM", EDOM, | ||
68 | "ERANGE", ERANGE, | ||
69 | "EDEADLK", EDEADLK, | ||
70 | "ENAMETOOLONG", ENAMETOOLONG, | ||
71 | "ENOLCK", ENOLCK, | ||
72 | "ENOSYS", ENOSYS, | ||
73 | "ENOTEMPTY", ENOTEMPTY, | ||
74 | "ELOOP", ELOOP, | ||
75 | "EWOULDBLOCK", EWOULDBLOCK, | ||
76 | "ENOMSG", ENOMSG, | ||
77 | "EIDRM", EIDRM, | ||
78 | "ECHRNG", ECHRNG, | ||
79 | "EL2NSYNC", EL2NSYNC, | ||
80 | "EL3HLT", EL3HLT, | ||
81 | "EL3RST", EL3RST, | ||
82 | "ELNRNG", ELNRNG, | ||
83 | "EUNATCH", EUNATCH, | ||
84 | "ENOCSI", ENOCSI, | ||
85 | "EL2HLT", EL2HLT, | ||
86 | "EBADE", EBADE, | ||
87 | "EBADR", EBADR, | ||
88 | "EXFULL", EXFULL, | ||
89 | "ENOANO", ENOANO, | ||
90 | "EBADRQC", EBADRQC, | ||
91 | "EBADSLT", EBADSLT, | ||
92 | "EDEADLOCK", EDEADLOCK, | ||
93 | "EBFONT", EBFONT, | ||
94 | "ENOSTR", ENOSTR, | ||
95 | "ENODATA", ENODATA, | ||
96 | "ETIME", ETIME, | ||
97 | "ENOSR", ENOSR, | ||
98 | "ENONET", ENONET, | ||
99 | "ENOPKG", ENOPKG, | ||
100 | "EREMOTE", EREMOTE, | ||
101 | "ENOLINK", ENOLINK, | ||
102 | "EADV", EADV, | ||
103 | "ESRMNT", ESRMNT, | ||
104 | "ECOMM", ECOMM, | ||
105 | "EPROTO", EPROTO, | ||
106 | "EMULTIHOP", EMULTIHOP, | ||
107 | "EDOTDOT", EDOTDOT, | ||
108 | "EBADMSG", EBADMSG, | ||
109 | "EOVERFLOW", EOVERFLOW, | ||
110 | "ENOTUNIQ", ENOTUNIQ, | ||
111 | "EBADFD", EBADFD, | ||
112 | "EREMCHG", EREMCHG, | ||
113 | "ELIBACC", ELIBACC, | ||
114 | "ELIBBAD", ELIBBAD, | ||
115 | "ELIBSCN", ELIBSCN, | ||
116 | "ELIBMAX", ELIBMAX, | ||
117 | "ELIBEXEC", ELIBEXEC, | ||
118 | "EILSEQ", EILSEQ, | ||
119 | "ERESTART", ERESTART, | ||
120 | "ESTRPIPE", ESTRPIPE, | ||
121 | "EUSERS", EUSERS, | ||
122 | "ENOTSOCK", ENOTSOCK, | ||
123 | "EDESTADDRREQ", EDESTADDRREQ, | ||
124 | "EMSGSIZE", EMSGSIZE, | ||
125 | "EPROTOTYPE", EPROTOTYPE, | ||
126 | "ENOPROTOOPT", ENOPROTOOPT, | ||
127 | "EPROTONOSUPPORT", EPROTONOSUPPORT, | ||
128 | "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT, | ||
129 | "EOPNOTSUPP", EOPNOTSUPP, | ||
130 | "EPFNOSUPPORT", EPFNOSUPPORT, | ||
131 | "EAFNOSUPPORT", EAFNOSUPPORT, | ||
132 | "EADDRINUSE", EADDRINUSE, | ||
133 | "EADDRNOTAVAIL", EADDRNOTAVAIL, | ||
134 | "ENETDOWN", ENETDOWN, | ||
135 | "ENETUNREACH", ENETUNREACH, | ||
136 | "ENETRESET", ENETRESET, | ||
137 | "ECONNABORTED", ECONNABORTED, | ||
138 | "ECONNRESET", ECONNRESET, | ||
139 | "ENOBUFS", ENOBUFS, | ||
140 | "EISCONN", EISCONN, | ||
141 | "ENOTCONN", ENOTCONN, | ||
142 | "ESHUTDOWN", ESHUTDOWN, | ||
143 | "ETOOMANYREFS", ETOOMANYREFS, | ||
144 | "ETIMEDOUT", ETIMEDOUT, | ||
145 | "ECONNREFUSED", ECONNREFUSED, | ||
146 | "EHOSTDOWN", EHOSTDOWN, | ||
147 | "EHOSTUNREACH", EHOSTUNREACH, | ||
148 | "EALREADY", EALREADY, | ||
149 | "EINPROGRESS", EINPROGRESS, | ||
150 | "ESTALE", ESTALE, | ||
151 | "EUCLEAN", EUCLEAN, | ||
152 | "ENOTNAM", ENOTNAM, | ||
153 | "ENAVAIL", ENAVAIL, | ||
154 | "EISNAM", EISNAM, | ||
155 | "EREMOTEIO", EREMOTEIO, | ||
156 | "EDQUOT", EDQUOT, | ||
157 | "ENOMEDIUM", ENOMEDIUM, | ||
158 | "EMEDIUMTYPE", EMEDIUMTYPE, | ||
159 | "ECANCELED", ECANCELED, | ||
160 | "ENOKEY", ENOKEY, | ||
161 | "EKEYEXPIRED", EKEYEXPIRED, | ||
162 | "EKEYREVOKED", EKEYREVOKED, | ||
163 | "EKEYREJECTED", EKEYREJECTED, | ||
164 | "EOWNERDEAD", EOWNERDEAD, | ||
165 | "ENOTRECOVERABLE", ENOTRECOVERABLE, | ||
166 | "ERFKILL", ERFKILL, | ||
167 | "EHWPOISON", EHWPOISON, | ||
168 | "ENOTSUP", ENOTSUP, | ||
169 | "ENOATTR", ENOATTR, | ||
170 | }; | ||
171 | |||
172 | int errno_highest_nr(void) { | ||
173 | int i, max = 0; | ||
174 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
175 | for (i = 0; i < elems; i++) { | ||
176 | if (errnolist[i].nr > max) | ||
177 | max = errnolist[i].nr; | ||
178 | } | ||
179 | |||
180 | return max; | ||
181 | } | ||
182 | |||
183 | int errno_find_name(const char *name) { | ||
184 | int i; | ||
185 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
186 | for (i = 0; i < elems; i++) { | ||
187 | if (strcasecmp(name, errnolist[i].name) == 0) | ||
188 | return errnolist[i].nr; | ||
189 | } | ||
190 | |||
191 | return -1; | ||
192 | } | ||
193 | |||
194 | char *errno_find_nr(int nr) { | ||
195 | int i; | ||
196 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
197 | for (i = 0; i < elems; i++) { | ||
198 | if (nr == errnolist[i].nr) | ||
199 | return errnolist[i].name; | ||
200 | } | ||
201 | |||
202 | return "unknown"; | ||
203 | } | ||
204 | |||
205 | void errno_print(void) { | ||
206 | int i; | ||
207 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
208 | for (i = 0; i < elems; i++) { | ||
209 | printf("%d\t- %s\n", errnolist[i].nr, errnolist[i].name); | ||
210 | } | ||
211 | printf("\n"); | ||
212 | } | ||
213 | |||
214 | #endif // HAVE_SECCOMP | ||
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index cef4b673c..589fdb42a 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -147,6 +147,7 @@ extern int arg_seccomp; // enable default seccomp filter | |||
147 | extern char *arg_seccomp_list;// optional seccomp list on top of default filter | 147 | extern char *arg_seccomp_list;// optional seccomp list on top of default filter |
148 | extern char *arg_seccomp_list_drop; // seccomp drop list | 148 | extern char *arg_seccomp_list_drop; // seccomp drop list |
149 | extern char *arg_seccomp_list_keep; // seccomp keep list | 149 | extern char *arg_seccomp_list_keep; // seccomp keep list |
150 | extern char **arg_seccomp_list_errno; // seccomp errno[nr] lists | ||
150 | 151 | ||
151 | extern int arg_caps_default_filter; // enable default capabilities filter | 152 | extern int arg_caps_default_filter; // enable default capabilities filter |
152 | extern int arg_caps_drop; // drop list | 153 | extern int arg_caps_drop; // drop list |
@@ -335,7 +336,7 @@ void caps_print_filter_name(const char *name); | |||
335 | // syscall.c | 336 | // syscall.c |
336 | const char *syscall_find_nr(int nr); | 337 | const char *syscall_find_nr(int nr); |
337 | // return -1 if error, 0 if no error | 338 | // return -1 if error, 0 if no error |
338 | int syscall_check_list(const char *slist, void (*callback)(int)); | 339 | int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg), int arg); |
339 | // print all available syscalls | 340 | // print all available syscalls |
340 | void syscall_print(void); | 341 | void syscall_print(void); |
341 | 342 | ||
@@ -392,5 +393,11 @@ void env_apply(void); | |||
392 | // fs_whitelist.c | 393 | // fs_whitelist.c |
393 | void fs_whitelist(void); | 394 | void fs_whitelist(void); |
394 | 395 | ||
396 | // errno.c | ||
397 | int errno_highest_errno(void); | ||
398 | int errno_find_name(const char *name); | ||
399 | char *errno_find_nr(int nr); | ||
400 | void errno_print(void); | ||
401 | |||
395 | #endif | 402 | #endif |
396 | 403 | ||
diff --git a/src/firejail/main.c b/src/firejail/main.c index 9d94630ef..ea04ea73f 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -61,6 +61,7 @@ int arg_seccomp = 0; // enable default seccomp filter | |||
61 | char *arg_seccomp_list = NULL; // optional seccomp list on top of default filter | 61 | char *arg_seccomp_list = NULL; // optional seccomp list on top of default filter |
62 | char *arg_seccomp_list_drop = NULL; // seccomp drop list | 62 | char *arg_seccomp_list_drop = NULL; // seccomp drop list |
63 | char *arg_seccomp_list_keep = NULL; // seccomp keep list | 63 | char *arg_seccomp_list_keep = NULL; // seccomp keep list |
64 | char **arg_seccomp_list_errno = NULL; // seccomp errno[nr] lists | ||
64 | 65 | ||
65 | int arg_caps_default_filter = 0; // enable default capabilities filter | 66 | int arg_caps_default_filter = 0; // enable default capabilities filter |
66 | int arg_caps_drop = 0; // drop list | 67 | int arg_caps_drop = 0; // drop list |
@@ -302,6 +303,10 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
302 | syscall_print(); | 303 | syscall_print(); |
303 | exit(0); | 304 | exit(0); |
304 | } | 305 | } |
306 | else if (strcmp(argv[i], "--debug-errnos") == 0) { | ||
307 | errno_print(); | ||
308 | exit(0); | ||
309 | } | ||
305 | else if (strncmp(argv[i], "--seccomp.print=", 16) == 0) { | 310 | else if (strncmp(argv[i], "--seccomp.print=", 16) == 0) { |
306 | // join sandbox by pid or by name | 311 | // join sandbox by pid or by name |
307 | pid_t pid; | 312 | pid_t pid; |
@@ -387,6 +392,7 @@ int main(int argc, char **argv) { | |||
387 | int arg_cgroup = 0; | 392 | int arg_cgroup = 0; |
388 | int custom_profile = 0; // custom profile loaded | 393 | int custom_profile = 0; // custom profile loaded |
389 | int arg_noprofile = 0; // use generic.profile if none other found/specified | 394 | int arg_noprofile = 0; // use generic.profile if none other found/specified |
395 | int highest_errno = errno_highest_nr(); | ||
390 | 396 | ||
391 | // check if we already have a sandbox running | 397 | // check if we already have a sandbox running |
392 | int rv = check_kernel_procs(); | 398 | int rv = check_kernel_procs(); |
@@ -478,6 +484,34 @@ int main(int argc, char **argv) { | |||
478 | if (!arg_seccomp_list_keep) | 484 | if (!arg_seccomp_list_keep) |
479 | errExit("strdup"); | 485 | errExit("strdup"); |
480 | } | 486 | } |
487 | else if (strncmp(argv[i], "--seccomp.e", 11) == 0 && strchr(argv[i], '=')) { | ||
488 | if (arg_seccomp && !arg_seccomp_list_errno) { | ||
489 | fprintf(stderr, "Error: seccomp already enabled\n"); | ||
490 | exit(1); | ||
491 | } | ||
492 | char *eq = strchr(argv[i], '='); | ||
493 | char *errnoname = strndup(argv[i] + 10, eq - (argv[i] + 10)); | ||
494 | int nr = errno_find_name(errnoname); | ||
495 | if (nr == -1) { | ||
496 | fprintf(stderr, "Error: unknown errno %s\n", errnoname); | ||
497 | free(errnoname); | ||
498 | exit(1); | ||
499 | } | ||
500 | |||
501 | if (!arg_seccomp_list_errno) | ||
502 | arg_seccomp_list_errno = calloc(highest_errno+1, sizeof(arg_seccomp_list_errno[0])); | ||
503 | |||
504 | if (arg_seccomp_list_errno[nr]) { | ||
505 | fprintf(stderr, "Error: errno %s already configured\n", errnoname); | ||
506 | free(errnoname); | ||
507 | exit(1); | ||
508 | } | ||
509 | arg_seccomp = 1; | ||
510 | arg_seccomp_list_errno[nr] = strdup(eq+1); | ||
511 | if (!arg_seccomp_list_errno[nr]) | ||
512 | errExit("strdup"); | ||
513 | free(errnoname); | ||
514 | } | ||
481 | #endif | 515 | #endif |
482 | else if (strcmp(argv[i], "--caps") == 0) | 516 | else if (strcmp(argv[i], "--caps") == 0) |
483 | arg_caps_default_filter = 1; | 517 | arg_caps_default_filter = 1; |
@@ -1288,6 +1322,15 @@ int main(int argc, char **argv) { | |||
1288 | 1322 | ||
1289 | // wait for the child to finish | 1323 | // wait for the child to finish |
1290 | waitpid(child, NULL, 0); | 1324 | waitpid(child, NULL, 0); |
1325 | |||
1326 | // free globals | ||
1327 | if (arg_seccomp_list_errno) { | ||
1328 | for (i = 0; i < highest_errno; i++) | ||
1329 | free(arg_seccomp_list_errno[i]); | ||
1330 | free(arg_seccomp_list_errno); | ||
1331 | } | ||
1332 | |||
1291 | myexit(0); | 1333 | myexit(0); |
1334 | |||
1292 | return 0; | 1335 | return 0; |
1293 | } | 1336 | } |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index c1a6d92ec..ddfcc8404 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -410,6 +410,8 @@ int sandbox(void* sandbox_arg) { | |||
410 | if (arg_seccomp == 1) { | 410 | if (arg_seccomp == 1) { |
411 | if (arg_seccomp_list_keep) | 411 | if (arg_seccomp_list_keep) |
412 | seccomp_filter_keep(); // this will also save the fmyilter to MNT_DIR/seccomp file | 412 | seccomp_filter_keep(); // this will also save the fmyilter to MNT_DIR/seccomp file |
413 | else if (arg_seccomp_list_errno) | ||
414 | seccomp_filter_errno(); // this will also save the filter to MNT_DIR/seccomp file | ||
413 | else | 415 | else |
414 | seccomp_filter_drop(); // this will also save the filter to MNT_DIR/seccomp file | 416 | seccomp_filter_drop(); // this will also save the filter to MNT_DIR/seccomp file |
415 | } | 417 | } |
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 47988dbf4..4f6b7b326 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c | |||
@@ -109,6 +109,10 @@ struct seccomp_data { | |||
109 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ | 109 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ |
110 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) | 110 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) |
111 | 111 | ||
112 | #define ERRNO(syscall_nr, nr) \ | ||
113 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ | ||
114 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr) | ||
115 | |||
112 | #define RETURN_ALLOW \ | 116 | #define RETURN_ALLOW \ |
113 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) | 117 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) |
114 | 118 | ||
@@ -157,6 +161,11 @@ void filter_debug(void) { | |||
157 | printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr)); | 161 | printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr)); |
158 | i += 2; | 162 | i += 2; |
159 | } | 163 | } |
164 | else if (*ptr == 0x15 && *(ptr +14) == 0x5 && *(ptr + 15) == 0) { | ||
165 | int err = *(ptr + 13) << 8 | *(ptr + 12); | ||
166 | printf(" ERRNO %d %s %d %s\n", *nr, syscall_find_nr(*nr), err, errno_find_nr(err)); | ||
167 | i += 2; | ||
168 | } | ||
160 | else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) { | 169 | else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) { |
161 | printf(" KILL_PROCESS\n"); | 170 | printf(" KILL_PROCESS\n"); |
162 | i++; | 171 | i++; |
@@ -216,7 +225,7 @@ static void filter_realloc(void) { | |||
216 | sfilter_alloc_size += SECSIZE; | 225 | sfilter_alloc_size += SECSIZE; |
217 | } | 226 | } |
218 | 227 | ||
219 | static void filter_add_whitelist(int syscall) { | 228 | static void filter_add_whitelist(int syscall, int arg) { |
220 | assert(sfilter); | 229 | assert(sfilter); |
221 | assert(sfilter_alloc_size); | 230 | assert(sfilter_alloc_size); |
222 | assert(sfilter_index); | 231 | assert(sfilter_index); |
@@ -242,7 +251,7 @@ static void filter_add_whitelist(int syscall) { | |||
242 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | 251 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); |
243 | } | 252 | } |
244 | 253 | ||
245 | static void filter_add_blacklist(int syscall) { | 254 | static void filter_add_blacklist(int syscall, int arg) { |
246 | assert(sfilter); | 255 | assert(sfilter); |
247 | assert(sfilter_alloc_size); | 256 | assert(sfilter_alloc_size); |
248 | assert(sfilter_index); | 257 | assert(sfilter_index); |
@@ -268,6 +277,32 @@ static void filter_add_blacklist(int syscall) { | |||
268 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | 277 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); |
269 | } | 278 | } |
270 | 279 | ||
280 | static void filter_add_errno(int syscall, int arg) { | ||
281 | assert(sfilter); | ||
282 | assert(sfilter_alloc_size); | ||
283 | assert(sfilter_index); | ||
284 | // if (arg_debug) | ||
285 | // printf("Errno syscall %d %d %s\n", syscall, arg, syscall_find_nr(syscall)); | ||
286 | |||
287 | if ((sfilter_index + 2) > sfilter_alloc_size) | ||
288 | filter_realloc(); | ||
289 | |||
290 | struct sock_filter filter[] = { | ||
291 | ERRNO(syscall, arg) | ||
292 | }; | ||
293 | #if 0 | ||
294 | { | ||
295 | int i; | ||
296 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
297 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
298 | printf("%x, ", (*ptr) & 0xff); | ||
299 | printf("\n"); | ||
300 | } | ||
301 | #endif | ||
302 | memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); | ||
303 | sfilter_index += sizeof(filter) / sizeof(struct sock_filter); | ||
304 | } | ||
305 | |||
271 | static void filter_end_blacklist(void) { | 306 | static void filter_end_blacklist(void) { |
272 | assert(sfilter); | 307 | assert(sfilter); |
273 | assert(sfilter_alloc_size); | 308 | assert(sfilter_alloc_size); |
@@ -405,96 +440,96 @@ int seccomp_filter_drop(void) { | |||
405 | // default seccomp | 440 | // default seccomp |
406 | if (arg_seccomp_list_drop == NULL) { | 441 | if (arg_seccomp_list_drop == NULL) { |
407 | #ifdef SYS_mount | 442 | #ifdef SYS_mount |
408 | filter_add_blacklist(SYS_mount); | 443 | filter_add_blacklist(SYS_mount, 0); |
409 | #endif | 444 | #endif |
410 | #ifdef SYS_umount2 | 445 | #ifdef SYS_umount2 |
411 | filter_add_blacklist(SYS_umount2); | 446 | filter_add_blacklist(SYS_umount2, 0); |
412 | #endif | 447 | #endif |
413 | #ifdef SYS_ptrace | 448 | #ifdef SYS_ptrace |
414 | filter_add_blacklist(SYS_ptrace); | 449 | filter_add_blacklist(SYS_ptrace, 0); |
415 | #endif | 450 | #endif |
416 | #ifdef SYS_kexec_load | 451 | #ifdef SYS_kexec_load |
417 | filter_add_blacklist(SYS_kexec_load); | 452 | filter_add_blacklist(SYS_kexec_load, 0); |
418 | #endif | 453 | #endif |
419 | #ifdef SYS_open_by_handle_at | 454 | #ifdef SYS_open_by_handle_at |
420 | filter_add_blacklist(SYS_open_by_handle_at); | 455 | filter_add_blacklist(SYS_open_by_handle_at, 0); |
421 | #endif | 456 | #endif |
422 | #ifdef SYS_init_module | 457 | #ifdef SYS_init_module |
423 | filter_add_blacklist(SYS_init_module); | 458 | filter_add_blacklist(SYS_init_module, 0); |
424 | #endif | 459 | #endif |
425 | #ifdef SYS_finit_module // introduced in 2013 | 460 | #ifdef SYS_finit_module // introduced in 2013 |
426 | filter_add_blacklist(SYS_finit_module); | 461 | filter_add_blacklist(SYS_finit_module, 0); |
427 | #endif | 462 | #endif |
428 | #ifdef SYS_delete_module | 463 | #ifdef SYS_delete_module |
429 | filter_add_blacklist(SYS_delete_module); | 464 | filter_add_blacklist(SYS_delete_module, 0); |
430 | #endif | 465 | #endif |
431 | #ifdef SYS_iopl | 466 | #ifdef SYS_iopl |
432 | filter_add_blacklist(SYS_iopl); | 467 | filter_add_blacklist(SYS_iopl, 0); |
433 | #endif | 468 | #endif |
434 | #ifdef SYS_ioperm | 469 | #ifdef SYS_ioperm |
435 | filter_add_blacklist(SYS_ioperm); | 470 | filter_add_blacklist(SYS_ioperm, 0); |
436 | #endif | 471 | #endif |
437 | #ifdef SYS_ni_syscall // new io permisions call on arm devices | 472 | #ifdef SYS_ni_syscall // new io permisions call on arm devices |
438 | filter_add_blacklist(SYS_ni_syscall); | 473 | filter_add_blacklist(SYS_ni_syscall, 0); |
439 | #endif | 474 | #endif |
440 | #ifdef SYS_swapon | 475 | #ifdef SYS_swapon |
441 | filter_add_blacklist(SYS_swapon); | 476 | filter_add_blacklist(SYS_swapon, 0); |
442 | #endif | 477 | #endif |
443 | #ifdef SYS_swapoff | 478 | #ifdef SYS_swapoff |
444 | filter_add_blacklist(SYS_swapoff); | 479 | filter_add_blacklist(SYS_swapoff, 0); |
445 | #endif | 480 | #endif |
446 | #ifdef SYS_syslog | 481 | #ifdef SYS_syslog |
447 | filter_add_blacklist(SYS_syslog); | 482 | filter_add_blacklist(SYS_syslog, 0); |
448 | #endif | 483 | #endif |
449 | #ifdef SYS_process_vm_readv | 484 | #ifdef SYS_process_vm_readv |
450 | filter_add_blacklist(SYS_process_vm_readv); | 485 | filter_add_blacklist(SYS_process_vm_readv, 0); |
451 | #endif | 486 | #endif |
452 | #ifdef SYS_process_vm_writev | 487 | #ifdef SYS_process_vm_writev |
453 | filter_add_blacklist(SYS_process_vm_writev); | 488 | filter_add_blacklist(SYS_process_vm_writev, 0); |
454 | #endif | 489 | #endif |
455 | 490 | ||
456 | // mknod removed in 0.9.29 | 491 | // mknod removed in 0.9.29 |
457 | //#ifdef SYS_mknod | 492 | //#ifdef SYS_mknod |
458 | // filter_add_blacklist(SYS_mknod); | 493 | // filter_add_blacklist(SYS_mknod, 0); |
459 | //#endif | 494 | //#endif |
460 | 495 | ||
461 | // new syscalls in 0.9,23 | 496 | // new syscalls in 0.9,23 |
462 | #ifdef SYS_sysfs | 497 | #ifdef SYS_sysfs |
463 | filter_add_blacklist(SYS_sysfs); | 498 | filter_add_blacklist(SYS_sysfs, 0); |
464 | #endif | 499 | #endif |
465 | #ifdef SYS__sysctl | 500 | #ifdef SYS__sysctl |
466 | filter_add_blacklist(SYS__sysctl); | 501 | filter_add_blacklist(SYS__sysctl, 0); |
467 | #endif | 502 | #endif |
468 | #ifdef SYS_adjtimex | 503 | #ifdef SYS_adjtimex |
469 | filter_add_blacklist(SYS_adjtimex); | 504 | filter_add_blacklist(SYS_adjtimex, 0); |
470 | #endif | 505 | #endif |
471 | #ifdef SYS_clock_adjtime | 506 | #ifdef SYS_clock_adjtime |
472 | filter_add_blacklist(SYS_clock_adjtime); | 507 | filter_add_blacklist(SYS_clock_adjtime, 0); |
473 | #endif | 508 | #endif |
474 | #ifdef SYS_lookup_dcookie | 509 | #ifdef SYS_lookup_dcookie |
475 | filter_add_blacklist(SYS_lookup_dcookie); | 510 | filter_add_blacklist(SYS_lookup_dcookie, 0); |
476 | #endif | 511 | #endif |
477 | #ifdef SYS_perf_event_open | 512 | #ifdef SYS_perf_event_open |
478 | filter_add_blacklist(SYS_perf_event_open); | 513 | filter_add_blacklist(SYS_perf_event_open, 0); |
479 | #endif | 514 | #endif |
480 | #ifdef SYS_fanotify_init | 515 | #ifdef SYS_fanotify_init |
481 | filter_add_blacklist(SYS_fanotify_init); | 516 | filter_add_blacklist(SYS_fanotify_init, 0); |
482 | #endif | 517 | #endif |
483 | #ifdef SYS_kcmp | 518 | #ifdef SYS_kcmp |
484 | filter_add_blacklist(SYS_kcmp); | 519 | filter_add_blacklist(SYS_kcmp, 0); |
485 | #endif | 520 | #endif |
486 | } | 521 | } |
487 | 522 | ||
488 | // default seccomp filter with additional drop list | 523 | // default seccomp filter with additional drop list |
489 | if (arg_seccomp_list && arg_seccomp_list_drop == NULL) { | 524 | if (arg_seccomp_list && arg_seccomp_list_drop == NULL) { |
490 | if (syscall_check_list(arg_seccomp_list, filter_add_blacklist)) { | 525 | if (syscall_check_list(arg_seccomp_list, filter_add_blacklist, 0)) { |
491 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | 526 | fprintf(stderr, "Error: cannot load seccomp filter\n"); |
492 | exit(1); | 527 | exit(1); |
493 | } | 528 | } |
494 | } | 529 | } |
495 | // drop list | 530 | // drop list |
496 | else if (arg_seccomp_list == NULL && arg_seccomp_list_drop) { | 531 | else if (arg_seccomp_list == NULL && arg_seccomp_list_drop) { |
497 | if (syscall_check_list(arg_seccomp_list_drop, filter_add_blacklist)) { | 532 | if (syscall_check_list(arg_seccomp_list_drop, filter_add_blacklist, 0)) { |
498 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | 533 | fprintf(stderr, "Error: cannot load seccomp filter\n"); |
499 | exit(1); | 534 | exit(1); |
500 | } | 535 | } |
@@ -531,14 +566,14 @@ int seccomp_filter_keep(void) { | |||
531 | filter_init(); | 566 | filter_init(); |
532 | 567 | ||
533 | // these 4 syscalls are used by firejail after the seccomp filter is initialized | 568 | // these 4 syscalls are used by firejail after the seccomp filter is initialized |
534 | filter_add_whitelist(SYS_setuid); | 569 | filter_add_whitelist(SYS_setuid, 0); |
535 | filter_add_whitelist(SYS_setgid); | 570 | filter_add_whitelist(SYS_setgid, 0); |
536 | filter_add_whitelist(SYS_setgroups); | 571 | filter_add_whitelist(SYS_setgroups, 0); |
537 | filter_add_whitelist(SYS_dup); | 572 | filter_add_whitelist(SYS_dup, 0); |
538 | 573 | ||
539 | // apply keep list | 574 | // apply keep list |
540 | if (arg_seccomp_list_keep) { | 575 | if (arg_seccomp_list_keep) { |
541 | if (syscall_check_list(arg_seccomp_list_keep, filter_add_whitelist)) { | 576 | if (syscall_check_list(arg_seccomp_list_keep, filter_add_whitelist, 0)) { |
542 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | 577 | fprintf(stderr, "Error: cannot load seccomp filter\n"); |
543 | exit(1); | 578 | exit(1); |
544 | } | 579 | } |
@@ -569,6 +604,47 @@ int seccomp_filter_keep(void) { | |||
569 | return 0; | 604 | return 0; |
570 | } | 605 | } |
571 | 606 | ||
607 | // errno filter for seccomp option | ||
608 | int seccomp_filter_errno(void) { | ||
609 | int i; | ||
610 | int higest_errno = errno_highest_nr(); | ||
611 | filter_init(); | ||
612 | |||
613 | // apply errno list | ||
614 | |||
615 | for (i = 0; i < higest_errno; i++) { | ||
616 | if (arg_seccomp_list_errno[i]) { | ||
617 | if (syscall_check_list(arg_seccomp_list_errno[i], filter_add_errno, i)) { | ||
618 | fprintf(stderr, "Error: cannot load seccomp filter\n"); | ||
619 | exit(1); | ||
620 | } | ||
621 | } | ||
622 | } | ||
623 | |||
624 | filter_end_blacklist(); | ||
625 | if (arg_debug) | ||
626 | filter_debug(); | ||
627 | |||
628 | // save seccomp filter in /tmp/firejail/mnt/seccomp | ||
629 | // in order to use it in --join operations | ||
630 | write_seccomp_file(); | ||
631 | |||
632 | struct sock_fprog prog = { | ||
633 | .len = sfilter_index, | ||
634 | .filter = sfilter, | ||
635 | }; | ||
636 | |||
637 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
638 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
639 | return 1; | ||
640 | } | ||
641 | else if (arg_debug) { | ||
642 | printf("seccomp enabled\n"); | ||
643 | } | ||
644 | |||
645 | return 0; | ||
646 | } | ||
647 | |||
572 | 648 | ||
573 | 649 | ||
574 | void seccomp_set(void) { | 650 | void seccomp_set(void) { |
diff --git a/src/firejail/syscall.c b/src/firejail/syscall.c index a74a00662..2ae3da100 100644 --- a/src/firejail/syscall.c +++ b/src/firejail/syscall.c | |||
@@ -4889,7 +4889,7 @@ static int syscall_find_name(const char *name) { | |||
4889 | } | 4889 | } |
4890 | 4890 | ||
4891 | // return 1 if error, 0 if OK | 4891 | // return 1 if error, 0 if OK |
4892 | int syscall_check_list(const char *slist, void (*callback)(int)) { | 4892 | int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg), int arg) { |
4893 | // don't allow empty lists | 4893 | // don't allow empty lists |
4894 | if (slist == NULL || *slist == '\0') { | 4894 | if (slist == NULL || *slist == '\0') { |
4895 | fprintf(stderr, "Error: empty syscall lists are not allowed\n"); | 4895 | fprintf(stderr, "Error: empty syscall lists are not allowed\n"); |
@@ -4912,7 +4912,7 @@ int syscall_check_list(const char *slist, void (*callback)(int)) { | |||
4912 | if (nr == -1) | 4912 | if (nr == -1) |
4913 | fprintf(stderr, "Warning: syscall %s not found\n", start); | 4913 | fprintf(stderr, "Warning: syscall %s not found\n", start); |
4914 | else if (callback != NULL) | 4914 | else if (callback != NULL) |
4915 | callback(nr); | 4915 | callback(nr, arg); |
4916 | 4916 | ||
4917 | start = ptr + 1; | 4917 | start = ptr + 1; |
4918 | } | 4918 | } |
@@ -4923,7 +4923,7 @@ int syscall_check_list(const char *slist, void (*callback)(int)) { | |||
4923 | if (nr == -1) | 4923 | if (nr == -1) |
4924 | fprintf(stderr, "Warning: syscall %s not found\n", start); | 4924 | fprintf(stderr, "Warning: syscall %s not found\n", start); |
4925 | else if (callback != NULL) | 4925 | else if (callback != NULL) |
4926 | callback(nr); | 4926 | callback(nr, arg); |
4927 | } | 4927 | } |
4928 | 4928 | ||
4929 | free(str); | 4929 | free(str); |
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 3f4fba00e..3f22a1d2a 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -845,6 +845,15 @@ Example: | |||
845 | .br | 845 | .br |
846 | $ firejail \-\-shell=none \-\-seccomp.keep=poll,select,[...] transmission-gtk | 846 | $ firejail \-\-shell=none \-\-seccomp.keep=poll,select,[...] transmission-gtk |
847 | .TP | 847 | .TP |
848 | \fB\-\-seccomp.<errno>=syscall,syscall,syscall | ||
849 | Enable seccomp filter, and return errno for the syscalls specified by the command. | ||
850 | .br | ||
851 | |||
852 | .br | ||
853 | Example: | ||
854 | .br | ||
855 | $ firejail \-\-shell=none \-\-seccomp.einval=kill kill 1 | ||
856 | .TP | ||
848 | \fB\-\-seccomp.print=name | 857 | \fB\-\-seccomp.print=name |
849 | Print the seccomp filter for the sandbox started using \-\-name option. | 858 | Print the seccomp filter for the sandbox started using \-\-name option. |
850 | .br | 859 | .br |
diff --git a/src/tools/extract_errnos.sh b/src/tools/extract_errnos.sh new file mode 100644 index 000000000..43b225828 --- /dev/null +++ b/src/tools/extract_errnos.sh | |||
@@ -0,0 +1,4 @@ | |||
1 | echo -e "#include <errno.h>\n#include <attr/xattr.h>" | \ | ||
2 | cpp -dD | \ | ||
3 | grep "^#define E" | \ | ||
4 | sed -e '{s/#define \(.*\) .*/\t"\1", \1,/g}' | ||