/src/selinux/libsepol/src/conditional.c
Line | Count | Source |
1 | | /* Authors: Karl MacMillan <kmacmillan@tresys.com> |
2 | | * Frank Mayer <mayerf@tresys.com> |
3 | | * David Caplan <dac@tresys.com> |
4 | | * |
5 | | * Copyright (C) 2003 - 2005 Tresys Technology, LLC |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * This library is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public |
18 | | * License along with this library; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | */ |
21 | | |
22 | | #include <stdlib.h> |
23 | | |
24 | | #include <sepol/policydb/flask_types.h> |
25 | | #include <sepol/policydb/conditional.h> |
26 | | |
27 | | #include "private.h" |
28 | | #include "debug.h" |
29 | | |
30 | | /* move all type rules to top of t/f lists to help kernel on evaluation */ |
31 | | static void cond_optimize(cond_av_list_t **l) |
32 | 0 | { |
33 | 0 | cond_av_list_t *top, *p, *cur; |
34 | |
|
35 | 0 | top = p = cur = *l; |
36 | |
|
37 | 0 | while (cur) { |
38 | 0 | if ((cur->node->key.specified & AVTAB_TYPE) && (top != cur)) { |
39 | 0 | p->next = cur->next; |
40 | 0 | cur->next = top; |
41 | 0 | top = cur; |
42 | 0 | cur = p->next; |
43 | 0 | } else { |
44 | 0 | p = cur; |
45 | 0 | cur = cur->next; |
46 | 0 | } |
47 | 0 | } |
48 | 0 | *l = top; |
49 | 0 | } |
50 | | |
51 | | /* reorder t/f lists for kernel */ |
52 | | void cond_optimize_lists(cond_list_t *cl) |
53 | 0 | { |
54 | 0 | cond_list_t *n; |
55 | |
|
56 | 0 | for (n = cl; n != NULL; n = n->next) { |
57 | 0 | cond_optimize(&n->true_list); |
58 | 0 | cond_optimize(&n->false_list); |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | | static int bool_present(unsigned int target, unsigned int bools[], |
63 | | unsigned int num_bools) |
64 | 0 | { |
65 | 0 | unsigned int i = 0; |
66 | 0 | int ret = 1; |
67 | |
|
68 | 0 | if (num_bools > COND_MAX_BOOLS) { |
69 | 0 | return 0; |
70 | 0 | } |
71 | 0 | while (i < num_bools && target != bools[i]) |
72 | 0 | i++; |
73 | 0 | if (i == num_bools) |
74 | 0 | ret = 0; /* got to end w/o match */ |
75 | 0 | return ret; |
76 | 0 | } |
77 | | |
78 | | static int same_bools(cond_node_t *a, cond_node_t *b) |
79 | 0 | { |
80 | 0 | unsigned int i, x; |
81 | |
|
82 | 0 | x = a->nbools; |
83 | | |
84 | | /* same number of bools? */ |
85 | 0 | if (x != b->nbools) |
86 | 0 | return 0; |
87 | | |
88 | | /* make sure all the bools in a are also in b */ |
89 | 0 | for (i = 0; i < x; i++) |
90 | 0 | if (!bool_present(a->bool_ids[i], b->bool_ids, x)) |
91 | 0 | return 0; |
92 | 0 | return 1; |
93 | 0 | } |
94 | | |
95 | | /* |
96 | | * Determine if two conditional expressions are equal. |
97 | | */ |
98 | | int cond_expr_equal(cond_node_t *a, cond_node_t *b) |
99 | 0 | { |
100 | 0 | cond_expr_t *cur_a, *cur_b; |
101 | |
|
102 | 0 | if (a == NULL || b == NULL) |
103 | 0 | return 0; |
104 | | |
105 | 0 | if (a->nbools != b->nbools) |
106 | 0 | return 0; |
107 | | |
108 | | /* if exprs have <= COND_MAX_BOOLS we can check the precompute values |
109 | | * for the expressions. |
110 | | */ |
111 | 0 | if (a->nbools <= COND_MAX_BOOLS && b->nbools <= COND_MAX_BOOLS) { |
112 | 0 | if (!same_bools(a, b)) |
113 | 0 | return 0; |
114 | 0 | return (a->expr_pre_comp == b->expr_pre_comp); |
115 | 0 | } |
116 | | |
117 | | /* for long expressions we check for exactly the same expression */ |
118 | 0 | cur_a = a->expr; |
119 | 0 | cur_b = b->expr; |
120 | 0 | while (1) { |
121 | 0 | if (cur_a == NULL && cur_b == NULL) |
122 | 0 | return 1; |
123 | 0 | else if (cur_a == NULL || cur_b == NULL) |
124 | 0 | return 0; |
125 | 0 | if (cur_a->expr_type != cur_b->expr_type) |
126 | 0 | return 0; |
127 | 0 | if (cur_a->expr_type == COND_BOOL) { |
128 | 0 | if (cur_a->boolean != cur_b->boolean) |
129 | 0 | return 0; |
130 | 0 | } |
131 | 0 | cur_a = cur_a->next; |
132 | 0 | cur_b = cur_b->next; |
133 | 0 | } |
134 | 0 | return 1; |
135 | 0 | } |
136 | | |
137 | | /* Create a new conditional node, optionally copying |
138 | | * the conditional expression from an existing node. |
139 | | * If node is NULL then a new node will be created |
140 | | * with no conditional expression. |
141 | | */ |
142 | | cond_node_t *cond_node_create(policydb_t *p, cond_node_t *node) |
143 | 0 | { |
144 | 0 | cond_node_t *new_node; |
145 | 0 | unsigned int i; |
146 | |
|
147 | 0 | new_node = (cond_node_t *)malloc(sizeof(cond_node_t)); |
148 | 0 | if (!new_node) { |
149 | 0 | return NULL; |
150 | 0 | } |
151 | 0 | memset(new_node, 0, sizeof(cond_node_t)); |
152 | |
|
153 | 0 | if (node) { |
154 | 0 | new_node->expr = cond_copy_expr(node->expr); |
155 | 0 | if (!new_node->expr) { |
156 | 0 | free(new_node); |
157 | 0 | return NULL; |
158 | 0 | } |
159 | 0 | new_node->cur_state = cond_evaluate_expr(p, new_node->expr); |
160 | 0 | new_node->nbools = node->nbools; |
161 | 0 | for (i = 0; i < min(node->nbools, COND_MAX_BOOLS); i++) |
162 | 0 | new_node->bool_ids[i] = node->bool_ids[i]; |
163 | 0 | new_node->expr_pre_comp = node->expr_pre_comp; |
164 | 0 | new_node->flags = node->flags; |
165 | 0 | } |
166 | | |
167 | 0 | return new_node; |
168 | 0 | } |
169 | | |
170 | | /* Find a conditional (the needle) within a list of existing ones (the |
171 | | * haystack) that has a matching expression. If found, return a |
172 | | * pointer to the existing node, setting 'was_created' to 0. |
173 | | * Otherwise create a new one and return it, setting 'was_created' to |
174 | | * 1. */ |
175 | | cond_node_t *cond_node_find(policydb_t *p, cond_node_t *needle, |
176 | | cond_node_t *haystack, int *was_created) |
177 | 0 | { |
178 | 0 | while (haystack) { |
179 | 0 | if (cond_expr_equal(needle, haystack)) { |
180 | 0 | *was_created = 0; |
181 | 0 | return haystack; |
182 | 0 | } |
183 | 0 | haystack = haystack->next; |
184 | 0 | } |
185 | 0 | *was_created = 1; |
186 | |
|
187 | 0 | return cond_node_create(p, needle); |
188 | 0 | } |
189 | | |
190 | | /* return either a pre-existing matching node or create a new node */ |
191 | | cond_node_t *cond_node_search(policydb_t *p, cond_node_t *list, cond_node_t *cn) |
192 | 0 | { |
193 | 0 | int was_created; |
194 | 0 | cond_node_t *result = cond_node_find(p, cn, list, &was_created); |
195 | 0 | if (result != NULL && was_created) { |
196 | | /* add conditional node to policy list */ |
197 | 0 | result->next = p->cond_list; |
198 | 0 | p->cond_list = result; |
199 | 0 | } |
200 | 0 | return result; |
201 | 0 | } |
202 | | |
203 | | /* |
204 | | * cond_evaluate_expr evaluates a conditional expr |
205 | | * in reverse polish notation. It returns true (1), false (0), |
206 | | * or undefined (-1). Undefined occurs when the expression |
207 | | * exceeds the stack depth of COND_EXPR_MAXDEPTH. |
208 | | */ |
209 | | int cond_evaluate_expr(policydb_t *p, cond_expr_t *expr) |
210 | 0 | { |
211 | 0 | cond_expr_t *cur; |
212 | 0 | int s[COND_EXPR_MAXDEPTH]; |
213 | 0 | int sp = -1; |
214 | |
|
215 | 0 | s[0] = -1; |
216 | |
|
217 | 0 | for (cur = expr; cur != NULL; cur = cur->next) { |
218 | 0 | switch (cur->expr_type) { |
219 | 0 | case COND_BOOL: |
220 | 0 | if (sp == (COND_EXPR_MAXDEPTH - 1)) |
221 | 0 | return -1; |
222 | 0 | sp++; |
223 | 0 | s[sp] = p->bool_val_to_struct[cur->boolean - 1]->state; |
224 | 0 | break; |
225 | 0 | case COND_NOT: |
226 | 0 | if (sp < 0) |
227 | 0 | return -1; |
228 | 0 | s[sp] = !s[sp]; |
229 | 0 | break; |
230 | 0 | case COND_OR: |
231 | 0 | if (sp < 1) |
232 | 0 | return -1; |
233 | 0 | sp--; |
234 | 0 | s[sp] |= s[sp + 1]; |
235 | 0 | break; |
236 | 0 | case COND_AND: |
237 | 0 | if (sp < 1) |
238 | 0 | return -1; |
239 | 0 | sp--; |
240 | 0 | s[sp] &= s[sp + 1]; |
241 | 0 | break; |
242 | 0 | case COND_XOR: |
243 | 0 | if (sp < 1) |
244 | 0 | return -1; |
245 | 0 | sp--; |
246 | 0 | s[sp] ^= s[sp + 1]; |
247 | 0 | break; |
248 | 0 | case COND_EQ: |
249 | 0 | if (sp < 1) |
250 | 0 | return -1; |
251 | 0 | sp--; |
252 | 0 | s[sp] = (s[sp] == s[sp + 1]); |
253 | 0 | break; |
254 | 0 | case COND_NEQ: |
255 | 0 | if (sp < 1) |
256 | 0 | return -1; |
257 | 0 | sp--; |
258 | 0 | s[sp] = (s[sp] != s[sp + 1]); |
259 | 0 | break; |
260 | 0 | default: |
261 | 0 | return -1; |
262 | 0 | } |
263 | 0 | } |
264 | 0 | return s[0]; |
265 | 0 | } |
266 | | |
267 | | cond_expr_t *cond_copy_expr(cond_expr_t *expr) |
268 | 0 | { |
269 | 0 | cond_expr_t *cur, *head, *tail, *new_expr; |
270 | 0 | tail = head = NULL; |
271 | 0 | cur = expr; |
272 | 0 | while (cur) { |
273 | 0 | new_expr = (cond_expr_t *)malloc(sizeof(cond_expr_t)); |
274 | 0 | if (!new_expr) |
275 | 0 | goto free_head; |
276 | 0 | memset(new_expr, 0, sizeof(cond_expr_t)); |
277 | |
|
278 | 0 | new_expr->expr_type = cur->expr_type; |
279 | 0 | new_expr->boolean = cur->boolean; |
280 | |
|
281 | 0 | if (!head) |
282 | 0 | head = new_expr; |
283 | 0 | if (tail) |
284 | 0 | tail->next = new_expr; |
285 | 0 | tail = new_expr; |
286 | 0 | cur = cur->next; |
287 | 0 | } |
288 | 0 | return head; |
289 | | |
290 | 0 | free_head: |
291 | 0 | while (head) { |
292 | 0 | tail = head->next; |
293 | 0 | free(head); |
294 | 0 | head = tail; |
295 | 0 | } |
296 | 0 | return NULL; |
297 | 0 | } |
298 | | |
299 | | /* |
300 | | * evaluate_cond_node evaluates the conditional stored in |
301 | | * a cond_node_t and if the result is different than the |
302 | | * current state of the node it sets the rules in the true/false |
303 | | * list appropriately. If the result of the expression is undefined |
304 | | * all of the rules are disabled for safety. |
305 | | */ |
306 | | static int evaluate_cond_node(policydb_t *p, cond_node_t *node) |
307 | 0 | { |
308 | 0 | int new_state; |
309 | 0 | cond_av_list_t *cur; |
310 | |
|
311 | 0 | new_state = cond_evaluate_expr(p, node->expr); |
312 | 0 | if (new_state != node->cur_state) { |
313 | 0 | node->cur_state = new_state; |
314 | 0 | if (new_state == -1) |
315 | 0 | WARN(NULL, |
316 | 0 | "expression result was undefined - disabling all rules."); |
317 | | /* turn the rules on or off */ |
318 | 0 | for (cur = node->true_list; cur != NULL; cur = cur->next) { |
319 | 0 | if (new_state <= 0) { |
320 | 0 | cur->node->key.specified &= ~AVTAB_ENABLED; |
321 | 0 | } else { |
322 | 0 | cur->node->key.specified |= AVTAB_ENABLED; |
323 | 0 | } |
324 | 0 | } |
325 | |
|
326 | 0 | for (cur = node->false_list; cur != NULL; cur = cur->next) { |
327 | | /* -1 or 1 */ |
328 | 0 | if (new_state) { |
329 | 0 | cur->node->key.specified &= ~AVTAB_ENABLED; |
330 | 0 | } else { |
331 | 0 | cur->node->key.specified |= AVTAB_ENABLED; |
332 | 0 | } |
333 | 0 | } |
334 | 0 | } |
335 | 0 | return 0; |
336 | 0 | } |
337 | | |
338 | | /* precompute and simplify an expression if possible. If left with !expression, change |
339 | | * to expression and switch t and f. precompute expression for expressions with limited |
340 | | * number of bools. |
341 | | */ |
342 | | int cond_normalize_expr(policydb_t *p, cond_node_t *cn) |
343 | 0 | { |
344 | 0 | cond_expr_t *ne, *e; |
345 | 0 | cond_av_list_t *tmp; |
346 | 0 | unsigned int i, j, orig_value[COND_MAX_BOOLS]; |
347 | 0 | int k; |
348 | 0 | uint32_t test = 0x0; |
349 | 0 | avrule_t *tmp2; |
350 | |
|
351 | 0 | cn->nbools = 0; |
352 | |
|
353 | 0 | memset(cn->bool_ids, 0, sizeof(cn->bool_ids)); |
354 | 0 | cn->expr_pre_comp = 0x0; |
355 | | |
356 | | /* take care of !expr case */ |
357 | 0 | ne = NULL; |
358 | 0 | e = cn->expr; |
359 | | |
360 | | /* because it's RPN look at last element */ |
361 | 0 | while (e->next != NULL) { |
362 | 0 | ne = e; |
363 | 0 | e = e->next; |
364 | 0 | } |
365 | 0 | if (e->expr_type == COND_NOT) { |
366 | 0 | if (ne) { |
367 | 0 | ne->next = NULL; |
368 | 0 | } else { /* ne should never be NULL */ |
369 | 0 | ERR(NULL, |
370 | 0 | "Found expr with no bools and only a ! - this should never happen."); |
371 | 0 | return -1; |
372 | 0 | } |
373 | | /* swap the true and false lists */ |
374 | 0 | tmp = cn->true_list; |
375 | 0 | cn->true_list = cn->false_list; |
376 | 0 | cn->false_list = tmp; |
377 | 0 | tmp2 = cn->avtrue_list; |
378 | 0 | cn->avtrue_list = cn->avfalse_list; |
379 | 0 | cn->avfalse_list = tmp2; |
380 | | |
381 | | /* free the "not" node in the list */ |
382 | 0 | free(e); |
383 | 0 | } |
384 | | |
385 | | /* find all the bools in the expression */ |
386 | 0 | for (e = cn->expr; e != NULL; e = e->next) { |
387 | 0 | switch (e->expr_type) { |
388 | 0 | case COND_BOOL: |
389 | | /* see if we've already seen this bool */ |
390 | 0 | if (!bool_present(e->boolean, cn->bool_ids, |
391 | 0 | cn->nbools)) { |
392 | | /* count em all but only record up to COND_MAX_BOOLS */ |
393 | 0 | if (cn->nbools < COND_MAX_BOOLS) |
394 | 0 | cn->bool_ids[cn->nbools++] = e->boolean; |
395 | 0 | else |
396 | 0 | cn->nbools++; |
397 | 0 | } |
398 | 0 | break; |
399 | 0 | default: |
400 | 0 | break; |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | | /* only precompute for exprs with <= COND_AX_BOOLS */ |
405 | 0 | if (cn->nbools <= COND_MAX_BOOLS) { |
406 | | /* save the default values for the bools so we can play with them */ |
407 | 0 | for (i = 0; i < cn->nbools; i++) { |
408 | 0 | orig_value[i] = |
409 | 0 | p->bool_val_to_struct[cn->bool_ids[i] - 1] |
410 | 0 | ->state; |
411 | 0 | } |
412 | | |
413 | | /* loop through all possible combinations of values for bools in expression */ |
414 | 0 | for (test = 0x0; test < (UINT32_C(1) << cn->nbools); test++) { |
415 | | /* temporarily set the value for all the bools in the |
416 | | * expression using the corr. bit in test */ |
417 | 0 | for (j = 0; j < cn->nbools; j++) { |
418 | 0 | p->bool_val_to_struct[cn->bool_ids[j] - 1] |
419 | 0 | ->state = |
420 | 0 | (test & (UINT32_C(1) << j)) ? 1 : 0; |
421 | 0 | } |
422 | 0 | k = cond_evaluate_expr(p, cn->expr); |
423 | 0 | if (k == -1) { |
424 | 0 | ERR(NULL, |
425 | 0 | "While testing expression, expression result " |
426 | 0 | "was undefined - this should never happen."); |
427 | 0 | return -1; |
428 | 0 | } |
429 | | /* set the bit if expression evaluates true */ |
430 | 0 | if (k) |
431 | 0 | cn->expr_pre_comp |= UINT32_C(1) << test; |
432 | 0 | } |
433 | | |
434 | | /* restore bool default values */ |
435 | 0 | for (i = 0; i < cn->nbools; i++) |
436 | 0 | p->bool_val_to_struct[cn->bool_ids[i] - 1]->state = |
437 | 0 | orig_value[i]; |
438 | 0 | } |
439 | 0 | return 0; |
440 | 0 | } |
441 | | |
442 | | int evaluate_conds(policydb_t *p) |
443 | 0 | { |
444 | 0 | int ret; |
445 | 0 | cond_node_t *cur; |
446 | |
|
447 | 0 | for (cur = p->cond_list; cur != NULL; cur = cur->next) { |
448 | 0 | ret = evaluate_cond_node(p, cur); |
449 | 0 | if (ret) |
450 | 0 | return ret; |
451 | 0 | } |
452 | 0 | return 0; |
453 | 0 | } |
454 | | |
455 | | int cond_policydb_init(policydb_t *p) |
456 | 9.64k | { |
457 | 9.64k | p->bool_val_to_struct = NULL; |
458 | 9.64k | p->cond_list = NULL; |
459 | 9.64k | if (avtab_init(&p->te_cond_avtab)) |
460 | 0 | return -1; |
461 | | |
462 | 9.64k | return 0; |
463 | 9.64k | } |
464 | | |
465 | | void cond_av_list_destroy(cond_av_list_t *list) |
466 | 3.77k | { |
467 | 3.77k | cond_av_list_t *cur, *next; |
468 | 3.77k | for (cur = list; cur != NULL; cur = next) { |
469 | 0 | next = cur->next; |
470 | | /* the avtab_ptr_t node is destroy by the avtab */ |
471 | 0 | free(cur); |
472 | 0 | } |
473 | 3.77k | } |
474 | | |
475 | | void cond_expr_destroy(cond_expr_t *expr) |
476 | 1.88k | { |
477 | 1.88k | cond_expr_t *cur_expr, *next_expr; |
478 | | |
479 | 1.88k | if (!expr) |
480 | 1.62k | return; |
481 | | |
482 | 734 | for (cur_expr = expr; cur_expr != NULL; cur_expr = next_expr) { |
483 | 475 | next_expr = cur_expr->next; |
484 | 475 | free(cur_expr); |
485 | 475 | } |
486 | 259 | } |
487 | | |
488 | | void cond_node_destroy(cond_node_t *node) |
489 | 1.88k | { |
490 | 1.88k | if (!node) |
491 | 0 | return; |
492 | | |
493 | 1.88k | cond_expr_destroy(node->expr); |
494 | 1.88k | avrule_list_destroy(node->avtrue_list); |
495 | 1.88k | avrule_list_destroy(node->avfalse_list); |
496 | 1.88k | cond_av_list_destroy(node->true_list); |
497 | 1.88k | cond_av_list_destroy(node->false_list); |
498 | 1.88k | } |
499 | | |
500 | | void cond_list_destroy(cond_list_t *list) |
501 | 29.4k | { |
502 | 29.4k | cond_node_t *next, *cur; |
503 | | |
504 | 29.4k | if (list == NULL) |
505 | 29.4k | return; |
506 | | |
507 | 1.68k | for (cur = list; cur != NULL; cur = next) { |
508 | 1.60k | next = cur->next; |
509 | 1.60k | cond_node_destroy(cur); |
510 | 1.60k | free(cur); |
511 | 1.60k | } |
512 | 74 | } |
513 | | |
514 | | void cond_policydb_destroy(policydb_t *p) |
515 | 9.64k | { |
516 | 9.64k | if (p->bool_val_to_struct != NULL) |
517 | 5.33k | free(p->bool_val_to_struct); |
518 | 9.64k | avtab_destroy(&p->te_cond_avtab); |
519 | 9.64k | cond_list_destroy(p->cond_list); |
520 | 9.64k | } |
521 | | |
522 | | int cond_init_bool_indexes(policydb_t *p) |
523 | 5.33k | { |
524 | 5.33k | if (p->bool_val_to_struct) |
525 | 0 | free(p->bool_val_to_struct); |
526 | 5.33k | p->bool_val_to_struct = (cond_bool_datum_t **)calloc( |
527 | 5.33k | p->p_bools.nprim, sizeof(cond_bool_datum_t *)); |
528 | 5.33k | if (!p->bool_val_to_struct) |
529 | 0 | return -1; |
530 | 5.33k | return 0; |
531 | 5.33k | } |
532 | | |
533 | | int cond_destroy_bool(hashtab_key_t key, hashtab_datum_t datum, |
534 | | void *p __attribute__((unused))) |
535 | 540 | { |
536 | 540 | if (key) |
537 | 419 | free(key); |
538 | 540 | free(datum); |
539 | 540 | return 0; |
540 | 540 | } |
541 | | |
542 | | int cond_index_bool(hashtab_key_t key, hashtab_datum_t datum, void *datap) |
543 | 121 | { |
544 | 121 | policydb_t *p; |
545 | 121 | cond_bool_datum_t *booldatum; |
546 | | |
547 | 121 | booldatum = datum; |
548 | 121 | p = datap; |
549 | | |
550 | 121 | if (!booldatum->s.value || booldatum->s.value > p->p_bools.nprim) |
551 | 72 | return -EINVAL; |
552 | | |
553 | 49 | if (p->p_bool_val_to_name[booldatum->s.value - 1] != NULL) |
554 | 0 | return -EINVAL; |
555 | | |
556 | 49 | p->p_bool_val_to_name[booldatum->s.value - 1] = key; |
557 | 49 | p->bool_val_to_struct[booldatum->s.value - 1] = booldatum; |
558 | | |
559 | 49 | return 0; |
560 | 49 | } |
561 | | |
562 | | static int bool_isvalid(cond_bool_datum_t *b) |
563 | 479 | { |
564 | 479 | if (!(b->state == 0 || b->state == 1)) |
565 | 50 | return 0; |
566 | 429 | return 1; |
567 | 479 | } |
568 | | |
569 | | int cond_read_bool(policydb_t *p, hashtab_t h, struct policy_file *fp) |
570 | 540 | { |
571 | 540 | char *key = NULL; |
572 | 540 | cond_bool_datum_t *booldatum; |
573 | 540 | uint32_t buf[3], len; |
574 | 540 | int rc; |
575 | | |
576 | 540 | booldatum = malloc(sizeof(cond_bool_datum_t)); |
577 | 540 | if (!booldatum) |
578 | 0 | return -1; |
579 | 540 | memset(booldatum, 0, sizeof(cond_bool_datum_t)); |
580 | | |
581 | 540 | rc = next_entry(buf, fp, sizeof(uint32_t) * 3); |
582 | 540 | if (rc < 0) |
583 | 61 | goto err; |
584 | | |
585 | 479 | booldatum->s.value = le32_to_cpu(buf[0]); |
586 | 479 | booldatum->state = le32_to_cpu(buf[1]); |
587 | | |
588 | 479 | if (!bool_isvalid(booldatum)) |
589 | 50 | goto err; |
590 | | |
591 | 429 | len = le32_to_cpu(buf[2]); |
592 | 429 | if (str_read(&key, fp, len)) |
593 | 10 | goto err; |
594 | | |
595 | 419 | if (p->policy_type != POLICY_KERN) { |
596 | 419 | rc = next_entry(buf, fp, sizeof(uint32_t)); |
597 | 419 | if (rc < 0) |
598 | 1 | goto err; |
599 | 418 | booldatum->flags = le32_to_cpu(buf[0]); |
600 | 418 | } |
601 | | |
602 | 418 | if (hashtab_insert(h, key, booldatum)) |
603 | 1 | goto err; |
604 | | |
605 | 417 | return 0; |
606 | 123 | err: |
607 | 123 | cond_destroy_bool(key, booldatum, NULL); |
608 | 123 | return -1; |
609 | 418 | } |
610 | | |
611 | | struct cond_insertf_data { |
612 | | struct policydb *p; |
613 | | cond_av_list_t *other; |
614 | | cond_av_list_t *head; |
615 | | cond_av_list_t *tail; |
616 | | }; |
617 | | |
618 | | static int cond_insertf(avtab_t *a __attribute__((unused)), avtab_key_t *k, |
619 | | avtab_datum_t *d, void *ptr) |
620 | 0 | { |
621 | 0 | struct cond_insertf_data *data = ptr; |
622 | 0 | struct policydb *p = data->p; |
623 | 0 | cond_av_list_t *other = data->other, *list, *cur; |
624 | 0 | avtab_ptr_t node_ptr; |
625 | 0 | uint8_t found; |
626 | | |
627 | | /* |
628 | | * For type rules we have to make certain there aren't any |
629 | | * conflicting rules by searching the te_avtab and the |
630 | | * cond_te_avtab. |
631 | | */ |
632 | 0 | if (k->specified & AVTAB_TYPE) { |
633 | 0 | if (avtab_search(&p->te_avtab, k)) { |
634 | 0 | WARN(NULL, |
635 | 0 | "security: type rule already exists outside of a conditional."); |
636 | 0 | return -1; |
637 | 0 | } |
638 | | /* |
639 | | * If we are reading the false list other will be a pointer to |
640 | | * the true list. We can have duplicate entries if there is only |
641 | | * 1 other entry and it is in our true list. |
642 | | * |
643 | | * If we are reading the true list (other == NULL) there shouldn't |
644 | | * be any other entries. |
645 | | */ |
646 | 0 | if (other) { |
647 | 0 | node_ptr = avtab_search_node(&p->te_cond_avtab, k); |
648 | 0 | if (node_ptr) { |
649 | 0 | if (avtab_search_node_next(node_ptr, |
650 | 0 | k->specified)) { |
651 | 0 | ERR(NULL, |
652 | 0 | "security: too many conflicting type rules."); |
653 | 0 | return -1; |
654 | 0 | } |
655 | 0 | found = 0; |
656 | 0 | for (cur = other; cur != NULL; |
657 | 0 | cur = cur->next) { |
658 | 0 | if (cur->node == node_ptr) { |
659 | 0 | found = 1; |
660 | 0 | break; |
661 | 0 | } |
662 | 0 | } |
663 | 0 | if (!found) { |
664 | 0 | ERR(NULL, |
665 | 0 | "security: conflicting type rules."); |
666 | 0 | return -1; |
667 | 0 | } |
668 | 0 | } |
669 | 0 | } else { |
670 | 0 | if (avtab_search(&p->te_cond_avtab, k)) { |
671 | 0 | ERR(NULL, |
672 | 0 | "security: conflicting type rules when adding type rule for true."); |
673 | 0 | return -1; |
674 | 0 | } |
675 | 0 | } |
676 | 0 | } |
677 | | |
678 | 0 | node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); |
679 | 0 | if (!node_ptr) { |
680 | 0 | ERR(NULL, "security: could not insert rule."); |
681 | 0 | return -1; |
682 | 0 | } |
683 | 0 | node_ptr->parse_context = (void *)1; |
684 | |
|
685 | 0 | list = malloc(sizeof(cond_av_list_t)); |
686 | 0 | if (!list) |
687 | 0 | return -1; |
688 | 0 | memset(list, 0, sizeof(cond_av_list_t)); |
689 | |
|
690 | 0 | list->node = node_ptr; |
691 | 0 | if (!data->head) |
692 | 0 | data->head = list; |
693 | 0 | else |
694 | 0 | data->tail->next = list; |
695 | 0 | data->tail = list; |
696 | 0 | return 0; |
697 | 0 | } |
698 | | |
699 | | static int cond_read_av_list(policydb_t *p, void *fp, cond_av_list_t **ret_list, |
700 | | cond_av_list_t *other) |
701 | 0 | { |
702 | 0 | unsigned int i; |
703 | 0 | int rc; |
704 | 0 | uint32_t buf[1], len; |
705 | 0 | struct cond_insertf_data data; |
706 | |
|
707 | 0 | *ret_list = NULL; |
708 | |
|
709 | 0 | rc = next_entry(buf, fp, sizeof(uint32_t)); |
710 | 0 | if (rc < 0) |
711 | 0 | return -1; |
712 | | |
713 | 0 | len = le32_to_cpu(buf[0]); |
714 | 0 | if (len == 0) { |
715 | 0 | return 0; |
716 | 0 | } |
717 | | |
718 | 0 | data.p = p; |
719 | 0 | data.other = other; |
720 | 0 | data.head = NULL; |
721 | 0 | data.tail = NULL; |
722 | 0 | for (i = 0; i < len; i++) { |
723 | 0 | rc = avtab_read_item(fp, &p->te_cond_avtab, cond_insertf, |
724 | 0 | &data); |
725 | 0 | if (rc) { |
726 | 0 | cond_av_list_destroy(data.head); |
727 | 0 | return rc; |
728 | 0 | } |
729 | 0 | } |
730 | | |
731 | 0 | *ret_list = data.head; |
732 | 0 | return 0; |
733 | 0 | } |
734 | | |
735 | | static int expr_isvalid(policydb_t *p, cond_expr_t *expr) |
736 | 527 | { |
737 | 527 | if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { |
738 | 48 | WARN(NULL, |
739 | 48 | "security: conditional expressions uses unknown operator."); |
740 | 48 | return 0; |
741 | 48 | } |
742 | | |
743 | 479 | if (expr->boolean > p->p_bools.nprim) { |
744 | 4 | WARN(NULL, |
745 | 4 | "security: conditional expressions uses unknown bool."); |
746 | 4 | return 0; |
747 | 4 | } |
748 | 475 | return 1; |
749 | 479 | } |
750 | | |
751 | | static int cond_read_node(policydb_t *p, cond_node_t *node, void *fp) |
752 | 1.88k | { |
753 | 1.88k | uint32_t buf[2], i, len; |
754 | 1.88k | int rc; |
755 | 1.88k | cond_expr_t *expr = NULL, *last = NULL; |
756 | | |
757 | 1.88k | rc = next_entry(buf, fp, sizeof(uint32_t)); |
758 | 1.88k | if (rc < 0) |
759 | 66 | goto err; |
760 | | |
761 | 1.81k | node->cur_state = le32_to_cpu(buf[0]); |
762 | | |
763 | 1.81k | rc = next_entry(buf, fp, sizeof(uint32_t)); |
764 | 1.81k | if (rc < 0) |
765 | 5 | goto err; |
766 | | |
767 | | /* expr */ |
768 | 1.81k | len = le32_to_cpu(buf[0]); |
769 | | |
770 | 2.28k | for (i = 0; i < len; i++) { |
771 | 589 | rc = next_entry(buf, fp, sizeof(uint32_t) * 2); |
772 | 589 | if (rc < 0) |
773 | 62 | goto err; |
774 | | |
775 | 527 | expr = malloc(sizeof(cond_expr_t)); |
776 | 527 | if (!expr) { |
777 | 0 | goto err; |
778 | 0 | } |
779 | 527 | memset(expr, 0, sizeof(cond_expr_t)); |
780 | | |
781 | 527 | expr->expr_type = le32_to_cpu(buf[0]); |
782 | 527 | expr->boolean = le32_to_cpu(buf[1]); |
783 | | |
784 | 527 | if (!expr_isvalid(p, expr)) { |
785 | 52 | free(expr); |
786 | 52 | goto err; |
787 | 52 | } |
788 | | |
789 | 475 | if (i == 0) { |
790 | 259 | node->expr = expr; |
791 | 259 | } else { |
792 | 216 | last->next = expr; |
793 | 216 | } |
794 | 475 | last = expr; |
795 | 475 | } |
796 | | |
797 | 1.70k | if (p->policy_type == POLICY_KERN) { |
798 | 0 | if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) |
799 | 0 | goto err; |
800 | 0 | if (cond_read_av_list(p, fp, &node->false_list, |
801 | 0 | node->true_list) != 0) |
802 | 0 | goto err; |
803 | 1.70k | } else { |
804 | 1.70k | if (avrule_read_list(p, &node->avtrue_list, fp)) |
805 | 70 | goto err; |
806 | 1.63k | if (avrule_read_list(p, &node->avfalse_list, fp)) |
807 | 11 | goto err; |
808 | 1.63k | } |
809 | | |
810 | 1.61k | if (p->policy_type != POLICY_KERN) { |
811 | 1.61k | rc = next_entry(buf, fp, sizeof(uint32_t)); |
812 | 1.61k | if (rc < 0) |
813 | 13 | goto err; |
814 | 1.60k | node->flags = le32_to_cpu(buf[0]); |
815 | 1.60k | } |
816 | | |
817 | 1.60k | return 0; |
818 | 279 | err: |
819 | 279 | cond_node_destroy(node); |
820 | 279 | free(node); |
821 | 279 | return -1; |
822 | 1.61k | } |
823 | | |
824 | | int cond_read_list(policydb_t *p, cond_list_t **list, void *fp) |
825 | 10.0k | { |
826 | 10.0k | cond_node_t *node, *last = NULL; |
827 | 10.0k | uint32_t buf[1], i, len; |
828 | 10.0k | int rc; |
829 | | |
830 | 10.0k | rc = next_entry(buf, fp, sizeof(uint32_t)); |
831 | 10.0k | if (rc < 0) |
832 | 6 | return -1; |
833 | | |
834 | 10.0k | len = le32_to_cpu(buf[0]); |
835 | | |
836 | 10.0k | rc = avtab_alloc(&p->te_cond_avtab, p->te_avtab.nel); |
837 | 10.0k | if (rc) |
838 | 0 | goto err; |
839 | | |
840 | 11.6k | for (i = 0; i < len; i++) { |
841 | 1.88k | node = malloc(sizeof(cond_node_t)); |
842 | 1.88k | if (!node) |
843 | 0 | goto err; |
844 | 1.88k | memset(node, 0, sizeof(cond_node_t)); |
845 | | |
846 | 1.88k | if (cond_read_node(p, node, fp) != 0) |
847 | 279 | goto err; |
848 | | |
849 | 1.60k | if (i == 0) { |
850 | 74 | *list = node; |
851 | 1.53k | } else { |
852 | 1.53k | last->next = node; |
853 | 1.53k | } |
854 | 1.60k | last = node; |
855 | 1.60k | } |
856 | 9.77k | return 0; |
857 | 279 | err: |
858 | 279 | return -1; |
859 | 10.0k | } |
860 | | |
861 | | /* Determine whether additional permissions are granted by the conditional |
862 | | * av table, and if so, add them to the result |
863 | | */ |
864 | | void cond_compute_av(avtab_t *ctab, avtab_key_t *key, |
865 | | struct sepol_av_decision *avd) |
866 | 0 | { |
867 | 0 | avtab_ptr_t node; |
868 | |
|
869 | 0 | if (!ctab || !key || !avd) |
870 | 0 | return; |
871 | | |
872 | 0 | for (node = avtab_search_node(ctab, key); node != NULL; |
873 | 0 | node = avtab_search_node_next(node, key->specified)) { |
874 | 0 | if ((uint16_t)(AVTAB_ALLOWED | AVTAB_ENABLED) == |
875 | 0 | (node->key.specified & (AVTAB_ALLOWED | AVTAB_ENABLED))) |
876 | 0 | avd->allowed |= node->datum.data; |
877 | 0 | if ((uint16_t)(AVTAB_AUDITDENY | AVTAB_ENABLED) == |
878 | 0 | (node->key.specified & (AVTAB_AUDITDENY | AVTAB_ENABLED))) |
879 | | /* Since a '0' in an auditdeny mask represents a |
880 | | * permission we do NOT want to audit (dontaudit), we use |
881 | | * the '&' operand to ensure that all '0's in the mask |
882 | | * are retained (much unlike the allow and auditallow cases). |
883 | | */ |
884 | 0 | avd->auditdeny &= node->datum.data; |
885 | 0 | if ((uint16_t)(AVTAB_AUDITALLOW | AVTAB_ENABLED) == |
886 | 0 | (node->key.specified & (AVTAB_AUDITALLOW | AVTAB_ENABLED))) |
887 | 0 | avd->auditallow |= node->datum.data; |
888 | 0 | } |
889 | 0 | return; |
890 | 0 | } |
891 | | |
892 | | avtab_datum_t *cond_av_list_search(avtab_key_t *key, cond_av_list_t *cond_list) |
893 | 0 | { |
894 | 0 | cond_av_list_t *cur_av; |
895 | |
|
896 | 0 | for (cur_av = cond_list; cur_av != NULL; cur_av = cur_av->next) { |
897 | 0 | if (cur_av->node->key.source_type == key->source_type && |
898 | 0 | cur_av->node->key.target_type == key->target_type && |
899 | 0 | cur_av->node->key.target_class == key->target_class) |
900 | | |
901 | 0 | return &cur_av->node->datum; |
902 | 0 | } |
903 | 0 | return NULL; |
904 | 0 | } |