]>
Commit | Line | Data |
---|---|---|
1 | /** | |
2 | * Seccomp Pseudo Filter Code (PFC) Generator | |
3 | * | |
4 | * Copyright (c) 2012 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 <errno.h> | |
23 | #include <inttypes.h> | |
24 | #include <stdlib.h> | |
25 | #include <stdio.h> | |
26 | #include <string.h> | |
27 | #include <unistd.h> | |
28 | ||
29 | /* NOTE: needed for the arch->token decoding in _pfc_arch() */ | |
30 | #include <linux/audit.h> | |
31 | ||
32 | #include <seccomp.h> | |
33 | ||
34 | #include "arch.h" | |
35 | #include "db.h" | |
36 | #include "gen_pfc.h" | |
37 | ||
38 | struct pfc_sys_list { | |
39 | struct db_sys_list *sys; | |
40 | struct pfc_sys_list *next; | |
41 | }; | |
42 | ||
43 | /* XXX - we should check the fprintf() return values */ | |
44 | ||
45 | /** | |
46 | * Display a string representation of the architecture | |
47 | * @param arch the architecture definition | |
48 | */ | |
49 | static const char *_pfc_arch(const struct arch_def *arch) | |
50 | { | |
51 | switch (arch->token) { | |
52 | case SCMP_ARCH_X86: | |
53 | return "x86"; | |
54 | case SCMP_ARCH_X86_64: | |
55 | return "x86_64"; | |
56 | case SCMP_ARCH_X32: | |
57 | return "x32"; | |
58 | case SCMP_ARCH_ARM: | |
59 | return "arm"; | |
60 | case SCMP_ARCH_AARCH64: | |
61 | return "aarch64"; | |
62 | case SCMP_ARCH_MIPS: | |
63 | return "mips"; | |
64 | case SCMP_ARCH_MIPSEL: | |
65 | return "mipsel"; | |
66 | case SCMP_ARCH_MIPS64: | |
67 | return "mips64"; | |
68 | case SCMP_ARCH_MIPSEL64: | |
69 | return "mipsel64"; | |
70 | case SCMP_ARCH_MIPS64N32: | |
71 | return "mips64n32"; | |
72 | case SCMP_ARCH_MIPSEL64N32: | |
73 | return "mipsel64n32"; | |
74 | case SCMP_ARCH_PPC64: | |
75 | return "ppc64"; | |
76 | case SCMP_ARCH_PPC64LE: | |
77 | return "ppc64le"; | |
78 | case SCMP_ARCH_PPC: | |
79 | return "ppc"; | |
80 | case SCMP_ARCH_S390X: | |
81 | return "s390x"; | |
82 | case SCMP_ARCH_S390: | |
83 | return "s390"; | |
84 | default: | |
85 | return "UNKNOWN"; | |
86 | } | |
87 | } | |
88 | ||
89 | /** | |
90 | * Display a string representation of the node argument | |
91 | * @param fds the file stream to send the output | |
92 | * @param arch the architecture definition | |
93 | * @param node the node | |
94 | */ | |
95 | static void _pfc_arg(FILE *fds, | |
96 | const struct arch_def *arch, | |
97 | const struct db_arg_chain_tree *node) | |
98 | { | |
99 | if (arch->size == ARCH_SIZE_64) { | |
100 | if (arch_arg_offset_hi(arch, node->arg) == node->arg_offset) | |
101 | fprintf(fds, "$a%d.hi32", node->arg); | |
102 | else | |
103 | fprintf(fds, "$a%d.lo32", node->arg); | |
104 | } else | |
105 | fprintf(fds, "$a%d", node->arg); | |
106 | } | |
107 | ||
108 | /** | |
109 | * Display a string representation of the filter action | |
110 | * @param fds the file stream to send the output | |
111 | * @param action the action | |
112 | */ | |
113 | static void _pfc_action(FILE *fds, uint32_t action) | |
114 | { | |
115 | switch (action & 0xffff0000) { | |
116 | case SCMP_ACT_KILL: | |
117 | fprintf(fds, "action KILL;\n"); | |
118 | break; | |
119 | case SCMP_ACT_TRAP: | |
120 | fprintf(fds, "action TRAP;\n"); | |
121 | break; | |
122 | case SCMP_ACT_ERRNO(0): | |
123 | fprintf(fds, "action ERRNO(%u);\n", (action & 0x0000ffff)); | |
124 | break; | |
125 | case SCMP_ACT_TRACE(0): | |
126 | fprintf(fds, "action TRACE(%u);\n", (action & 0x0000ffff)); | |
127 | break; | |
128 | case SCMP_ACT_ALLOW: | |
129 | fprintf(fds, "action ALLOW;\n"); | |
130 | break; | |
131 | default: | |
132 | fprintf(fds, "action 0x%x;\n", action); | |
133 | } | |
134 | } | |
135 | ||
136 | /** | |
137 | * Indent the output stream | |
138 | * @param fds the file stream to send the output | |
139 | * @param lvl the indentation level | |
140 | * | |
141 | * This function indents the output stream with whitespace based on the | |
142 | * requested indentation level. | |
143 | */ | |
144 | static void _indent(FILE *fds, unsigned int lvl) | |
145 | { | |
146 | while (lvl-- > 0) | |
147 | fprintf(fds, " "); | |
148 | } | |
149 | ||
150 | /** | |
151 | * Generate the pseudo filter code for an argument chain | |
152 | * @param arch the architecture definition | |
153 | * @param node the head of the argument chain | |
154 | * @param lvl the indentation level | |
155 | * @param fds the file stream to send the output | |
156 | * | |
157 | * This function generates the pseudo filter code representation of the given | |
158 | * argument chain and writes it to the given output stream. | |
159 | * | |
160 | */ | |
161 | static void _gen_pfc_chain(const struct arch_def *arch, | |
162 | const struct db_arg_chain_tree *node, | |
163 | unsigned int lvl, FILE *fds) | |
164 | { | |
165 | const struct db_arg_chain_tree *c_iter; | |
166 | ||
167 | /* get to the start */ | |
168 | c_iter = node; | |
169 | while (c_iter->lvl_prv != NULL) | |
170 | c_iter = c_iter->lvl_prv; | |
171 | ||
172 | while (c_iter != NULL) { | |
173 | /* comparison operation */ | |
174 | _indent(fds, lvl); | |
175 | fprintf(fds, "if ("); | |
176 | _pfc_arg(fds, arch, c_iter); | |
177 | switch (c_iter->op) { | |
178 | case SCMP_CMP_EQ: | |
179 | fprintf(fds, " == "); | |
180 | break; | |
181 | case SCMP_CMP_GE: | |
182 | fprintf(fds, " >= "); | |
183 | break; | |
184 | case SCMP_CMP_GT: | |
185 | fprintf(fds, " > "); | |
186 | break; | |
187 | case SCMP_CMP_MASKED_EQ: | |
188 | fprintf(fds, " & 0x%.8x == ", c_iter->mask); | |
189 | break; | |
190 | case SCMP_CMP_NE: | |
191 | case SCMP_CMP_LT: | |
192 | case SCMP_CMP_LE: | |
193 | default: | |
194 | fprintf(fds, " ??? "); | |
195 | } | |
196 | fprintf(fds, "%u)\n", c_iter->datum); | |
197 | ||
198 | /* true result */ | |
199 | if (c_iter->act_t_flg) { | |
200 | _indent(fds, lvl + 1); | |
201 | _pfc_action(fds, c_iter->act_t); | |
202 | } else if (c_iter->nxt_t != NULL) | |
203 | _gen_pfc_chain(arch, c_iter->nxt_t, lvl + 1, fds); | |
204 | ||
205 | /* false result */ | |
206 | if (c_iter->act_f_flg) { | |
207 | _indent(fds, lvl); | |
208 | fprintf(fds, "else\n"); | |
209 | _indent(fds, lvl + 1); | |
210 | _pfc_action(fds, c_iter->act_f); | |
211 | } else if (c_iter->nxt_f != NULL) { | |
212 | _indent(fds, lvl); | |
213 | fprintf(fds, "else\n"); | |
214 | _gen_pfc_chain(arch, c_iter->nxt_f, lvl + 1, fds); | |
215 | } | |
216 | ||
217 | c_iter = c_iter->lvl_nxt; | |
218 | } | |
219 | } | |
220 | ||
221 | /** | |
222 | * Generate pseudo filter code for a syscall | |
223 | * @param arch the architecture definition | |
224 | * @param sys the syscall filter | |
225 | * @param fds the file stream to send the output | |
226 | * | |
227 | * This function generates a pseduo filter code representation of the given | |
228 | * syscall filter and writes it to the given output stream. | |
229 | * | |
230 | */ | |
231 | static void _gen_pfc_syscall(const struct arch_def *arch, | |
232 | const struct db_sys_list *sys, FILE *fds) | |
233 | { | |
234 | unsigned int sys_num = sys->num; | |
235 | const char *sys_name = arch_syscall_resolve_num(arch, sys_num); | |
236 | ||
237 | _indent(fds, 1); | |
238 | fprintf(fds, "# filter for syscall \"%s\" (%d) [priority: %d]\n", | |
239 | (sys_name ? sys_name : "UNKNOWN"), sys_num, sys->priority); | |
240 | _indent(fds, 1); | |
241 | fprintf(fds, "if ($syscall == %d)\n", sys_num); | |
242 | if (sys->chains == NULL) { | |
243 | _indent(fds, 2); | |
244 | _pfc_action(fds, sys->action); | |
245 | } else | |
246 | _gen_pfc_chain(arch, sys->chains, 2, fds); | |
247 | } | |
248 | ||
249 | /** | |
250 | * Generate pseudo filter code for an architecture | |
251 | * @param col the seccomp filter collection | |
252 | * @param db the single seccomp filter | |
253 | * @param fds the file stream to send the output | |
254 | * | |
255 | * This function generates a pseudo filter code representation of the given | |
256 | * filter DB and writes it to the given output stream. Returns zero on | |
257 | * success, negative values on failure. | |
258 | * | |
259 | */ | |
260 | static int _gen_pfc_arch(const struct db_filter_col *col, | |
261 | const struct db_filter *db, FILE *fds) | |
262 | { | |
263 | int rc = 0; | |
264 | struct db_sys_list *s_iter; | |
265 | struct pfc_sys_list *p_iter = NULL, *p_new, *p_head = NULL, *p_prev; | |
266 | ||
267 | /* sort the syscall list */ | |
268 | db_list_foreach(s_iter, db->syscalls) { | |
269 | p_new = malloc(sizeof(*p_new)); | |
270 | if (p_new == NULL) { | |
271 | rc = -ENOMEM; | |
272 | goto arch_return; | |
273 | } | |
274 | memset(p_new, 0, sizeof(*p_new)); | |
275 | p_new->sys = s_iter; | |
276 | ||
277 | p_prev = NULL; | |
278 | p_iter = p_head; | |
279 | while (p_iter != NULL && | |
280 | s_iter->priority < p_iter->sys->priority) { | |
281 | p_prev = p_iter; | |
282 | p_iter = p_iter->next; | |
283 | } | |
284 | if (p_head == NULL) | |
285 | p_head = p_new; | |
286 | else if (p_prev == NULL) { | |
287 | p_new->next = p_head; | |
288 | p_head = p_new; | |
289 | } else { | |
290 | p_new->next = p_iter; | |
291 | p_prev->next = p_new; | |
292 | } | |
293 | } | |
294 | ||
295 | fprintf(fds, "# filter for arch %s (%u)\n", | |
296 | _pfc_arch(db->arch), db->arch->token_bpf); | |
297 | fprintf(fds, "if ($arch == %u)\n", db->arch->token_bpf); | |
298 | p_iter = p_head; | |
299 | while (p_iter != NULL) { | |
300 | if (!p_iter->sys->valid) | |
301 | continue; | |
302 | _gen_pfc_syscall(db->arch, p_iter->sys, fds); | |
303 | p_iter = p_iter->next; | |
304 | } | |
305 | _indent(fds, 1); | |
306 | fprintf(fds, "# default action\n"); | |
307 | _indent(fds, 1); | |
308 | _pfc_action(fds, col->attr.act_default); | |
309 | ||
310 | arch_return: | |
311 | while (p_head != NULL) { | |
312 | p_iter = p_head; | |
313 | p_head = p_head->next; | |
314 | free(p_iter); | |
315 | } | |
316 | return rc; | |
317 | } | |
318 | ||
319 | /** | |
320 | * Generate a pseudo filter code string representation | |
321 | * @param col the seccomp filter collection | |
322 | * @param fd the fd to send the output | |
323 | * | |
324 | * This function generates a pseudo filter code representation of the given | |
325 | * filter collection and writes it to the given fd. Returns zero on success, | |
326 | * negative values on failure. | |
327 | * | |
328 | */ | |
329 | int gen_pfc_generate(const struct db_filter_col *col, int fd) | |
330 | { | |
331 | int rc = 0; | |
332 | int newfd; | |
333 | unsigned int iter; | |
334 | FILE *fds; | |
335 | ||
336 | newfd = dup(fd); | |
337 | if (newfd < 0) | |
338 | return errno; | |
339 | fds = fdopen(newfd, "a"); | |
340 | if (fds == NULL) { | |
341 | close(newfd); | |
342 | return errno; | |
343 | } | |
344 | ||
345 | /* generate the pfc */ | |
346 | fprintf(fds, "#\n"); | |
347 | fprintf(fds, "# pseudo filter code start\n"); | |
348 | fprintf(fds, "#\n"); | |
349 | ||
350 | for (iter = 0; iter < col->filter_cnt; iter++) | |
351 | _gen_pfc_arch(col, col->filters[iter], fds); | |
352 | ||
353 | fprintf(fds, "# invalid architecture action\n"); | |
354 | _pfc_action(fds, col->attr.act_badarch); | |
355 | fprintf(fds, "#\n"); | |
356 | fprintf(fds, "# pseudo filter code end\n"); | |
357 | fprintf(fds, "#\n"); | |
358 | ||
359 | fflush(fds); | |
360 | fclose(fds); | |
361 | ||
362 | return rc; | |
363 | } |