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