]>
Commit | Line | Data |
---|---|---|
1 | /** | |
2 | * Enhanced Seccomp x86 Specific Code | |
3 | * | |
4 | * Copyright (c) 2012,2016 Red Hat <pmoore@redhat.com> | |
5 | * Author: Paul Moore <paul@paul-moore.com> | |
6 | */ | |
7 | ||
8 | /* | |
9 | * This library is free software; you can redistribute it and/or modify it | |
10 | * under the terms of version 2.1 of the GNU Lesser General Public License as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This library is distributed in the hope that it will be useful, but WITHOUT | |
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License | |
16 | * for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Lesser General Public License | |
19 | * along with this library; if not, see <http://www.gnu.org/licenses>. | |
20 | */ | |
21 | ||
22 | #include <stdlib.h> | |
23 | #include <errno.h> | |
24 | #include <string.h> | |
25 | #include <linux/audit.h> | |
26 | ||
27 | #include "arch.h" | |
28 | #include "arch-x86.h" | |
29 | ||
30 | /* x86 syscall numbers */ | |
31 | #define __x86_NR_socketcall 102 | |
32 | #define __x86_NR_ipc 117 | |
33 | ||
34 | const struct arch_def arch_def_x86 = { | |
35 | .token = SCMP_ARCH_X86, | |
36 | .token_bpf = AUDIT_ARCH_I386, | |
37 | .size = ARCH_SIZE_32, | |
38 | .endian = ARCH_ENDIAN_LITTLE, | |
39 | .syscall_resolve_name = x86_syscall_resolve_name, | |
40 | .syscall_resolve_num = x86_syscall_resolve_num, | |
41 | .syscall_rewrite = x86_syscall_rewrite, | |
42 | .rule_add = x86_rule_add, | |
43 | }; | |
44 | ||
45 | /** | |
46 | * Convert a multiplexed pseudo socket syscall into a direct syscall | |
47 | * @param socketcall the multiplexed pseudo syscall number | |
48 | * | |
49 | * Return the related direct syscall number, __NR_SCMP_UNDEF is there is | |
50 | * no related syscall, or __NR_SCMP_ERROR otherwise. | |
51 | * | |
52 | */ | |
53 | int _x86_sock_demux(int socketcall) | |
54 | { | |
55 | switch (socketcall) { | |
56 | case -101: | |
57 | /* socket */ | |
58 | return 359; | |
59 | case -102: | |
60 | /* bind */ | |
61 | return 361; | |
62 | case -103: | |
63 | /* connect */ | |
64 | return 362; | |
65 | case -104: | |
66 | /* listen */ | |
67 | return 363; | |
68 | case -105: | |
69 | /* accept - not defined */ | |
70 | return __NR_SCMP_UNDEF; | |
71 | case -106: | |
72 | /* getsockname */ | |
73 | return 367; | |
74 | case -107: | |
75 | /* getpeername */ | |
76 | return 368; | |
77 | case -108: | |
78 | /* socketpair */ | |
79 | return 360; | |
80 | case -109: | |
81 | /* send - not defined */ | |
82 | return __NR_SCMP_UNDEF; | |
83 | case -110: | |
84 | /* recv - not defined */ | |
85 | return __NR_SCMP_UNDEF; | |
86 | case -111: | |
87 | /* sendto */ | |
88 | return 369; | |
89 | case -112: | |
90 | /* recvfrom */ | |
91 | return 371; | |
92 | case -113: | |
93 | /* shutdown */ | |
94 | return 373; | |
95 | case -114: | |
96 | /* setsockopt */ | |
97 | return 366; | |
98 | case -115: | |
99 | /* getsockopt */ | |
100 | return 365; | |
101 | case -116: | |
102 | /* sendmsg */ | |
103 | return 370; | |
104 | case -117: | |
105 | /* recvmsg */ | |
106 | return 372; | |
107 | case -118: | |
108 | /* accept4 */ | |
109 | return 364; | |
110 | case -119: | |
111 | /* recvmmsg */ | |
112 | return 337; | |
113 | case -120: | |
114 | /* sendmmsg */ | |
115 | return 345; | |
116 | } | |
117 | ||
118 | return __NR_SCMP_ERROR; | |
119 | } | |
120 | ||
121 | /** | |
122 | * Convert a direct socket syscall into multiplexed pseudo socket syscall | |
123 | * @param syscall the direct syscall | |
124 | * | |
125 | * Return the related multiplexed pseduo syscall number, __NR_SCMP_UNDEF is | |
126 | * there is no related pseudo syscall, or __NR_SCMP_ERROR otherwise. | |
127 | * | |
128 | */ | |
129 | int _x86_sock_mux(int syscall) | |
130 | { | |
131 | switch (syscall) { | |
132 | case 337: | |
133 | /* recvmmsg */ | |
134 | return -119; | |
135 | case 345: | |
136 | /* sendmmsg */ | |
137 | return -120; | |
138 | case 359: | |
139 | /* socket */ | |
140 | return -101; | |
141 | case 360: | |
142 | /* socketpair */ | |
143 | return -108; | |
144 | case 361: | |
145 | /* bind */ | |
146 | return -102; | |
147 | case 362: | |
148 | /* connect */ | |
149 | return -103; | |
150 | case 363: | |
151 | /* listen */ | |
152 | return -104; | |
153 | case 364: | |
154 | /* accept4 */ | |
155 | return -118; | |
156 | case 365: | |
157 | /* getsockopt */ | |
158 | return -115; | |
159 | case 366: | |
160 | /* setsockopt */ | |
161 | return -114; | |
162 | case 367: | |
163 | /* getsockname */ | |
164 | return -106; | |
165 | case 368: | |
166 | /* getpeername */ | |
167 | return -107; | |
168 | case 369: | |
169 | /* sendto */ | |
170 | return -111; | |
171 | case 370: | |
172 | /* sendmsg */ | |
173 | return -116; | |
174 | case 371: | |
175 | /* recvfrom */ | |
176 | return -112; | |
177 | case 372: | |
178 | /* recvmsg */ | |
179 | return -117; | |
180 | case 373: | |
181 | /* shutdown */ | |
182 | return -113; | |
183 | } | |
184 | ||
185 | return __NR_SCMP_ERROR; | |
186 | } | |
187 | ||
188 | /** | |
189 | * Rewrite a syscall value to match the architecture | |
190 | * @param syscall the syscall number | |
191 | * | |
192 | * Syscalls can vary across different architectures so this function rewrites | |
193 | * the syscall into the correct value for the specified architecture. Returns | |
194 | * zero on success, negative values on failure. | |
195 | * | |
196 | */ | |
197 | int x86_syscall_rewrite(int *syscall) | |
198 | { | |
199 | int sys = *syscall; | |
200 | ||
201 | if (sys <= -100 && sys >= -120) | |
202 | *syscall = __x86_NR_socketcall; | |
203 | else if (sys <= -200 && sys >= -224) | |
204 | *syscall = __x86_NR_ipc; | |
205 | else if (sys < 0) | |
206 | return -EDOM; | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | /** | |
212 | * add a new rule to the x86 seccomp filter | |
213 | * @param col the filter collection | |
214 | * @param db the seccomp filter db | |
215 | * @param strict the strict flag | |
216 | * @param rule the filter rule | |
217 | * | |
218 | * This function adds a new syscall filter to the seccomp filter db, making any | |
219 | * necessary adjustments for the x86 ABI. Returns zero on success, negative | |
220 | * values on failure. | |
221 | * | |
222 | */ | |
223 | int x86_rule_add(struct db_filter_col *col, struct db_filter *db, bool strict, | |
224 | struct db_api_rule_list *rule) | |
225 | { | |
226 | int rc; | |
227 | unsigned int iter; | |
228 | size_t args_size; | |
229 | int sys = rule->syscall; | |
230 | int sys_a, sys_b; | |
231 | struct db_api_rule_list *rule_a, *rule_b; | |
232 | ||
233 | if ((sys <= -100 && sys >= -120) || (sys >= 359 && sys <= 373)) { | |
234 | /* (-100 to -120) : multiplexed socket syscalls | |
235 | (359 to 373) : direct socket syscalls, Linux 4.3+ */ | |
236 | ||
237 | /* strict check for the multiplexed socket syscalls */ | |
238 | for (iter = 0; iter < rule->args_cnt; iter++) { | |
239 | if ((rule->args[iter].valid != 0) && (strict)) | |
240 | return -EINVAL; | |
241 | } | |
242 | ||
243 | /* determine both the muxed and direct syscall numbers */ | |
244 | if (sys > 0) { | |
245 | sys_a = _x86_sock_mux(sys); | |
246 | if (sys_a == __NR_SCMP_ERROR) | |
247 | return __NR_SCMP_ERROR; | |
248 | sys_b = sys; | |
249 | } else { | |
250 | sys_a = sys; | |
251 | sys_b = _x86_sock_demux(sys); | |
252 | if (sys_b == __NR_SCMP_ERROR) | |
253 | return __NR_SCMP_ERROR; | |
254 | } | |
255 | ||
256 | /* use rule_a for the multiplexed syscall and use rule_b for | |
257 | * the direct wired syscall */ | |
258 | ||
259 | if (sys_a == __NR_SCMP_UNDEF) { | |
260 | rule_a = NULL; | |
261 | rule_b = rule; | |
262 | } else if (sys_b == __NR_SCMP_UNDEF) { | |
263 | rule_a = rule; | |
264 | rule_b = NULL; | |
265 | } else { | |
266 | /* need two rules, dup the first and link together */ | |
267 | rule_a = rule; | |
268 | rule_b = malloc(sizeof(*rule_b)); | |
269 | if (rule_b == NULL) | |
270 | return -ENOMEM; | |
271 | args_size = sizeof(*rule_b->args) * rule_a->args_cnt; | |
272 | rule_b->args = malloc(args_size); | |
273 | if (rule_b->args == NULL) { | |
274 | free(rule_b); | |
275 | return -ENOMEM; | |
276 | } | |
277 | rule_b->action = rule_a->action; | |
278 | rule_b->syscall = rule_a->syscall; | |
279 | rule_b->args_cnt = rule_a->args_cnt; | |
280 | memcpy(rule_b->args, rule_a->args, args_size); | |
281 | rule_b->prev = rule_a; | |
282 | rule_b->next = NULL; | |
283 | rule_a->next = rule_b; | |
284 | } | |
285 | ||
286 | /* multiplexed socket syscalls */ | |
287 | if (rule_a != NULL) { | |
288 | rule_a->syscall = __x86_NR_socketcall; | |
289 | rule_a->args[0].arg = 0; | |
290 | rule_a->args[0].op = SCMP_CMP_EQ; | |
291 | rule_a->args[0].mask = DATUM_MAX; | |
292 | rule_a->args[0].datum = (-sys_a) % 100; | |
293 | rule_a->args[0].valid = 1; | |
294 | } | |
295 | ||
296 | /* direct wired socket syscalls */ | |
297 | if (rule_b != NULL) | |
298 | rule_b->syscall = sys_b; | |
299 | ||
300 | /* add the rules as a single transaction */ | |
301 | rc = db_col_transaction_start(col); | |
302 | if (rc < 0) | |
303 | return rc; | |
304 | if (rule_a != NULL) { | |
305 | rc = db_rule_add(db, rule_a); | |
306 | if (rc < 0) | |
307 | goto fail_transaction; | |
308 | } | |
309 | if (rule_b != NULL) { | |
310 | rc = db_rule_add(db, rule_b); | |
311 | if (rc < 0) | |
312 | goto fail_transaction; | |
313 | } | |
314 | db_col_transaction_commit(col); | |
315 | } else if (sys <= -200 && sys >= -224) { | |
316 | /* multiplexed ipc syscalls */ | |
317 | for (iter = 0; iter < ARG_COUNT_MAX; iter++) { | |
318 | if ((rule->args[iter].valid != 0) && (strict)) | |
319 | return -EINVAL; | |
320 | } | |
321 | rule->args[0].arg = 0; | |
322 | rule->args[0].op = SCMP_CMP_EQ; | |
323 | rule->args[0].mask = DATUM_MAX; | |
324 | rule->args[0].datum = abs(sys) % 200; | |
325 | rule->args[0].valid = 1; | |
326 | rule->syscall = __x86_NR_ipc; | |
327 | ||
328 | rc = db_rule_add(db, rule); | |
329 | if (rc < 0) | |
330 | return rc; | |
331 | } else if (sys >= 0) { | |
332 | /* normal syscall processing */ | |
333 | rc = db_rule_add(db, rule); | |
334 | if (rc < 0) | |
335 | return rc; | |
336 | } else if (strict) | |
337 | return -EDOM; | |
338 | ||
339 | return 0; | |
340 | ||
341 | fail_transaction: | |
342 | db_col_transaction_abort(col); | |
343 | return rc; | |
344 | } |