diff options
Diffstat (limited to 'security/selinux/ss/conditional.c')
-rw-r--r-- | security/selinux/ss/conditional.c | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c new file mode 100644 index 000000000000..b53441184aca --- /dev/null +++ b/security/selinux/ss/conditional.c | |||
@@ -0,0 +1,489 @@ | |||
1 | /* Authors: Karl MacMillan <kmacmillan@tresys.com> | ||
2 | * Frank Mayer <mayerf@tresys.com> | ||
3 | * | ||
4 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, version 2. | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/string.h> | ||
13 | #include <linux/spinlock.h> | ||
14 | #include <asm/semaphore.h> | ||
15 | #include <linux/slab.h> | ||
16 | |||
17 | #include "security.h" | ||
18 | #include "conditional.h" | ||
19 | |||
20 | /* | ||
21 | * cond_evaluate_expr evaluates a conditional expr | ||
22 | * in reverse polish notation. It returns true (1), false (0), | ||
23 | * or undefined (-1). Undefined occurs when the expression | ||
24 | * exceeds the stack depth of COND_EXPR_MAXDEPTH. | ||
25 | */ | ||
26 | static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr) | ||
27 | { | ||
28 | |||
29 | struct cond_expr *cur; | ||
30 | int s[COND_EXPR_MAXDEPTH]; | ||
31 | int sp = -1; | ||
32 | |||
33 | for (cur = expr; cur != NULL; cur = cur->next) { | ||
34 | switch (cur->expr_type) { | ||
35 | case COND_BOOL: | ||
36 | if (sp == (COND_EXPR_MAXDEPTH - 1)) | ||
37 | return -1; | ||
38 | sp++; | ||
39 | s[sp] = p->bool_val_to_struct[cur->bool - 1]->state; | ||
40 | break; | ||
41 | case COND_NOT: | ||
42 | if (sp < 0) | ||
43 | return -1; | ||
44 | s[sp] = !s[sp]; | ||
45 | break; | ||
46 | case COND_OR: | ||
47 | if (sp < 1) | ||
48 | return -1; | ||
49 | sp--; | ||
50 | s[sp] |= s[sp + 1]; | ||
51 | break; | ||
52 | case COND_AND: | ||
53 | if (sp < 1) | ||
54 | return -1; | ||
55 | sp--; | ||
56 | s[sp] &= s[sp + 1]; | ||
57 | break; | ||
58 | case COND_XOR: | ||
59 | if (sp < 1) | ||
60 | return -1; | ||
61 | sp--; | ||
62 | s[sp] ^= s[sp + 1]; | ||
63 | break; | ||
64 | case COND_EQ: | ||
65 | if (sp < 1) | ||
66 | return -1; | ||
67 | sp--; | ||
68 | s[sp] = (s[sp] == s[sp + 1]); | ||
69 | break; | ||
70 | case COND_NEQ: | ||
71 | if (sp < 1) | ||
72 | return -1; | ||
73 | sp--; | ||
74 | s[sp] = (s[sp] != s[sp + 1]); | ||
75 | break; | ||
76 | default: | ||
77 | return -1; | ||
78 | } | ||
79 | } | ||
80 | return s[0]; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * evaluate_cond_node evaluates the conditional stored in | ||
85 | * a struct cond_node and if the result is different than the | ||
86 | * current state of the node it sets the rules in the true/false | ||
87 | * list appropriately. If the result of the expression is undefined | ||
88 | * all of the rules are disabled for safety. | ||
89 | */ | ||
90 | int evaluate_cond_node(struct policydb *p, struct cond_node *node) | ||
91 | { | ||
92 | int new_state; | ||
93 | struct cond_av_list* cur; | ||
94 | |||
95 | new_state = cond_evaluate_expr(p, node->expr); | ||
96 | if (new_state != node->cur_state) { | ||
97 | node->cur_state = new_state; | ||
98 | if (new_state == -1) | ||
99 | printk(KERN_ERR "security: expression result was undefined - disabling all rules.\n"); | ||
100 | /* turn the rules on or off */ | ||
101 | for (cur = node->true_list; cur != NULL; cur = cur->next) { | ||
102 | if (new_state <= 0) { | ||
103 | cur->node->datum.specified &= ~AVTAB_ENABLED; | ||
104 | } else { | ||
105 | cur->node->datum.specified |= AVTAB_ENABLED; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | for (cur = node->false_list; cur != NULL; cur = cur->next) { | ||
110 | /* -1 or 1 */ | ||
111 | if (new_state) { | ||
112 | cur->node->datum.specified &= ~AVTAB_ENABLED; | ||
113 | } else { | ||
114 | cur->node->datum.specified |= AVTAB_ENABLED; | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | int cond_policydb_init(struct policydb *p) | ||
122 | { | ||
123 | p->bool_val_to_struct = NULL; | ||
124 | p->cond_list = NULL; | ||
125 | if (avtab_init(&p->te_cond_avtab)) | ||
126 | return -1; | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static void cond_av_list_destroy(struct cond_av_list *list) | ||
132 | { | ||
133 | struct cond_av_list *cur, *next; | ||
134 | for (cur = list; cur != NULL; cur = next) { | ||
135 | next = cur->next; | ||
136 | /* the avtab_ptr_t node is destroy by the avtab */ | ||
137 | kfree(cur); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static void cond_node_destroy(struct cond_node *node) | ||
142 | { | ||
143 | struct cond_expr *cur_expr, *next_expr; | ||
144 | |||
145 | for (cur_expr = node->expr; cur_expr != NULL; cur_expr = next_expr) { | ||
146 | next_expr = cur_expr->next; | ||
147 | kfree(cur_expr); | ||
148 | } | ||
149 | cond_av_list_destroy(node->true_list); | ||
150 | cond_av_list_destroy(node->false_list); | ||
151 | kfree(node); | ||
152 | } | ||
153 | |||
154 | static void cond_list_destroy(struct cond_node *list) | ||
155 | { | ||
156 | struct cond_node *next, *cur; | ||
157 | |||
158 | if (list == NULL) | ||
159 | return; | ||
160 | |||
161 | for (cur = list; cur != NULL; cur = next) { | ||
162 | next = cur->next; | ||
163 | cond_node_destroy(cur); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | void cond_policydb_destroy(struct policydb *p) | ||
168 | { | ||
169 | if (p->bool_val_to_struct != NULL) | ||
170 | kfree(p->bool_val_to_struct); | ||
171 | avtab_destroy(&p->te_cond_avtab); | ||
172 | cond_list_destroy(p->cond_list); | ||
173 | } | ||
174 | |||
175 | int cond_init_bool_indexes(struct policydb *p) | ||
176 | { | ||
177 | if (p->bool_val_to_struct) | ||
178 | kfree(p->bool_val_to_struct); | ||
179 | p->bool_val_to_struct = (struct cond_bool_datum**) | ||
180 | kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum*), GFP_KERNEL); | ||
181 | if (!p->bool_val_to_struct) | ||
182 | return -1; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | int cond_destroy_bool(void *key, void *datum, void *p) | ||
187 | { | ||
188 | if (key) | ||
189 | kfree(key); | ||
190 | kfree(datum); | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | int cond_index_bool(void *key, void *datum, void *datap) | ||
195 | { | ||
196 | struct policydb *p; | ||
197 | struct cond_bool_datum *booldatum; | ||
198 | |||
199 | booldatum = datum; | ||
200 | p = datap; | ||
201 | |||
202 | if (!booldatum->value || booldatum->value > p->p_bools.nprim) | ||
203 | return -EINVAL; | ||
204 | |||
205 | p->p_bool_val_to_name[booldatum->value - 1] = key; | ||
206 | p->bool_val_to_struct[booldatum->value -1] = booldatum; | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int bool_isvalid(struct cond_bool_datum *b) | ||
212 | { | ||
213 | if (!(b->state == 0 || b->state == 1)) | ||
214 | return 0; | ||
215 | return 1; | ||
216 | } | ||
217 | |||
218 | int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) | ||
219 | { | ||
220 | char *key = NULL; | ||
221 | struct cond_bool_datum *booldatum; | ||
222 | u32 buf[3], len; | ||
223 | int rc; | ||
224 | |||
225 | booldatum = kmalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); | ||
226 | if (!booldatum) | ||
227 | return -1; | ||
228 | memset(booldatum, 0, sizeof(struct cond_bool_datum)); | ||
229 | |||
230 | rc = next_entry(buf, fp, sizeof buf); | ||
231 | if (rc < 0) | ||
232 | goto err; | ||
233 | |||
234 | booldatum->value = le32_to_cpu(buf[0]); | ||
235 | booldatum->state = le32_to_cpu(buf[1]); | ||
236 | |||
237 | if (!bool_isvalid(booldatum)) | ||
238 | goto err; | ||
239 | |||
240 | len = le32_to_cpu(buf[2]); | ||
241 | |||
242 | key = kmalloc(len + 1, GFP_KERNEL); | ||
243 | if (!key) | ||
244 | goto err; | ||
245 | rc = next_entry(key, fp, len); | ||
246 | if (rc < 0) | ||
247 | goto err; | ||
248 | key[len] = 0; | ||
249 | if (hashtab_insert(h, key, booldatum)) | ||
250 | goto err; | ||
251 | |||
252 | return 0; | ||
253 | err: | ||
254 | cond_destroy_bool(key, booldatum, NULL); | ||
255 | return -1; | ||
256 | } | ||
257 | |||
258 | static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, | ||
259 | struct cond_av_list *other) | ||
260 | { | ||
261 | struct cond_av_list *list, *last = NULL, *cur; | ||
262 | struct avtab_key key; | ||
263 | struct avtab_datum datum; | ||
264 | struct avtab_node *node_ptr; | ||
265 | int rc; | ||
266 | u32 buf[1], i, len; | ||
267 | u8 found; | ||
268 | |||
269 | *ret_list = NULL; | ||
270 | |||
271 | len = 0; | ||
272 | rc = next_entry(buf, fp, sizeof buf); | ||
273 | if (rc < 0) | ||
274 | return -1; | ||
275 | |||
276 | len = le32_to_cpu(buf[0]); | ||
277 | if (len == 0) { | ||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | for (i = 0; i < len; i++) { | ||
282 | if (avtab_read_item(fp, &datum, &key)) | ||
283 | goto err; | ||
284 | |||
285 | /* | ||
286 | * For type rules we have to make certain there aren't any | ||
287 | * conflicting rules by searching the te_avtab and the | ||
288 | * cond_te_avtab. | ||
289 | */ | ||
290 | if (datum.specified & AVTAB_TYPE) { | ||
291 | if (avtab_search(&p->te_avtab, &key, AVTAB_TYPE)) { | ||
292 | printk("security: type rule already exists outside of a conditional."); | ||
293 | goto err; | ||
294 | } | ||
295 | /* | ||
296 | * If we are reading the false list other will be a pointer to | ||
297 | * the true list. We can have duplicate entries if there is only | ||
298 | * 1 other entry and it is in our true list. | ||
299 | * | ||
300 | * If we are reading the true list (other == NULL) there shouldn't | ||
301 | * be any other entries. | ||
302 | */ | ||
303 | if (other) { | ||
304 | node_ptr = avtab_search_node(&p->te_cond_avtab, &key, AVTAB_TYPE); | ||
305 | if (node_ptr) { | ||
306 | if (avtab_search_node_next(node_ptr, AVTAB_TYPE)) { | ||
307 | printk("security: too many conflicting type rules."); | ||
308 | goto err; | ||
309 | } | ||
310 | found = 0; | ||
311 | for (cur = other; cur != NULL; cur = cur->next) { | ||
312 | if (cur->node == node_ptr) { | ||
313 | found = 1; | ||
314 | break; | ||
315 | } | ||
316 | } | ||
317 | if (!found) { | ||
318 | printk("security: conflicting type rules."); | ||
319 | goto err; | ||
320 | } | ||
321 | } | ||
322 | } else { | ||
323 | if (avtab_search(&p->te_cond_avtab, &key, AVTAB_TYPE)) { | ||
324 | printk("security: conflicting type rules when adding type rule for true."); | ||
325 | goto err; | ||
326 | } | ||
327 | } | ||
328 | } | ||
329 | node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, &key, &datum); | ||
330 | if (!node_ptr) { | ||
331 | printk("security: could not insert rule."); | ||
332 | goto err; | ||
333 | } | ||
334 | |||
335 | list = kmalloc(sizeof(struct cond_av_list), GFP_KERNEL); | ||
336 | if (!list) | ||
337 | goto err; | ||
338 | memset(list, 0, sizeof(struct cond_av_list)); | ||
339 | |||
340 | list->node = node_ptr; | ||
341 | if (i == 0) | ||
342 | *ret_list = list; | ||
343 | else | ||
344 | last->next = list; | ||
345 | last = list; | ||
346 | |||
347 | } | ||
348 | |||
349 | return 0; | ||
350 | err: | ||
351 | cond_av_list_destroy(*ret_list); | ||
352 | *ret_list = NULL; | ||
353 | return -1; | ||
354 | } | ||
355 | |||
356 | static int expr_isvalid(struct policydb *p, struct cond_expr *expr) | ||
357 | { | ||
358 | if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { | ||
359 | printk("security: conditional expressions uses unknown operator.\n"); | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | if (expr->bool > p->p_bools.nprim) { | ||
364 | printk("security: conditional expressions uses unknown bool.\n"); | ||
365 | return 0; | ||
366 | } | ||
367 | return 1; | ||
368 | } | ||
369 | |||
370 | static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | ||
371 | { | ||
372 | u32 buf[2], len, i; | ||
373 | int rc; | ||
374 | struct cond_expr *expr = NULL, *last = NULL; | ||
375 | |||
376 | rc = next_entry(buf, fp, sizeof(u32)); | ||
377 | if (rc < 0) | ||
378 | return -1; | ||
379 | |||
380 | node->cur_state = le32_to_cpu(buf[0]); | ||
381 | |||
382 | len = 0; | ||
383 | rc = next_entry(buf, fp, sizeof(u32)); | ||
384 | if (rc < 0) | ||
385 | return -1; | ||
386 | |||
387 | /* expr */ | ||
388 | len = le32_to_cpu(buf[0]); | ||
389 | |||
390 | for (i = 0; i < len; i++ ) { | ||
391 | rc = next_entry(buf, fp, sizeof(u32) * 2); | ||
392 | if (rc < 0) | ||
393 | goto err; | ||
394 | |||
395 | expr = kmalloc(sizeof(struct cond_expr), GFP_KERNEL); | ||
396 | if (!expr) { | ||
397 | goto err; | ||
398 | } | ||
399 | memset(expr, 0, sizeof(struct cond_expr)); | ||
400 | |||
401 | expr->expr_type = le32_to_cpu(buf[0]); | ||
402 | expr->bool = le32_to_cpu(buf[1]); | ||
403 | |||
404 | if (!expr_isvalid(p, expr)) { | ||
405 | kfree(expr); | ||
406 | goto err; | ||
407 | } | ||
408 | |||
409 | if (i == 0) { | ||
410 | node->expr = expr; | ||
411 | } else { | ||
412 | last->next = expr; | ||
413 | } | ||
414 | last = expr; | ||
415 | } | ||
416 | |||
417 | if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) | ||
418 | goto err; | ||
419 | if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) | ||
420 | goto err; | ||
421 | return 0; | ||
422 | err: | ||
423 | cond_node_destroy(node); | ||
424 | return -1; | ||
425 | } | ||
426 | |||
427 | int cond_read_list(struct policydb *p, void *fp) | ||
428 | { | ||
429 | struct cond_node *node, *last = NULL; | ||
430 | u32 buf[1], i, len; | ||
431 | int rc; | ||
432 | |||
433 | rc = next_entry(buf, fp, sizeof buf); | ||
434 | if (rc < 0) | ||
435 | return -1; | ||
436 | |||
437 | len = le32_to_cpu(buf[0]); | ||
438 | |||
439 | for (i = 0; i < len; i++) { | ||
440 | node = kmalloc(sizeof(struct cond_node), GFP_KERNEL); | ||
441 | if (!node) | ||
442 | goto err; | ||
443 | memset(node, 0, sizeof(struct cond_node)); | ||
444 | |||
445 | if (cond_read_node(p, node, fp) != 0) | ||
446 | goto err; | ||
447 | |||
448 | if (i == 0) { | ||
449 | p->cond_list = node; | ||
450 | } else { | ||
451 | last->next = node; | ||
452 | } | ||
453 | last = node; | ||
454 | } | ||
455 | return 0; | ||
456 | err: | ||
457 | cond_list_destroy(p->cond_list); | ||
458 | return -1; | ||
459 | } | ||
460 | |||
461 | /* Determine whether additional permissions are granted by the conditional | ||
462 | * av table, and if so, add them to the result | ||
463 | */ | ||
464 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd) | ||
465 | { | ||
466 | struct avtab_node *node; | ||
467 | |||
468 | if(!ctab || !key || !avd) | ||
469 | return; | ||
470 | |||
471 | for(node = avtab_search_node(ctab, key, AVTAB_AV); node != NULL; | ||
472 | node = avtab_search_node_next(node, AVTAB_AV)) { | ||
473 | if ( (__u32) (AVTAB_ALLOWED|AVTAB_ENABLED) == | ||
474 | (node->datum.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) | ||
475 | avd->allowed |= avtab_allowed(&node->datum); | ||
476 | if ( (__u32) (AVTAB_AUDITDENY|AVTAB_ENABLED) == | ||
477 | (node->datum.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) | ||
478 | /* Since a '0' in an auditdeny mask represents a | ||
479 | * permission we do NOT want to audit (dontaudit), we use | ||
480 | * the '&' operand to ensure that all '0's in the mask | ||
481 | * are retained (much unlike the allow and auditallow cases). | ||
482 | */ | ||
483 | avd->auditdeny &= avtab_auditdeny(&node->datum); | ||
484 | if ( (__u32) (AVTAB_AUDITALLOW|AVTAB_ENABLED) == | ||
485 | (node->datum.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) | ||
486 | avd->auditallow |= avtab_auditallow(&node->datum); | ||
487 | } | ||
488 | return; | ||
489 | } | ||