Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* Authors: Karl MacMillan <kmacmillan@tresys.com> |
7c2b240e | 2 | * Frank Mayer <mayerf@tresys.com> |
1da177e4 LT |
3 | * |
4 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | |
5 | * This program is free software; you can redistribute it and/or modify | |
7c2b240e | 6 | * it under the terms of the GNU General Public License as published by |
1da177e4 LT |
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; | |
7c2b240e | 93 | struct cond_av_list *cur; |
1da177e4 LT |
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) | |
454d972c | 99 | printk(KERN_ERR "SELinux: expression result was undefined - disabling all rules.\n"); |
1da177e4 LT |
100 | /* turn the rules on or off */ |
101 | for (cur = node->true_list; cur != NULL; cur = cur->next) { | |
7c2b240e | 102 | if (new_state <= 0) |
782ebb99 | 103 | cur->node->key.specified &= ~AVTAB_ENABLED; |
7c2b240e | 104 | else |
782ebb99 | 105 | cur->node->key.specified |= AVTAB_ENABLED; |
1da177e4 LT |
106 | } |
107 | ||
108 | for (cur = node->false_list; cur != NULL; cur = cur->next) { | |
109 | /* -1 or 1 */ | |
7c2b240e | 110 | if (new_state) |
782ebb99 | 111 | cur->node->key.specified &= ~AVTAB_ENABLED; |
7c2b240e | 112 | else |
782ebb99 | 113 | cur->node->key.specified |= AVTAB_ENABLED; |
1da177e4 LT |
114 | } |
115 | } | |
116 | return 0; | |
117 | } | |
118 | ||
119 | int cond_policydb_init(struct policydb *p) | |
120 | { | |
121 | p->bool_val_to_struct = NULL; | |
122 | p->cond_list = NULL; | |
123 | if (avtab_init(&p->te_cond_avtab)) | |
124 | return -1; | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | static void cond_av_list_destroy(struct cond_av_list *list) | |
130 | { | |
131 | struct cond_av_list *cur, *next; | |
132 | for (cur = list; cur != NULL; cur = next) { | |
133 | next = cur->next; | |
134 | /* the avtab_ptr_t node is destroy by the avtab */ | |
135 | kfree(cur); | |
136 | } | |
137 | } | |
138 | ||
139 | static void cond_node_destroy(struct cond_node *node) | |
140 | { | |
141 | struct cond_expr *cur_expr, *next_expr; | |
142 | ||
143 | for (cur_expr = node->expr; cur_expr != NULL; cur_expr = next_expr) { | |
144 | next_expr = cur_expr->next; | |
145 | kfree(cur_expr); | |
146 | } | |
147 | cond_av_list_destroy(node->true_list); | |
148 | cond_av_list_destroy(node->false_list); | |
149 | kfree(node); | |
150 | } | |
151 | ||
152 | static void cond_list_destroy(struct cond_node *list) | |
153 | { | |
154 | struct cond_node *next, *cur; | |
155 | ||
156 | if (list == NULL) | |
157 | return; | |
158 | ||
159 | for (cur = list; cur != NULL; cur = next) { | |
160 | next = cur->next; | |
161 | cond_node_destroy(cur); | |
162 | } | |
163 | } | |
164 | ||
165 | void cond_policydb_destroy(struct policydb *p) | |
166 | { | |
9a5f04bf | 167 | kfree(p->bool_val_to_struct); |
1da177e4 LT |
168 | avtab_destroy(&p->te_cond_avtab); |
169 | cond_list_destroy(p->cond_list); | |
170 | } | |
171 | ||
172 | int cond_init_bool_indexes(struct policydb *p) | |
173 | { | |
9a5f04bf | 174 | kfree(p->bool_val_to_struct); |
7c2b240e EP |
175 | p->bool_val_to_struct = (struct cond_bool_datum **) |
176 | kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL); | |
1da177e4 LT |
177 | if (!p->bool_val_to_struct) |
178 | return -1; | |
179 | return 0; | |
180 | } | |
181 | ||
182 | int cond_destroy_bool(void *key, void *datum, void *p) | |
183 | { | |
9a5f04bf | 184 | kfree(key); |
1da177e4 LT |
185 | kfree(datum); |
186 | return 0; | |
187 | } | |
188 | ||
189 | int cond_index_bool(void *key, void *datum, void *datap) | |
190 | { | |
191 | struct policydb *p; | |
192 | struct cond_bool_datum *booldatum; | |
193 | ||
194 | booldatum = datum; | |
195 | p = datap; | |
196 | ||
197 | if (!booldatum->value || booldatum->value > p->p_bools.nprim) | |
198 | return -EINVAL; | |
199 | ||
200 | p->p_bool_val_to_name[booldatum->value - 1] = key; | |
7c2b240e | 201 | p->bool_val_to_struct[booldatum->value - 1] = booldatum; |
1da177e4 LT |
202 | |
203 | return 0; | |
204 | } | |
205 | ||
206 | static int bool_isvalid(struct cond_bool_datum *b) | |
207 | { | |
208 | if (!(b->state == 0 || b->state == 1)) | |
209 | return 0; | |
210 | return 1; | |
211 | } | |
212 | ||
213 | int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) | |
214 | { | |
215 | char *key = NULL; | |
216 | struct cond_bool_datum *booldatum; | |
b5bf6c55 AD |
217 | __le32 buf[3]; |
218 | u32 len; | |
1da177e4 LT |
219 | int rc; |
220 | ||
89d155ef | 221 | booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); |
1da177e4 LT |
222 | if (!booldatum) |
223 | return -1; | |
1da177e4 LT |
224 | |
225 | rc = next_entry(buf, fp, sizeof buf); | |
226 | if (rc < 0) | |
227 | goto err; | |
228 | ||
229 | booldatum->value = le32_to_cpu(buf[0]); | |
230 | booldatum->state = le32_to_cpu(buf[1]); | |
231 | ||
232 | if (!bool_isvalid(booldatum)) | |
233 | goto err; | |
234 | ||
235 | len = le32_to_cpu(buf[2]); | |
236 | ||
237 | key = kmalloc(len + 1, GFP_KERNEL); | |
238 | if (!key) | |
239 | goto err; | |
240 | rc = next_entry(key, fp, len); | |
241 | if (rc < 0) | |
242 | goto err; | |
243 | key[len] = 0; | |
244 | if (hashtab_insert(h, key, booldatum)) | |
245 | goto err; | |
246 | ||
247 | return 0; | |
248 | err: | |
249 | cond_destroy_bool(key, booldatum, NULL); | |
250 | return -1; | |
251 | } | |
252 | ||
7c2b240e | 253 | struct cond_insertf_data { |
782ebb99 SS |
254 | struct policydb *p; |
255 | struct cond_av_list *other; | |
256 | struct cond_av_list *head; | |
257 | struct cond_av_list *tail; | |
258 | }; | |
259 | ||
260 | static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *ptr) | |
261 | { | |
262 | struct cond_insertf_data *data = ptr; | |
263 | struct policydb *p = data->p; | |
264 | struct cond_av_list *other = data->other, *list, *cur; | |
1da177e4 | 265 | struct avtab_node *node_ptr; |
1da177e4 LT |
266 | u8 found; |
267 | ||
1da177e4 | 268 | |
782ebb99 SS |
269 | /* |
270 | * For type rules we have to make certain there aren't any | |
271 | * conflicting rules by searching the te_avtab and the | |
272 | * cond_te_avtab. | |
273 | */ | |
274 | if (k->specified & AVTAB_TYPE) { | |
275 | if (avtab_search(&p->te_avtab, k)) { | |
744ba35e | 276 | printk(KERN_ERR "SELinux: type rule already exists outside of a conditional.\n"); |
1da177e4 | 277 | goto err; |
782ebb99 | 278 | } |
1da177e4 | 279 | /* |
782ebb99 SS |
280 | * If we are reading the false list other will be a pointer to |
281 | * the true list. We can have duplicate entries if there is only | |
282 | * 1 other entry and it is in our true list. | |
283 | * | |
284 | * If we are reading the true list (other == NULL) there shouldn't | |
285 | * be any other entries. | |
1da177e4 | 286 | */ |
782ebb99 SS |
287 | if (other) { |
288 | node_ptr = avtab_search_node(&p->te_cond_avtab, k); | |
289 | if (node_ptr) { | |
290 | if (avtab_search_node_next(node_ptr, k->specified)) { | |
744ba35e | 291 | printk(KERN_ERR "SELinux: too many conflicting type rules.\n"); |
782ebb99 SS |
292 | goto err; |
293 | } | |
294 | found = 0; | |
295 | for (cur = other; cur != NULL; cur = cur->next) { | |
296 | if (cur->node == node_ptr) { | |
297 | found = 1; | |
298 | break; | |
1da177e4 LT |
299 | } |
300 | } | |
782ebb99 | 301 | if (!found) { |
744ba35e | 302 | printk(KERN_ERR "SELinux: conflicting type rules.\n"); |
1da177e4 LT |
303 | goto err; |
304 | } | |
305 | } | |
782ebb99 SS |
306 | } else { |
307 | if (avtab_search(&p->te_cond_avtab, k)) { | |
744ba35e | 308 | printk(KERN_ERR "SELinux: conflicting type rules when adding type rule for true.\n"); |
782ebb99 SS |
309 | goto err; |
310 | } | |
1da177e4 | 311 | } |
782ebb99 | 312 | } |
1da177e4 | 313 | |
782ebb99 SS |
314 | node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); |
315 | if (!node_ptr) { | |
744ba35e | 316 | printk(KERN_ERR "SELinux: could not insert rule.\n"); |
782ebb99 | 317 | goto err; |
1da177e4 LT |
318 | } |
319 | ||
89d155ef | 320 | list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); |
782ebb99 SS |
321 | if (!list) |
322 | goto err; | |
782ebb99 SS |
323 | |
324 | list->node = node_ptr; | |
325 | if (!data->head) | |
326 | data->head = list; | |
327 | else | |
328 | data->tail->next = list; | |
329 | data->tail = list; | |
1da177e4 | 330 | return 0; |
782ebb99 | 331 | |
1da177e4 | 332 | err: |
782ebb99 SS |
333 | cond_av_list_destroy(data->head); |
334 | data->head = NULL; | |
1da177e4 LT |
335 | return -1; |
336 | } | |
337 | ||
782ebb99 SS |
338 | static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) |
339 | { | |
340 | int i, rc; | |
b5bf6c55 AD |
341 | __le32 buf[1]; |
342 | u32 len; | |
782ebb99 SS |
343 | struct cond_insertf_data data; |
344 | ||
345 | *ret_list = NULL; | |
346 | ||
347 | len = 0; | |
348 | rc = next_entry(buf, fp, sizeof(u32)); | |
349 | if (rc < 0) | |
350 | return -1; | |
351 | ||
352 | len = le32_to_cpu(buf[0]); | |
7c2b240e | 353 | if (len == 0) |
782ebb99 | 354 | return 0; |
782ebb99 SS |
355 | |
356 | data.p = p; | |
357 | data.other = other; | |
358 | data.head = NULL; | |
359 | data.tail = NULL; | |
360 | for (i = 0; i < len; i++) { | |
45e5421e SS |
361 | rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf, |
362 | &data); | |
782ebb99 SS |
363 | if (rc) |
364 | return rc; | |
365 | ||
366 | } | |
367 | ||
368 | *ret_list = data.head; | |
369 | return 0; | |
370 | } | |
371 | ||
1da177e4 LT |
372 | static int expr_isvalid(struct policydb *p, struct cond_expr *expr) |
373 | { | |
374 | if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { | |
744ba35e | 375 | printk(KERN_ERR "SELinux: conditional expressions uses unknown operator.\n"); |
1da177e4 LT |
376 | return 0; |
377 | } | |
378 | ||
379 | if (expr->bool > p->p_bools.nprim) { | |
744ba35e | 380 | printk(KERN_ERR "SELinux: conditional expressions uses unknown bool.\n"); |
1da177e4 LT |
381 | return 0; |
382 | } | |
383 | return 1; | |
384 | } | |
385 | ||
386 | static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | |
387 | { | |
b5bf6c55 AD |
388 | __le32 buf[2]; |
389 | u32 len, i; | |
1da177e4 LT |
390 | int rc; |
391 | struct cond_expr *expr = NULL, *last = NULL; | |
392 | ||
393 | rc = next_entry(buf, fp, sizeof(u32)); | |
394 | if (rc < 0) | |
395 | return -1; | |
396 | ||
397 | node->cur_state = le32_to_cpu(buf[0]); | |
398 | ||
399 | len = 0; | |
400 | rc = next_entry(buf, fp, sizeof(u32)); | |
401 | if (rc < 0) | |
402 | return -1; | |
403 | ||
404 | /* expr */ | |
405 | len = le32_to_cpu(buf[0]); | |
406 | ||
7c2b240e | 407 | for (i = 0; i < len; i++) { |
1da177e4 LT |
408 | rc = next_entry(buf, fp, sizeof(u32) * 2); |
409 | if (rc < 0) | |
410 | goto err; | |
411 | ||
89d155ef | 412 | expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); |
7c2b240e | 413 | if (!expr) |
1da177e4 | 414 | goto err; |
1da177e4 LT |
415 | |
416 | expr->expr_type = le32_to_cpu(buf[0]); | |
417 | expr->bool = le32_to_cpu(buf[1]); | |
418 | ||
419 | if (!expr_isvalid(p, expr)) { | |
420 | kfree(expr); | |
421 | goto err; | |
422 | } | |
423 | ||
7c2b240e | 424 | if (i == 0) |
1da177e4 | 425 | node->expr = expr; |
7c2b240e | 426 | else |
1da177e4 | 427 | last->next = expr; |
1da177e4 LT |
428 | last = expr; |
429 | } | |
430 | ||
431 | if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) | |
432 | goto err; | |
433 | if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) | |
434 | goto err; | |
435 | return 0; | |
436 | err: | |
437 | cond_node_destroy(node); | |
438 | return -1; | |
439 | } | |
440 | ||
441 | int cond_read_list(struct policydb *p, void *fp) | |
442 | { | |
443 | struct cond_node *node, *last = NULL; | |
b5bf6c55 AD |
444 | __le32 buf[1]; |
445 | u32 i, len; | |
1da177e4 LT |
446 | int rc; |
447 | ||
448 | rc = next_entry(buf, fp, sizeof buf); | |
449 | if (rc < 0) | |
450 | return -1; | |
451 | ||
452 | len = le32_to_cpu(buf[0]); | |
453 | ||
3232c110 YN |
454 | rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel); |
455 | if (rc) | |
456 | goto err; | |
457 | ||
1da177e4 | 458 | for (i = 0; i < len; i++) { |
89d155ef | 459 | node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); |
1da177e4 LT |
460 | if (!node) |
461 | goto err; | |
1da177e4 LT |
462 | |
463 | if (cond_read_node(p, node, fp) != 0) | |
464 | goto err; | |
465 | ||
7c2b240e | 466 | if (i == 0) |
1da177e4 | 467 | p->cond_list = node; |
7c2b240e | 468 | else |
1da177e4 | 469 | last->next = node; |
1da177e4 LT |
470 | last = node; |
471 | } | |
472 | return 0; | |
473 | err: | |
474 | cond_list_destroy(p->cond_list); | |
782ebb99 | 475 | p->cond_list = NULL; |
1da177e4 LT |
476 | return -1; |
477 | } | |
478 | ||
479 | /* Determine whether additional permissions are granted by the conditional | |
480 | * av table, and if so, add them to the result | |
481 | */ | |
482 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd) | |
483 | { | |
484 | struct avtab_node *node; | |
485 | ||
7c2b240e | 486 | if (!ctab || !key || !avd) |
1da177e4 LT |
487 | return; |
488 | ||
7c2b240e | 489 | for (node = avtab_search_node(ctab, key); node != NULL; |
782ebb99 | 490 | node = avtab_search_node_next(node, key->specified)) { |
7c2b240e EP |
491 | if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) == |
492 | (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) | |
782ebb99 | 493 | avd->allowed |= node->datum.data; |
7c2b240e EP |
494 | if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) == |
495 | (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) | |
1da177e4 LT |
496 | /* Since a '0' in an auditdeny mask represents a |
497 | * permission we do NOT want to audit (dontaudit), we use | |
498 | * the '&' operand to ensure that all '0's in the mask | |
499 | * are retained (much unlike the allow and auditallow cases). | |
500 | */ | |
782ebb99 | 501 | avd->auditdeny &= node->datum.data; |
7c2b240e EP |
502 | if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == |
503 | (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) | |
782ebb99 | 504 | avd->auditallow |= node->datum.data; |
1da177e4 LT |
505 | } |
506 | return; | |
507 | } |