diff options
Diffstat (limited to 'security/selinux/ss')
-rw-r--r-- | security/selinux/ss/Makefile | 9 | ||||
-rw-r--r-- | security/selinux/ss/avtab.c | 399 | ||||
-rw-r--r-- | security/selinux/ss/avtab.h | 85 | ||||
-rw-r--r-- | security/selinux/ss/conditional.c | 489 | ||||
-rw-r--r-- | security/selinux/ss/conditional.h | 77 | ||||
-rw-r--r-- | security/selinux/ss/constraint.h | 61 | ||||
-rw-r--r-- | security/selinux/ss/context.h | 107 | ||||
-rw-r--r-- | security/selinux/ss/ebitmap.c | 293 | ||||
-rw-r--r-- | security/selinux/ss/ebitmap.h | 48 | ||||
-rw-r--r-- | security/selinux/ss/hashtab.c | 167 | ||||
-rw-r--r-- | security/selinux/ss/hashtab.h | 87 | ||||
-rw-r--r-- | security/selinux/ss/mls.c | 527 | ||||
-rw-r--r-- | security/selinux/ss/mls.h | 42 | ||||
-rw-r--r-- | security/selinux/ss/mls_types.h | 56 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 1843 | ||||
-rw-r--r-- | security/selinux/ss/policydb.h | 275 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 1777 | ||||
-rw-r--r-- | security/selinux/ss/services.h | 15 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.c | 305 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.h | 59 | ||||
-rw-r--r-- | security/selinux/ss/symtab.c | 44 | ||||
-rw-r--r-- | security/selinux/ss/symtab.h | 23 |
22 files changed, 6788 insertions, 0 deletions
diff --git a/security/selinux/ss/Makefile b/security/selinux/ss/Makefile new file mode 100644 index 000000000000..bad78779b9b0 --- /dev/null +++ b/security/selinux/ss/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Makefile for building the SELinux security server as part of the kernel tree. | ||
3 | # | ||
4 | |||
5 | EXTRA_CFLAGS += -Isecurity/selinux/include | ||
6 | obj-y := ss.o | ||
7 | |||
8 | ss-y := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o conditional.o mls.o | ||
9 | |||
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c new file mode 100644 index 000000000000..f238c034c44e --- /dev/null +++ b/security/selinux/ss/avtab.c | |||
@@ -0,0 +1,399 @@ | |||
1 | /* | ||
2 | * Implementation of the access vector table type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | |||
7 | /* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
8 | * | ||
9 | * Added conditional policy language extensions | ||
10 | * | ||
11 | * Copyright (C) 2003 Tresys Technology, LLC | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation, version 2. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/vmalloc.h> | ||
20 | #include <linux/errno.h> | ||
21 | |||
22 | #include "avtab.h" | ||
23 | #include "policydb.h" | ||
24 | |||
25 | #define AVTAB_HASH(keyp) \ | ||
26 | ((keyp->target_class + \ | ||
27 | (keyp->target_type << 2) + \ | ||
28 | (keyp->source_type << 9)) & \ | ||
29 | AVTAB_HASH_MASK) | ||
30 | |||
31 | static kmem_cache_t *avtab_node_cachep; | ||
32 | |||
33 | static struct avtab_node* | ||
34 | avtab_insert_node(struct avtab *h, int hvalue, | ||
35 | struct avtab_node * prev, struct avtab_node * cur, | ||
36 | struct avtab_key *key, struct avtab_datum *datum) | ||
37 | { | ||
38 | struct avtab_node * newnode; | ||
39 | newnode = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL); | ||
40 | if (newnode == NULL) | ||
41 | return NULL; | ||
42 | memset(newnode, 0, sizeof(struct avtab_node)); | ||
43 | newnode->key = *key; | ||
44 | newnode->datum = *datum; | ||
45 | if (prev) { | ||
46 | newnode->next = prev->next; | ||
47 | prev->next = newnode; | ||
48 | } else { | ||
49 | newnode->next = h->htable[hvalue]; | ||
50 | h->htable[hvalue] = newnode; | ||
51 | } | ||
52 | |||
53 | h->nel++; | ||
54 | return newnode; | ||
55 | } | ||
56 | |||
57 | static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum) | ||
58 | { | ||
59 | int hvalue; | ||
60 | struct avtab_node *prev, *cur, *newnode; | ||
61 | |||
62 | if (!h) | ||
63 | return -EINVAL; | ||
64 | |||
65 | hvalue = AVTAB_HASH(key); | ||
66 | for (prev = NULL, cur = h->htable[hvalue]; | ||
67 | cur; | ||
68 | prev = cur, cur = cur->next) { | ||
69 | if (key->source_type == cur->key.source_type && | ||
70 | key->target_type == cur->key.target_type && | ||
71 | key->target_class == cur->key.target_class && | ||
72 | (datum->specified & cur->datum.specified)) | ||
73 | return -EEXIST; | ||
74 | if (key->source_type < cur->key.source_type) | ||
75 | break; | ||
76 | if (key->source_type == cur->key.source_type && | ||
77 | key->target_type < cur->key.target_type) | ||
78 | break; | ||
79 | if (key->source_type == cur->key.source_type && | ||
80 | key->target_type == cur->key.target_type && | ||
81 | key->target_class < cur->key.target_class) | ||
82 | break; | ||
83 | } | ||
84 | |||
85 | newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); | ||
86 | if(!newnode) | ||
87 | return -ENOMEM; | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | /* Unlike avtab_insert(), this function allow multiple insertions of the same | ||
93 | * key/specified mask into the table, as needed by the conditional avtab. | ||
94 | * It also returns a pointer to the node inserted. | ||
95 | */ | ||
96 | struct avtab_node * | ||
97 | avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_datum * datum) | ||
98 | { | ||
99 | int hvalue; | ||
100 | struct avtab_node *prev, *cur, *newnode; | ||
101 | |||
102 | if (!h) | ||
103 | return NULL; | ||
104 | hvalue = AVTAB_HASH(key); | ||
105 | for (prev = NULL, cur = h->htable[hvalue]; | ||
106 | cur; | ||
107 | prev = cur, cur = cur->next) { | ||
108 | if (key->source_type == cur->key.source_type && | ||
109 | key->target_type == cur->key.target_type && | ||
110 | key->target_class == cur->key.target_class && | ||
111 | (datum->specified & cur->datum.specified)) | ||
112 | break; | ||
113 | if (key->source_type < cur->key.source_type) | ||
114 | break; | ||
115 | if (key->source_type == cur->key.source_type && | ||
116 | key->target_type < cur->key.target_type) | ||
117 | break; | ||
118 | if (key->source_type == cur->key.source_type && | ||
119 | key->target_type == cur->key.target_type && | ||
120 | key->target_class < cur->key.target_class) | ||
121 | break; | ||
122 | } | ||
123 | newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); | ||
124 | |||
125 | return newnode; | ||
126 | } | ||
127 | |||
128 | struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key, int specified) | ||
129 | { | ||
130 | int hvalue; | ||
131 | struct avtab_node *cur; | ||
132 | |||
133 | if (!h) | ||
134 | return NULL; | ||
135 | |||
136 | hvalue = AVTAB_HASH(key); | ||
137 | for (cur = h->htable[hvalue]; cur; cur = cur->next) { | ||
138 | if (key->source_type == cur->key.source_type && | ||
139 | key->target_type == cur->key.target_type && | ||
140 | key->target_class == cur->key.target_class && | ||
141 | (specified & cur->datum.specified)) | ||
142 | return &cur->datum; | ||
143 | |||
144 | if (key->source_type < cur->key.source_type) | ||
145 | break; | ||
146 | if (key->source_type == cur->key.source_type && | ||
147 | key->target_type < cur->key.target_type) | ||
148 | break; | ||
149 | if (key->source_type == cur->key.source_type && | ||
150 | key->target_type == cur->key.target_type && | ||
151 | key->target_class < cur->key.target_class) | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | return NULL; | ||
156 | } | ||
157 | |||
158 | /* This search function returns a node pointer, and can be used in | ||
159 | * conjunction with avtab_search_next_node() | ||
160 | */ | ||
161 | struct avtab_node* | ||
162 | avtab_search_node(struct avtab *h, struct avtab_key *key, int specified) | ||
163 | { | ||
164 | int hvalue; | ||
165 | struct avtab_node *cur; | ||
166 | |||
167 | if (!h) | ||
168 | return NULL; | ||
169 | |||
170 | hvalue = AVTAB_HASH(key); | ||
171 | for (cur = h->htable[hvalue]; cur; cur = cur->next) { | ||
172 | if (key->source_type == cur->key.source_type && | ||
173 | key->target_type == cur->key.target_type && | ||
174 | key->target_class == cur->key.target_class && | ||
175 | (specified & cur->datum.specified)) | ||
176 | return cur; | ||
177 | |||
178 | if (key->source_type < cur->key.source_type) | ||
179 | break; | ||
180 | if (key->source_type == cur->key.source_type && | ||
181 | key->target_type < cur->key.target_type) | ||
182 | break; | ||
183 | if (key->source_type == cur->key.source_type && | ||
184 | key->target_type == cur->key.target_type && | ||
185 | key->target_class < cur->key.target_class) | ||
186 | break; | ||
187 | } | ||
188 | return NULL; | ||
189 | } | ||
190 | |||
191 | struct avtab_node* | ||
192 | avtab_search_node_next(struct avtab_node *node, int specified) | ||
193 | { | ||
194 | struct avtab_node *cur; | ||
195 | |||
196 | if (!node) | ||
197 | return NULL; | ||
198 | |||
199 | for (cur = node->next; cur; cur = cur->next) { | ||
200 | if (node->key.source_type == cur->key.source_type && | ||
201 | node->key.target_type == cur->key.target_type && | ||
202 | node->key.target_class == cur->key.target_class && | ||
203 | (specified & cur->datum.specified)) | ||
204 | return cur; | ||
205 | |||
206 | if (node->key.source_type < cur->key.source_type) | ||
207 | break; | ||
208 | if (node->key.source_type == cur->key.source_type && | ||
209 | node->key.target_type < cur->key.target_type) | ||
210 | break; | ||
211 | if (node->key.source_type == cur->key.source_type && | ||
212 | node->key.target_type == cur->key.target_type && | ||
213 | node->key.target_class < cur->key.target_class) | ||
214 | break; | ||
215 | } | ||
216 | return NULL; | ||
217 | } | ||
218 | |||
219 | void avtab_destroy(struct avtab *h) | ||
220 | { | ||
221 | int i; | ||
222 | struct avtab_node *cur, *temp; | ||
223 | |||
224 | if (!h || !h->htable) | ||
225 | return; | ||
226 | |||
227 | for (i = 0; i < AVTAB_SIZE; i++) { | ||
228 | cur = h->htable[i]; | ||
229 | while (cur != NULL) { | ||
230 | temp = cur; | ||
231 | cur = cur->next; | ||
232 | kmem_cache_free(avtab_node_cachep, temp); | ||
233 | } | ||
234 | h->htable[i] = NULL; | ||
235 | } | ||
236 | vfree(h->htable); | ||
237 | h->htable = NULL; | ||
238 | } | ||
239 | |||
240 | |||
241 | int avtab_init(struct avtab *h) | ||
242 | { | ||
243 | int i; | ||
244 | |||
245 | h->htable = vmalloc(sizeof(*(h->htable)) * AVTAB_SIZE); | ||
246 | if (!h->htable) | ||
247 | return -ENOMEM; | ||
248 | for (i = 0; i < AVTAB_SIZE; i++) | ||
249 | h->htable[i] = NULL; | ||
250 | h->nel = 0; | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | void avtab_hash_eval(struct avtab *h, char *tag) | ||
255 | { | ||
256 | int i, chain_len, slots_used, max_chain_len; | ||
257 | struct avtab_node *cur; | ||
258 | |||
259 | slots_used = 0; | ||
260 | max_chain_len = 0; | ||
261 | for (i = 0; i < AVTAB_SIZE; i++) { | ||
262 | cur = h->htable[i]; | ||
263 | if (cur) { | ||
264 | slots_used++; | ||
265 | chain_len = 0; | ||
266 | while (cur) { | ||
267 | chain_len++; | ||
268 | cur = cur->next; | ||
269 | } | ||
270 | |||
271 | if (chain_len > max_chain_len) | ||
272 | max_chain_len = chain_len; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest " | ||
277 | "chain length %d\n", tag, h->nel, slots_used, AVTAB_SIZE, | ||
278 | max_chain_len); | ||
279 | } | ||
280 | |||
281 | int avtab_read_item(void *fp, struct avtab_datum *avdatum, struct avtab_key *avkey) | ||
282 | { | ||
283 | u32 buf[7]; | ||
284 | u32 items, items2; | ||
285 | int rc; | ||
286 | |||
287 | memset(avkey, 0, sizeof(struct avtab_key)); | ||
288 | memset(avdatum, 0, sizeof(struct avtab_datum)); | ||
289 | |||
290 | rc = next_entry(buf, fp, sizeof(u32)); | ||
291 | if (rc < 0) { | ||
292 | printk(KERN_ERR "security: avtab: truncated entry\n"); | ||
293 | goto bad; | ||
294 | } | ||
295 | items2 = le32_to_cpu(buf[0]); | ||
296 | if (items2 > ARRAY_SIZE(buf)) { | ||
297 | printk(KERN_ERR "security: avtab: entry overflow\n"); | ||
298 | goto bad; | ||
299 | } | ||
300 | rc = next_entry(buf, fp, sizeof(u32)*items2); | ||
301 | if (rc < 0) { | ||
302 | printk(KERN_ERR "security: avtab: truncated entry\n"); | ||
303 | goto bad; | ||
304 | } | ||
305 | items = 0; | ||
306 | avkey->source_type = le32_to_cpu(buf[items++]); | ||
307 | avkey->target_type = le32_to_cpu(buf[items++]); | ||
308 | avkey->target_class = le32_to_cpu(buf[items++]); | ||
309 | avdatum->specified = le32_to_cpu(buf[items++]); | ||
310 | if (!(avdatum->specified & (AVTAB_AV | AVTAB_TYPE))) { | ||
311 | printk(KERN_ERR "security: avtab: null entry\n"); | ||
312 | goto bad; | ||
313 | } | ||
314 | if ((avdatum->specified & AVTAB_AV) && | ||
315 | (avdatum->specified & AVTAB_TYPE)) { | ||
316 | printk(KERN_ERR "security: avtab: entry has both access vectors and types\n"); | ||
317 | goto bad; | ||
318 | } | ||
319 | if (avdatum->specified & AVTAB_AV) { | ||
320 | if (avdatum->specified & AVTAB_ALLOWED) | ||
321 | avtab_allowed(avdatum) = le32_to_cpu(buf[items++]); | ||
322 | if (avdatum->specified & AVTAB_AUDITDENY) | ||
323 | avtab_auditdeny(avdatum) = le32_to_cpu(buf[items++]); | ||
324 | if (avdatum->specified & AVTAB_AUDITALLOW) | ||
325 | avtab_auditallow(avdatum) = le32_to_cpu(buf[items++]); | ||
326 | } else { | ||
327 | if (avdatum->specified & AVTAB_TRANSITION) | ||
328 | avtab_transition(avdatum) = le32_to_cpu(buf[items++]); | ||
329 | if (avdatum->specified & AVTAB_CHANGE) | ||
330 | avtab_change(avdatum) = le32_to_cpu(buf[items++]); | ||
331 | if (avdatum->specified & AVTAB_MEMBER) | ||
332 | avtab_member(avdatum) = le32_to_cpu(buf[items++]); | ||
333 | } | ||
334 | if (items != items2) { | ||
335 | printk(KERN_ERR "security: avtab: entry only had %d items, expected %d\n", | ||
336 | items2, items); | ||
337 | goto bad; | ||
338 | } | ||
339 | |||
340 | return 0; | ||
341 | bad: | ||
342 | return -1; | ||
343 | } | ||
344 | |||
345 | int avtab_read(struct avtab *a, void *fp, u32 config) | ||
346 | { | ||
347 | int rc; | ||
348 | struct avtab_key avkey; | ||
349 | struct avtab_datum avdatum; | ||
350 | u32 buf[1]; | ||
351 | u32 nel, i; | ||
352 | |||
353 | |||
354 | rc = next_entry(buf, fp, sizeof(u32)); | ||
355 | if (rc < 0) { | ||
356 | printk(KERN_ERR "security: avtab: truncated table\n"); | ||
357 | goto bad; | ||
358 | } | ||
359 | nel = le32_to_cpu(buf[0]); | ||
360 | if (!nel) { | ||
361 | printk(KERN_ERR "security: avtab: table is empty\n"); | ||
362 | rc = -EINVAL; | ||
363 | goto bad; | ||
364 | } | ||
365 | for (i = 0; i < nel; i++) { | ||
366 | if (avtab_read_item(fp, &avdatum, &avkey)) { | ||
367 | rc = -EINVAL; | ||
368 | goto bad; | ||
369 | } | ||
370 | rc = avtab_insert(a, &avkey, &avdatum); | ||
371 | if (rc) { | ||
372 | if (rc == -ENOMEM) | ||
373 | printk(KERN_ERR "security: avtab: out of memory\n"); | ||
374 | if (rc == -EEXIST) | ||
375 | printk(KERN_ERR "security: avtab: duplicate entry\n"); | ||
376 | goto bad; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | rc = 0; | ||
381 | out: | ||
382 | return rc; | ||
383 | |||
384 | bad: | ||
385 | avtab_destroy(a); | ||
386 | goto out; | ||
387 | } | ||
388 | |||
389 | void avtab_cache_init(void) | ||
390 | { | ||
391 | avtab_node_cachep = kmem_cache_create("avtab_node", | ||
392 | sizeof(struct avtab_node), | ||
393 | 0, SLAB_PANIC, NULL, NULL); | ||
394 | } | ||
395 | |||
396 | void avtab_cache_destroy(void) | ||
397 | { | ||
398 | kmem_cache_destroy (avtab_node_cachep); | ||
399 | } | ||
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h new file mode 100644 index 000000000000..519d4f6dc655 --- /dev/null +++ b/security/selinux/ss/avtab.h | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * An access vector table (avtab) is a hash table | ||
3 | * of access vectors and transition types indexed | ||
4 | * by a type pair and a class. An access vector | ||
5 | * table is used to represent the type enforcement | ||
6 | * tables. | ||
7 | * | ||
8 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
9 | */ | ||
10 | |||
11 | /* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
12 | * | ||
13 | * Added conditional policy language extensions | ||
14 | * | ||
15 | * Copyright (C) 2003 Tresys Technology, LLC | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License as published by | ||
18 | * the Free Software Foundation, version 2. | ||
19 | */ | ||
20 | #ifndef _SS_AVTAB_H_ | ||
21 | #define _SS_AVTAB_H_ | ||
22 | |||
23 | struct avtab_key { | ||
24 | u32 source_type; /* source type */ | ||
25 | u32 target_type; /* target type */ | ||
26 | u32 target_class; /* target object class */ | ||
27 | }; | ||
28 | |||
29 | struct avtab_datum { | ||
30 | #define AVTAB_ALLOWED 1 | ||
31 | #define AVTAB_AUDITALLOW 2 | ||
32 | #define AVTAB_AUDITDENY 4 | ||
33 | #define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY) | ||
34 | #define AVTAB_TRANSITION 16 | ||
35 | #define AVTAB_MEMBER 32 | ||
36 | #define AVTAB_CHANGE 64 | ||
37 | #define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) | ||
38 | #define AVTAB_ENABLED 0x80000000 /* reserved for used in cond_avtab */ | ||
39 | u32 specified; /* what fields are specified */ | ||
40 | u32 data[3]; /* access vectors or types */ | ||
41 | #define avtab_allowed(x) (x)->data[0] | ||
42 | #define avtab_auditdeny(x) (x)->data[1] | ||
43 | #define avtab_auditallow(x) (x)->data[2] | ||
44 | #define avtab_transition(x) (x)->data[0] | ||
45 | #define avtab_change(x) (x)->data[1] | ||
46 | #define avtab_member(x) (x)->data[2] | ||
47 | }; | ||
48 | |||
49 | struct avtab_node { | ||
50 | struct avtab_key key; | ||
51 | struct avtab_datum datum; | ||
52 | struct avtab_node *next; | ||
53 | }; | ||
54 | |||
55 | struct avtab { | ||
56 | struct avtab_node **htable; | ||
57 | u32 nel; /* number of elements */ | ||
58 | }; | ||
59 | |||
60 | int avtab_init(struct avtab *); | ||
61 | struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k, int specified); | ||
62 | void avtab_destroy(struct avtab *h); | ||
63 | void avtab_hash_eval(struct avtab *h, char *tag); | ||
64 | |||
65 | int avtab_read_item(void *fp, struct avtab_datum *avdatum, struct avtab_key *avkey); | ||
66 | int avtab_read(struct avtab *a, void *fp, u32 config); | ||
67 | |||
68 | struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, | ||
69 | struct avtab_datum *datum); | ||
70 | |||
71 | struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key, int specified); | ||
72 | |||
73 | struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified); | ||
74 | |||
75 | void avtab_cache_init(void); | ||
76 | void avtab_cache_destroy(void); | ||
77 | |||
78 | #define AVTAB_HASH_BITS 15 | ||
79 | #define AVTAB_HASH_BUCKETS (1 << AVTAB_HASH_BITS) | ||
80 | #define AVTAB_HASH_MASK (AVTAB_HASH_BUCKETS-1) | ||
81 | |||
82 | #define AVTAB_SIZE AVTAB_HASH_BUCKETS | ||
83 | |||
84 | #endif /* _SS_AVTAB_H_ */ | ||
85 | |||
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 | } | ||
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h new file mode 100644 index 000000000000..f3a1fc6e5d66 --- /dev/null +++ b/security/selinux/ss/conditional.h | |||
@@ -0,0 +1,77 @@ | |||
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 | #ifndef _CONDITIONAL_H_ | ||
11 | #define _CONDITIONAL_H_ | ||
12 | |||
13 | #include "avtab.h" | ||
14 | #include "symtab.h" | ||
15 | #include "policydb.h" | ||
16 | |||
17 | #define COND_EXPR_MAXDEPTH 10 | ||
18 | |||
19 | /* | ||
20 | * A conditional expression is a list of operators and operands | ||
21 | * in reverse polish notation. | ||
22 | */ | ||
23 | struct cond_expr { | ||
24 | #define COND_BOOL 1 /* plain bool */ | ||
25 | #define COND_NOT 2 /* !bool */ | ||
26 | #define COND_OR 3 /* bool || bool */ | ||
27 | #define COND_AND 4 /* bool && bool */ | ||
28 | #define COND_XOR 5 /* bool ^ bool */ | ||
29 | #define COND_EQ 6 /* bool == bool */ | ||
30 | #define COND_NEQ 7 /* bool != bool */ | ||
31 | #define COND_LAST 8 | ||
32 | __u32 expr_type; | ||
33 | __u32 bool; | ||
34 | struct cond_expr *next; | ||
35 | }; | ||
36 | |||
37 | /* | ||
38 | * Each cond_node contains a list of rules to be enabled/disabled | ||
39 | * depending on the current value of the conditional expression. This | ||
40 | * struct is for that list. | ||
41 | */ | ||
42 | struct cond_av_list { | ||
43 | struct avtab_node *node; | ||
44 | struct cond_av_list *next; | ||
45 | }; | ||
46 | |||
47 | /* | ||
48 | * A cond node represents a conditional block in a policy. It | ||
49 | * contains a conditional expression, the current state of the expression, | ||
50 | * two lists of rules to enable/disable depending on the value of the | ||
51 | * expression (the true list corresponds to if and the false list corresponds | ||
52 | * to else).. | ||
53 | */ | ||
54 | struct cond_node { | ||
55 | int cur_state; | ||
56 | struct cond_expr *expr; | ||
57 | struct cond_av_list *true_list; | ||
58 | struct cond_av_list *false_list; | ||
59 | struct cond_node *next; | ||
60 | }; | ||
61 | |||
62 | int cond_policydb_init(struct policydb* p); | ||
63 | void cond_policydb_destroy(struct policydb* p); | ||
64 | |||
65 | int cond_init_bool_indexes(struct policydb* p); | ||
66 | int cond_destroy_bool(void *key, void *datum, void *p); | ||
67 | |||
68 | int cond_index_bool(void *key, void *datum, void *datap); | ||
69 | |||
70 | int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp); | ||
71 | int cond_read_list(struct policydb *p, void *fp); | ||
72 | |||
73 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd); | ||
74 | |||
75 | int evaluate_cond_node(struct policydb *p, struct cond_node *node); | ||
76 | |||
77 | #endif /* _CONDITIONAL_H_ */ | ||
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h new file mode 100644 index 000000000000..149dda731fd3 --- /dev/null +++ b/security/selinux/ss/constraint.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * A constraint is a condition that must be satisfied in | ||
3 | * order for one or more permissions to be granted. | ||
4 | * Constraints are used to impose additional restrictions | ||
5 | * beyond the type-based rules in `te' or the role-based | ||
6 | * transition rules in `rbac'. Constraints are typically | ||
7 | * used to prevent a process from transitioning to a new user | ||
8 | * identity or role unless it is in a privileged type. | ||
9 | * Constraints are likewise typically used to prevent a | ||
10 | * process from labeling an object with a different user | ||
11 | * identity. | ||
12 | * | ||
13 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
14 | */ | ||
15 | #ifndef _SS_CONSTRAINT_H_ | ||
16 | #define _SS_CONSTRAINT_H_ | ||
17 | |||
18 | #include "ebitmap.h" | ||
19 | |||
20 | #define CEXPR_MAXDEPTH 5 | ||
21 | |||
22 | struct constraint_expr { | ||
23 | #define CEXPR_NOT 1 /* not expr */ | ||
24 | #define CEXPR_AND 2 /* expr and expr */ | ||
25 | #define CEXPR_OR 3 /* expr or expr */ | ||
26 | #define CEXPR_ATTR 4 /* attr op attr */ | ||
27 | #define CEXPR_NAMES 5 /* attr op names */ | ||
28 | u32 expr_type; /* expression type */ | ||
29 | |||
30 | #define CEXPR_USER 1 /* user */ | ||
31 | #define CEXPR_ROLE 2 /* role */ | ||
32 | #define CEXPR_TYPE 4 /* type */ | ||
33 | #define CEXPR_TARGET 8 /* target if set, source otherwise */ | ||
34 | #define CEXPR_XTARGET 16 /* special 3rd target for validatetrans rule */ | ||
35 | #define CEXPR_L1L2 32 /* low level 1 vs. low level 2 */ | ||
36 | #define CEXPR_L1H2 64 /* low level 1 vs. high level 2 */ | ||
37 | #define CEXPR_H1L2 128 /* high level 1 vs. low level 2 */ | ||
38 | #define CEXPR_H1H2 256 /* high level 1 vs. high level 2 */ | ||
39 | #define CEXPR_L1H1 512 /* low level 1 vs. high level 1 */ | ||
40 | #define CEXPR_L2H2 1024 /* low level 2 vs. high level 2 */ | ||
41 | u32 attr; /* attribute */ | ||
42 | |||
43 | #define CEXPR_EQ 1 /* == or eq */ | ||
44 | #define CEXPR_NEQ 2 /* != */ | ||
45 | #define CEXPR_DOM 3 /* dom */ | ||
46 | #define CEXPR_DOMBY 4 /* domby */ | ||
47 | #define CEXPR_INCOMP 5 /* incomp */ | ||
48 | u32 op; /* operator */ | ||
49 | |||
50 | struct ebitmap names; /* names */ | ||
51 | |||
52 | struct constraint_expr *next; /* next expression */ | ||
53 | }; | ||
54 | |||
55 | struct constraint_node { | ||
56 | u32 permissions; /* constrained permissions */ | ||
57 | struct constraint_expr *expr; /* constraint on permissions */ | ||
58 | struct constraint_node *next; /* next constraint */ | ||
59 | }; | ||
60 | |||
61 | #endif /* _SS_CONSTRAINT_H_ */ | ||
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h new file mode 100644 index 000000000000..0562bacb7b99 --- /dev/null +++ b/security/selinux/ss/context.h | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * A security context is a set of security attributes | ||
3 | * associated with each subject and object controlled | ||
4 | * by the security policy. Security contexts are | ||
5 | * externally represented as variable-length strings | ||
6 | * that can be interpreted by a user or application | ||
7 | * with an understanding of the security policy. | ||
8 | * Internally, the security server uses a simple | ||
9 | * structure. This structure is private to the | ||
10 | * security server and can be changed without affecting | ||
11 | * clients of the security server. | ||
12 | * | ||
13 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
14 | */ | ||
15 | #ifndef _SS_CONTEXT_H_ | ||
16 | #define _SS_CONTEXT_H_ | ||
17 | |||
18 | #include "ebitmap.h" | ||
19 | #include "mls_types.h" | ||
20 | #include "security.h" | ||
21 | |||
22 | /* | ||
23 | * A security context consists of an authenticated user | ||
24 | * identity, a role, a type and a MLS range. | ||
25 | */ | ||
26 | struct context { | ||
27 | u32 user; | ||
28 | u32 role; | ||
29 | u32 type; | ||
30 | struct mls_range range; | ||
31 | }; | ||
32 | |||
33 | static inline void mls_context_init(struct context *c) | ||
34 | { | ||
35 | memset(&c->range, 0, sizeof(c->range)); | ||
36 | } | ||
37 | |||
38 | static inline int mls_context_cpy(struct context *dst, struct context *src) | ||
39 | { | ||
40 | int rc; | ||
41 | |||
42 | if (!selinux_mls_enabled) | ||
43 | return 0; | ||
44 | |||
45 | dst->range.level[0].sens = src->range.level[0].sens; | ||
46 | rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat); | ||
47 | if (rc) | ||
48 | goto out; | ||
49 | |||
50 | dst->range.level[1].sens = src->range.level[1].sens; | ||
51 | rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat); | ||
52 | if (rc) | ||
53 | ebitmap_destroy(&dst->range.level[0].cat); | ||
54 | out: | ||
55 | return rc; | ||
56 | } | ||
57 | |||
58 | static inline int mls_context_cmp(struct context *c1, struct context *c2) | ||
59 | { | ||
60 | if (!selinux_mls_enabled) | ||
61 | return 1; | ||
62 | |||
63 | return ((c1->range.level[0].sens == c2->range.level[0].sens) && | ||
64 | ebitmap_cmp(&c1->range.level[0].cat,&c2->range.level[0].cat) && | ||
65 | (c1->range.level[1].sens == c2->range.level[1].sens) && | ||
66 | ebitmap_cmp(&c1->range.level[1].cat,&c2->range.level[1].cat)); | ||
67 | } | ||
68 | |||
69 | static inline void mls_context_destroy(struct context *c) | ||
70 | { | ||
71 | if (!selinux_mls_enabled) | ||
72 | return; | ||
73 | |||
74 | ebitmap_destroy(&c->range.level[0].cat); | ||
75 | ebitmap_destroy(&c->range.level[1].cat); | ||
76 | mls_context_init(c); | ||
77 | } | ||
78 | |||
79 | static inline void context_init(struct context *c) | ||
80 | { | ||
81 | memset(c, 0, sizeof(*c)); | ||
82 | } | ||
83 | |||
84 | static inline int context_cpy(struct context *dst, struct context *src) | ||
85 | { | ||
86 | dst->user = src->user; | ||
87 | dst->role = src->role; | ||
88 | dst->type = src->type; | ||
89 | return mls_context_cpy(dst, src); | ||
90 | } | ||
91 | |||
92 | static inline void context_destroy(struct context *c) | ||
93 | { | ||
94 | c->user = c->role = c->type = 0; | ||
95 | mls_context_destroy(c); | ||
96 | } | ||
97 | |||
98 | static inline int context_cmp(struct context *c1, struct context *c2) | ||
99 | { | ||
100 | return ((c1->user == c2->user) && | ||
101 | (c1->role == c2->role) && | ||
102 | (c1->type == c2->type) && | ||
103 | mls_context_cmp(c1, c2)); | ||
104 | } | ||
105 | |||
106 | #endif /* _SS_CONTEXT_H_ */ | ||
107 | |||
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c new file mode 100644 index 000000000000..d8ce9cc0b9f1 --- /dev/null +++ b/security/selinux/ss/ebitmap.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /* | ||
2 | * Implementation of the extensible bitmap type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <linux/errno.h> | ||
9 | #include "ebitmap.h" | ||
10 | #include "policydb.h" | ||
11 | |||
12 | int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) | ||
13 | { | ||
14 | struct ebitmap_node *n1, *n2; | ||
15 | |||
16 | if (e1->highbit != e2->highbit) | ||
17 | return 0; | ||
18 | |||
19 | n1 = e1->node; | ||
20 | n2 = e2->node; | ||
21 | while (n1 && n2 && | ||
22 | (n1->startbit == n2->startbit) && | ||
23 | (n1->map == n2->map)) { | ||
24 | n1 = n1->next; | ||
25 | n2 = n2->next; | ||
26 | } | ||
27 | |||
28 | if (n1 || n2) | ||
29 | return 0; | ||
30 | |||
31 | return 1; | ||
32 | } | ||
33 | |||
34 | int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) | ||
35 | { | ||
36 | struct ebitmap_node *n, *new, *prev; | ||
37 | |||
38 | ebitmap_init(dst); | ||
39 | n = src->node; | ||
40 | prev = NULL; | ||
41 | while (n) { | ||
42 | new = kmalloc(sizeof(*new), GFP_ATOMIC); | ||
43 | if (!new) { | ||
44 | ebitmap_destroy(dst); | ||
45 | return -ENOMEM; | ||
46 | } | ||
47 | memset(new, 0, sizeof(*new)); | ||
48 | new->startbit = n->startbit; | ||
49 | new->map = n->map; | ||
50 | new->next = NULL; | ||
51 | if (prev) | ||
52 | prev->next = new; | ||
53 | else | ||
54 | dst->node = new; | ||
55 | prev = new; | ||
56 | n = n->next; | ||
57 | } | ||
58 | |||
59 | dst->highbit = src->highbit; | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) | ||
64 | { | ||
65 | struct ebitmap_node *n1, *n2; | ||
66 | |||
67 | if (e1->highbit < e2->highbit) | ||
68 | return 0; | ||
69 | |||
70 | n1 = e1->node; | ||
71 | n2 = e2->node; | ||
72 | while (n1 && n2 && (n1->startbit <= n2->startbit)) { | ||
73 | if (n1->startbit < n2->startbit) { | ||
74 | n1 = n1->next; | ||
75 | continue; | ||
76 | } | ||
77 | if ((n1->map & n2->map) != n2->map) | ||
78 | return 0; | ||
79 | |||
80 | n1 = n1->next; | ||
81 | n2 = n2->next; | ||
82 | } | ||
83 | |||
84 | if (n2) | ||
85 | return 0; | ||
86 | |||
87 | return 1; | ||
88 | } | ||
89 | |||
90 | int ebitmap_get_bit(struct ebitmap *e, unsigned long bit) | ||
91 | { | ||
92 | struct ebitmap_node *n; | ||
93 | |||
94 | if (e->highbit < bit) | ||
95 | return 0; | ||
96 | |||
97 | n = e->node; | ||
98 | while (n && (n->startbit <= bit)) { | ||
99 | if ((n->startbit + MAPSIZE) > bit) { | ||
100 | if (n->map & (MAPBIT << (bit - n->startbit))) | ||
101 | return 1; | ||
102 | else | ||
103 | return 0; | ||
104 | } | ||
105 | n = n->next; | ||
106 | } | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) | ||
112 | { | ||
113 | struct ebitmap_node *n, *prev, *new; | ||
114 | |||
115 | prev = NULL; | ||
116 | n = e->node; | ||
117 | while (n && n->startbit <= bit) { | ||
118 | if ((n->startbit + MAPSIZE) > bit) { | ||
119 | if (value) { | ||
120 | n->map |= (MAPBIT << (bit - n->startbit)); | ||
121 | } else { | ||
122 | n->map &= ~(MAPBIT << (bit - n->startbit)); | ||
123 | if (!n->map) { | ||
124 | /* drop this node from the bitmap */ | ||
125 | |||
126 | if (!n->next) { | ||
127 | /* | ||
128 | * this was the highest map | ||
129 | * within the bitmap | ||
130 | */ | ||
131 | if (prev) | ||
132 | e->highbit = prev->startbit + MAPSIZE; | ||
133 | else | ||
134 | e->highbit = 0; | ||
135 | } | ||
136 | if (prev) | ||
137 | prev->next = n->next; | ||
138 | else | ||
139 | e->node = n->next; | ||
140 | |||
141 | kfree(n); | ||
142 | } | ||
143 | } | ||
144 | return 0; | ||
145 | } | ||
146 | prev = n; | ||
147 | n = n->next; | ||
148 | } | ||
149 | |||
150 | if (!value) | ||
151 | return 0; | ||
152 | |||
153 | new = kmalloc(sizeof(*new), GFP_ATOMIC); | ||
154 | if (!new) | ||
155 | return -ENOMEM; | ||
156 | memset(new, 0, sizeof(*new)); | ||
157 | |||
158 | new->startbit = bit & ~(MAPSIZE - 1); | ||
159 | new->map = (MAPBIT << (bit - new->startbit)); | ||
160 | |||
161 | if (!n) | ||
162 | /* this node will be the highest map within the bitmap */ | ||
163 | e->highbit = new->startbit + MAPSIZE; | ||
164 | |||
165 | if (prev) { | ||
166 | new->next = prev->next; | ||
167 | prev->next = new; | ||
168 | } else { | ||
169 | new->next = e->node; | ||
170 | e->node = new; | ||
171 | } | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | void ebitmap_destroy(struct ebitmap *e) | ||
177 | { | ||
178 | struct ebitmap_node *n, *temp; | ||
179 | |||
180 | if (!e) | ||
181 | return; | ||
182 | |||
183 | n = e->node; | ||
184 | while (n) { | ||
185 | temp = n; | ||
186 | n = n->next; | ||
187 | kfree(temp); | ||
188 | } | ||
189 | |||
190 | e->highbit = 0; | ||
191 | e->node = NULL; | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | int ebitmap_read(struct ebitmap *e, void *fp) | ||
196 | { | ||
197 | int rc; | ||
198 | struct ebitmap_node *n, *l; | ||
199 | u32 buf[3], mapsize, count, i; | ||
200 | u64 map; | ||
201 | |||
202 | ebitmap_init(e); | ||
203 | |||
204 | rc = next_entry(buf, fp, sizeof buf); | ||
205 | if (rc < 0) | ||
206 | goto out; | ||
207 | |||
208 | mapsize = le32_to_cpu(buf[0]); | ||
209 | e->highbit = le32_to_cpu(buf[1]); | ||
210 | count = le32_to_cpu(buf[2]); | ||
211 | |||
212 | if (mapsize != MAPSIZE) { | ||
213 | printk(KERN_ERR "security: ebitmap: map size %u does not " | ||
214 | "match my size %Zd (high bit was %d)\n", mapsize, | ||
215 | MAPSIZE, e->highbit); | ||
216 | goto bad; | ||
217 | } | ||
218 | if (!e->highbit) { | ||
219 | e->node = NULL; | ||
220 | goto ok; | ||
221 | } | ||
222 | if (e->highbit & (MAPSIZE - 1)) { | ||
223 | printk(KERN_ERR "security: ebitmap: high bit (%d) is not a " | ||
224 | "multiple of the map size (%Zd)\n", e->highbit, MAPSIZE); | ||
225 | goto bad; | ||
226 | } | ||
227 | l = NULL; | ||
228 | for (i = 0; i < count; i++) { | ||
229 | rc = next_entry(buf, fp, sizeof(u32)); | ||
230 | if (rc < 0) { | ||
231 | printk(KERN_ERR "security: ebitmap: truncated map\n"); | ||
232 | goto bad; | ||
233 | } | ||
234 | n = kmalloc(sizeof(*n), GFP_KERNEL); | ||
235 | if (!n) { | ||
236 | printk(KERN_ERR "security: ebitmap: out of memory\n"); | ||
237 | rc = -ENOMEM; | ||
238 | goto bad; | ||
239 | } | ||
240 | memset(n, 0, sizeof(*n)); | ||
241 | |||
242 | n->startbit = le32_to_cpu(buf[0]); | ||
243 | |||
244 | if (n->startbit & (MAPSIZE - 1)) { | ||
245 | printk(KERN_ERR "security: ebitmap start bit (%d) is " | ||
246 | "not a multiple of the map size (%Zd)\n", | ||
247 | n->startbit, MAPSIZE); | ||
248 | goto bad_free; | ||
249 | } | ||
250 | if (n->startbit > (e->highbit - MAPSIZE)) { | ||
251 | printk(KERN_ERR "security: ebitmap start bit (%d) is " | ||
252 | "beyond the end of the bitmap (%Zd)\n", | ||
253 | n->startbit, (e->highbit - MAPSIZE)); | ||
254 | goto bad_free; | ||
255 | } | ||
256 | rc = next_entry(&map, fp, sizeof(u64)); | ||
257 | if (rc < 0) { | ||
258 | printk(KERN_ERR "security: ebitmap: truncated map\n"); | ||
259 | goto bad_free; | ||
260 | } | ||
261 | n->map = le64_to_cpu(map); | ||
262 | |||
263 | if (!n->map) { | ||
264 | printk(KERN_ERR "security: ebitmap: null map in " | ||
265 | "ebitmap (startbit %d)\n", n->startbit); | ||
266 | goto bad_free; | ||
267 | } | ||
268 | if (l) { | ||
269 | if (n->startbit <= l->startbit) { | ||
270 | printk(KERN_ERR "security: ebitmap: start " | ||
271 | "bit %d comes after start bit %d\n", | ||
272 | n->startbit, l->startbit); | ||
273 | goto bad_free; | ||
274 | } | ||
275 | l->next = n; | ||
276 | } else | ||
277 | e->node = n; | ||
278 | |||
279 | l = n; | ||
280 | } | ||
281 | |||
282 | ok: | ||
283 | rc = 0; | ||
284 | out: | ||
285 | return rc; | ||
286 | bad_free: | ||
287 | kfree(n); | ||
288 | bad: | ||
289 | if (!rc) | ||
290 | rc = -EINVAL; | ||
291 | ebitmap_destroy(e); | ||
292 | goto out; | ||
293 | } | ||
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h new file mode 100644 index 000000000000..471370233fd9 --- /dev/null +++ b/security/selinux/ss/ebitmap.h | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * An extensible bitmap is a bitmap that supports an | ||
3 | * arbitrary number of bits. Extensible bitmaps are | ||
4 | * used to represent sets of values, such as types, | ||
5 | * roles, categories, and classes. | ||
6 | * | ||
7 | * Each extensible bitmap is implemented as a linked | ||
8 | * list of bitmap nodes, where each bitmap node has | ||
9 | * an explicitly specified starting bit position within | ||
10 | * the total bitmap. | ||
11 | * | ||
12 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
13 | */ | ||
14 | #ifndef _SS_EBITMAP_H_ | ||
15 | #define _SS_EBITMAP_H_ | ||
16 | |||
17 | #define MAPTYPE u64 /* portion of bitmap in each node */ | ||
18 | #define MAPSIZE (sizeof(MAPTYPE) * 8) /* number of bits in node bitmap */ | ||
19 | #define MAPBIT 1ULL /* a bit in the node bitmap */ | ||
20 | |||
21 | struct ebitmap_node { | ||
22 | u32 startbit; /* starting position in the total bitmap */ | ||
23 | MAPTYPE map; /* this node's portion of the bitmap */ | ||
24 | struct ebitmap_node *next; | ||
25 | }; | ||
26 | |||
27 | struct ebitmap { | ||
28 | struct ebitmap_node *node; /* first node in the bitmap */ | ||
29 | u32 highbit; /* highest position in the total bitmap */ | ||
30 | }; | ||
31 | |||
32 | #define ebitmap_length(e) ((e)->highbit) | ||
33 | #define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0) | ||
34 | |||
35 | static inline void ebitmap_init(struct ebitmap *e) | ||
36 | { | ||
37 | memset(e, 0, sizeof(*e)); | ||
38 | } | ||
39 | |||
40 | int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2); | ||
41 | int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); | ||
42 | int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2); | ||
43 | int ebitmap_get_bit(struct ebitmap *e, unsigned long bit); | ||
44 | int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value); | ||
45 | void ebitmap_destroy(struct ebitmap *e); | ||
46 | int ebitmap_read(struct ebitmap *e, void *fp); | ||
47 | |||
48 | #endif /* _SS_EBITMAP_H_ */ | ||
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c new file mode 100644 index 000000000000..26661fcc00ce --- /dev/null +++ b/security/selinux/ss/hashtab.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * Implementation of the hash table type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <linux/errno.h> | ||
9 | #include "hashtab.h" | ||
10 | |||
11 | struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), | ||
12 | int (*keycmp)(struct hashtab *h, void *key1, void *key2), | ||
13 | u32 size) | ||
14 | { | ||
15 | struct hashtab *p; | ||
16 | u32 i; | ||
17 | |||
18 | p = kmalloc(sizeof(*p), GFP_KERNEL); | ||
19 | if (p == NULL) | ||
20 | return p; | ||
21 | |||
22 | memset(p, 0, sizeof(*p)); | ||
23 | p->size = size; | ||
24 | p->nel = 0; | ||
25 | p->hash_value = hash_value; | ||
26 | p->keycmp = keycmp; | ||
27 | p->htable = kmalloc(sizeof(*(p->htable)) * size, GFP_KERNEL); | ||
28 | if (p->htable == NULL) { | ||
29 | kfree(p); | ||
30 | return NULL; | ||
31 | } | ||
32 | |||
33 | for (i = 0; i < size; i++) | ||
34 | p->htable[i] = NULL; | ||
35 | |||
36 | return p; | ||
37 | } | ||
38 | |||
39 | int hashtab_insert(struct hashtab *h, void *key, void *datum) | ||
40 | { | ||
41 | u32 hvalue; | ||
42 | struct hashtab_node *prev, *cur, *newnode; | ||
43 | |||
44 | if (!h || h->nel == HASHTAB_MAX_NODES) | ||
45 | return -EINVAL; | ||
46 | |||
47 | hvalue = h->hash_value(h, key); | ||
48 | prev = NULL; | ||
49 | cur = h->htable[hvalue]; | ||
50 | while (cur && h->keycmp(h, key, cur->key) > 0) { | ||
51 | prev = cur; | ||
52 | cur = cur->next; | ||
53 | } | ||
54 | |||
55 | if (cur && (h->keycmp(h, key, cur->key) == 0)) | ||
56 | return -EEXIST; | ||
57 | |||
58 | newnode = kmalloc(sizeof(*newnode), GFP_KERNEL); | ||
59 | if (newnode == NULL) | ||
60 | return -ENOMEM; | ||
61 | memset(newnode, 0, sizeof(*newnode)); | ||
62 | newnode->key = key; | ||
63 | newnode->datum = datum; | ||
64 | if (prev) { | ||
65 | newnode->next = prev->next; | ||
66 | prev->next = newnode; | ||
67 | } else { | ||
68 | newnode->next = h->htable[hvalue]; | ||
69 | h->htable[hvalue] = newnode; | ||
70 | } | ||
71 | |||
72 | h->nel++; | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | void *hashtab_search(struct hashtab *h, void *key) | ||
77 | { | ||
78 | u32 hvalue; | ||
79 | struct hashtab_node *cur; | ||
80 | |||
81 | if (!h) | ||
82 | return NULL; | ||
83 | |||
84 | hvalue = h->hash_value(h, key); | ||
85 | cur = h->htable[hvalue]; | ||
86 | while (cur != NULL && h->keycmp(h, key, cur->key) > 0) | ||
87 | cur = cur->next; | ||
88 | |||
89 | if (cur == NULL || (h->keycmp(h, key, cur->key) != 0)) | ||
90 | return NULL; | ||
91 | |||
92 | return cur->datum; | ||
93 | } | ||
94 | |||
95 | void hashtab_destroy(struct hashtab *h) | ||
96 | { | ||
97 | u32 i; | ||
98 | struct hashtab_node *cur, *temp; | ||
99 | |||
100 | if (!h) | ||
101 | return; | ||
102 | |||
103 | for (i = 0; i < h->size; i++) { | ||
104 | cur = h->htable[i]; | ||
105 | while (cur != NULL) { | ||
106 | temp = cur; | ||
107 | cur = cur->next; | ||
108 | kfree(temp); | ||
109 | } | ||
110 | h->htable[i] = NULL; | ||
111 | } | ||
112 | |||
113 | kfree(h->htable); | ||
114 | h->htable = NULL; | ||
115 | |||
116 | kfree(h); | ||
117 | } | ||
118 | |||
119 | int hashtab_map(struct hashtab *h, | ||
120 | int (*apply)(void *k, void *d, void *args), | ||
121 | void *args) | ||
122 | { | ||
123 | u32 i; | ||
124 | int ret; | ||
125 | struct hashtab_node *cur; | ||
126 | |||
127 | if (!h) | ||
128 | return 0; | ||
129 | |||
130 | for (i = 0; i < h->size; i++) { | ||
131 | cur = h->htable[i]; | ||
132 | while (cur != NULL) { | ||
133 | ret = apply(cur->key, cur->datum, args); | ||
134 | if (ret) | ||
135 | return ret; | ||
136 | cur = cur->next; | ||
137 | } | ||
138 | } | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | |||
143 | void hashtab_stat(struct hashtab *h, struct hashtab_info *info) | ||
144 | { | ||
145 | u32 i, chain_len, slots_used, max_chain_len; | ||
146 | struct hashtab_node *cur; | ||
147 | |||
148 | slots_used = 0; | ||
149 | max_chain_len = 0; | ||
150 | for (slots_used = max_chain_len = i = 0; i < h->size; i++) { | ||
151 | cur = h->htable[i]; | ||
152 | if (cur) { | ||
153 | slots_used++; | ||
154 | chain_len = 0; | ||
155 | while (cur) { | ||
156 | chain_len++; | ||
157 | cur = cur->next; | ||
158 | } | ||
159 | |||
160 | if (chain_len > max_chain_len) | ||
161 | max_chain_len = chain_len; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | info->slots_used = slots_used; | ||
166 | info->max_chain_len = max_chain_len; | ||
167 | } | ||
diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h new file mode 100644 index 000000000000..4cc85816a718 --- /dev/null +++ b/security/selinux/ss/hashtab.h | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * A hash table (hashtab) maintains associations between | ||
3 | * key values and datum values. The type of the key values | ||
4 | * and the type of the datum values is arbitrary. The | ||
5 | * functions for hash computation and key comparison are | ||
6 | * provided by the creator of the table. | ||
7 | * | ||
8 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
9 | */ | ||
10 | #ifndef _SS_HASHTAB_H_ | ||
11 | #define _SS_HASHTAB_H_ | ||
12 | |||
13 | #define HASHTAB_MAX_NODES 0xffffffff | ||
14 | |||
15 | struct hashtab_node { | ||
16 | void *key; | ||
17 | void *datum; | ||
18 | struct hashtab_node *next; | ||
19 | }; | ||
20 | |||
21 | struct hashtab { | ||
22 | struct hashtab_node **htable; /* hash table */ | ||
23 | u32 size; /* number of slots in hash table */ | ||
24 | u32 nel; /* number of elements in hash table */ | ||
25 | u32 (*hash_value)(struct hashtab *h, void *key); | ||
26 | /* hash function */ | ||
27 | int (*keycmp)(struct hashtab *h, void *key1, void *key2); | ||
28 | /* key comparison function */ | ||
29 | }; | ||
30 | |||
31 | struct hashtab_info { | ||
32 | u32 slots_used; | ||
33 | u32 max_chain_len; | ||
34 | }; | ||
35 | |||
36 | /* | ||
37 | * Creates a new hash table with the specified characteristics. | ||
38 | * | ||
39 | * Returns NULL if insufficent space is available or | ||
40 | * the new hash table otherwise. | ||
41 | */ | ||
42 | struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), | ||
43 | int (*keycmp)(struct hashtab *h, void *key1, void *key2), | ||
44 | u32 size); | ||
45 | |||
46 | /* | ||
47 | * Inserts the specified (key, datum) pair into the specified hash table. | ||
48 | * | ||
49 | * Returns -ENOMEM on memory allocation error, | ||
50 | * -EEXIST if there is already an entry with the same key, | ||
51 | * -EINVAL for general errors or | ||
52 | * 0 otherwise. | ||
53 | */ | ||
54 | int hashtab_insert(struct hashtab *h, void *k, void *d); | ||
55 | |||
56 | /* | ||
57 | * Searches for the entry with the specified key in the hash table. | ||
58 | * | ||
59 | * Returns NULL if no entry has the specified key or | ||
60 | * the datum of the entry otherwise. | ||
61 | */ | ||
62 | void *hashtab_search(struct hashtab *h, void *k); | ||
63 | |||
64 | /* | ||
65 | * Destroys the specified hash table. | ||
66 | */ | ||
67 | void hashtab_destroy(struct hashtab *h); | ||
68 | |||
69 | /* | ||
70 | * Applies the specified apply function to (key,datum,args) | ||
71 | * for each entry in the specified hash table. | ||
72 | * | ||
73 | * The order in which the function is applied to the entries | ||
74 | * is dependent upon the internal structure of the hash table. | ||
75 | * | ||
76 | * If apply returns a non-zero status, then hashtab_map will cease | ||
77 | * iterating through the hash table and will propagate the error | ||
78 | * return to its caller. | ||
79 | */ | ||
80 | int hashtab_map(struct hashtab *h, | ||
81 | int (*apply)(void *k, void *d, void *args), | ||
82 | void *args); | ||
83 | |||
84 | /* Fill info with some hash table statistics */ | ||
85 | void hashtab_stat(struct hashtab *h, struct hashtab_info *info); | ||
86 | |||
87 | #endif /* _SS_HASHTAB_H */ | ||
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c new file mode 100644 index 000000000000..756036bcc243 --- /dev/null +++ b/security/selinux/ss/mls.c | |||
@@ -0,0 +1,527 @@ | |||
1 | /* | ||
2 | * Implementation of the multi-level security (MLS) policy. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | /* | ||
7 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
8 | * | ||
9 | * Support for enhanced MLS infrastructure. | ||
10 | * | ||
11 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include "mls.h" | ||
19 | #include "policydb.h" | ||
20 | #include "services.h" | ||
21 | |||
22 | /* | ||
23 | * Return the length in bytes for the MLS fields of the | ||
24 | * security context string representation of `context'. | ||
25 | */ | ||
26 | int mls_compute_context_len(struct context * context) | ||
27 | { | ||
28 | int i, l, len, range; | ||
29 | |||
30 | if (!selinux_mls_enabled) | ||
31 | return 0; | ||
32 | |||
33 | len = 1; /* for the beginning ":" */ | ||
34 | for (l = 0; l < 2; l++) { | ||
35 | range = 0; | ||
36 | len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); | ||
37 | |||
38 | for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) { | ||
39 | if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) { | ||
40 | if (range) { | ||
41 | range++; | ||
42 | continue; | ||
43 | } | ||
44 | |||
45 | len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; | ||
46 | range++; | ||
47 | } else { | ||
48 | if (range > 1) | ||
49 | len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1; | ||
50 | range = 0; | ||
51 | } | ||
52 | } | ||
53 | /* Handle case where last category is the end of range */ | ||
54 | if (range > 1) | ||
55 | len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1; | ||
56 | |||
57 | if (l == 0) { | ||
58 | if (mls_level_eq(&context->range.level[0], | ||
59 | &context->range.level[1])) | ||
60 | break; | ||
61 | else | ||
62 | len++; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | return len; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Write the security context string representation of | ||
71 | * the MLS fields of `context' into the string `*scontext'. | ||
72 | * Update `*scontext' to point to the end of the MLS fields. | ||
73 | */ | ||
74 | void mls_sid_to_context(struct context *context, | ||
75 | char **scontext) | ||
76 | { | ||
77 | char *scontextp; | ||
78 | int i, l, range, wrote_sep; | ||
79 | |||
80 | if (!selinux_mls_enabled) | ||
81 | return; | ||
82 | |||
83 | scontextp = *scontext; | ||
84 | |||
85 | *scontextp = ':'; | ||
86 | scontextp++; | ||
87 | |||
88 | for (l = 0; l < 2; l++) { | ||
89 | range = 0; | ||
90 | wrote_sep = 0; | ||
91 | strcpy(scontextp, | ||
92 | policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); | ||
93 | scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); | ||
94 | |||
95 | /* categories */ | ||
96 | for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) { | ||
97 | if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) { | ||
98 | if (range) { | ||
99 | range++; | ||
100 | continue; | ||
101 | } | ||
102 | |||
103 | if (!wrote_sep) { | ||
104 | *scontextp++ = ':'; | ||
105 | wrote_sep = 1; | ||
106 | } else | ||
107 | *scontextp++ = ','; | ||
108 | strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); | ||
109 | scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); | ||
110 | range++; | ||
111 | } else { | ||
112 | if (range > 1) { | ||
113 | if (range > 2) | ||
114 | *scontextp++ = '.'; | ||
115 | else | ||
116 | *scontextp++ = ','; | ||
117 | |||
118 | strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]); | ||
119 | scontextp += strlen(policydb.p_cat_val_to_name[i - 2]); | ||
120 | } | ||
121 | range = 0; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | /* Handle case where last category is the end of range */ | ||
126 | if (range > 1) { | ||
127 | if (range > 2) | ||
128 | *scontextp++ = '.'; | ||
129 | else | ||
130 | *scontextp++ = ','; | ||
131 | |||
132 | strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]); | ||
133 | scontextp += strlen(policydb.p_cat_val_to_name[i - 2]); | ||
134 | } | ||
135 | |||
136 | if (l == 0) { | ||
137 | if (mls_level_eq(&context->range.level[0], | ||
138 | &context->range.level[1])) | ||
139 | break; | ||
140 | else { | ||
141 | *scontextp = '-'; | ||
142 | scontextp++; | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | |||
147 | *scontext = scontextp; | ||
148 | return; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Return 1 if the MLS fields in the security context | ||
153 | * structure `c' are valid. Return 0 otherwise. | ||
154 | */ | ||
155 | int mls_context_isvalid(struct policydb *p, struct context *c) | ||
156 | { | ||
157 | struct level_datum *levdatum; | ||
158 | struct user_datum *usrdatum; | ||
159 | int i, l; | ||
160 | |||
161 | if (!selinux_mls_enabled) | ||
162 | return 1; | ||
163 | |||
164 | /* | ||
165 | * MLS range validity checks: high must dominate low, low level must | ||
166 | * be valid (category set <-> sensitivity check), and high level must | ||
167 | * be valid (category set <-> sensitivity check) | ||
168 | */ | ||
169 | if (!mls_level_dom(&c->range.level[1], &c->range.level[0])) | ||
170 | /* High does not dominate low. */ | ||
171 | return 0; | ||
172 | |||
173 | for (l = 0; l < 2; l++) { | ||
174 | if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim) | ||
175 | return 0; | ||
176 | levdatum = hashtab_search(p->p_levels.table, | ||
177 | p->p_sens_val_to_name[c->range.level[l].sens - 1]); | ||
178 | if (!levdatum) | ||
179 | return 0; | ||
180 | |||
181 | for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) { | ||
182 | if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) { | ||
183 | if (i > p->p_cats.nprim) | ||
184 | return 0; | ||
185 | if (!ebitmap_get_bit(&levdatum->level->cat, i - 1)) | ||
186 | /* | ||
187 | * Category may not be associated with | ||
188 | * sensitivity in low level. | ||
189 | */ | ||
190 | return 0; | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | |||
195 | if (c->role == OBJECT_R_VAL) | ||
196 | return 1; | ||
197 | |||
198 | /* | ||
199 | * User must be authorized for the MLS range. | ||
200 | */ | ||
201 | if (!c->user || c->user > p->p_users.nprim) | ||
202 | return 0; | ||
203 | usrdatum = p->user_val_to_struct[c->user - 1]; | ||
204 | if (!mls_range_contains(usrdatum->range, c->range)) | ||
205 | return 0; /* user may not be associated with range */ | ||
206 | |||
207 | return 1; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Set the MLS fields in the security context structure | ||
212 | * `context' based on the string representation in | ||
213 | * the string `*scontext'. Update `*scontext' to | ||
214 | * point to the end of the string representation of | ||
215 | * the MLS fields. | ||
216 | * | ||
217 | * This function modifies the string in place, inserting | ||
218 | * NULL characters to terminate the MLS fields. | ||
219 | */ | ||
220 | int mls_context_to_sid(char oldc, | ||
221 | char **scontext, | ||
222 | struct context *context) | ||
223 | { | ||
224 | |||
225 | char delim; | ||
226 | char *scontextp, *p, *rngptr; | ||
227 | struct level_datum *levdatum; | ||
228 | struct cat_datum *catdatum, *rngdatum; | ||
229 | int l, rc = -EINVAL; | ||
230 | |||
231 | if (!selinux_mls_enabled) | ||
232 | return 0; | ||
233 | |||
234 | /* No MLS component to the security context. */ | ||
235 | if (!oldc) | ||
236 | goto out; | ||
237 | |||
238 | /* Extract low sensitivity. */ | ||
239 | scontextp = p = *scontext; | ||
240 | while (*p && *p != ':' && *p != '-') | ||
241 | p++; | ||
242 | |||
243 | delim = *p; | ||
244 | if (delim != 0) | ||
245 | *p++ = 0; | ||
246 | |||
247 | for (l = 0; l < 2; l++) { | ||
248 | levdatum = hashtab_search(policydb.p_levels.table, scontextp); | ||
249 | if (!levdatum) { | ||
250 | rc = -EINVAL; | ||
251 | goto out; | ||
252 | } | ||
253 | |||
254 | context->range.level[l].sens = levdatum->level->sens; | ||
255 | |||
256 | if (delim == ':') { | ||
257 | /* Extract category set. */ | ||
258 | while (1) { | ||
259 | scontextp = p; | ||
260 | while (*p && *p != ',' && *p != '-') | ||
261 | p++; | ||
262 | delim = *p; | ||
263 | if (delim != 0) | ||
264 | *p++ = 0; | ||
265 | |||
266 | /* Separate into range if exists */ | ||
267 | if ((rngptr = strchr(scontextp, '.')) != NULL) { | ||
268 | /* Remove '.' */ | ||
269 | *rngptr++ = 0; | ||
270 | } | ||
271 | |||
272 | catdatum = hashtab_search(policydb.p_cats.table, | ||
273 | scontextp); | ||
274 | if (!catdatum) { | ||
275 | rc = -EINVAL; | ||
276 | goto out; | ||
277 | } | ||
278 | |||
279 | rc = ebitmap_set_bit(&context->range.level[l].cat, | ||
280 | catdatum->value - 1, 1); | ||
281 | if (rc) | ||
282 | goto out; | ||
283 | |||
284 | /* If range, set all categories in range */ | ||
285 | if (rngptr) { | ||
286 | int i; | ||
287 | |||
288 | rngdatum = hashtab_search(policydb.p_cats.table, rngptr); | ||
289 | if (!rngdatum) { | ||
290 | rc = -EINVAL; | ||
291 | goto out; | ||
292 | } | ||
293 | |||
294 | if (catdatum->value >= rngdatum->value) { | ||
295 | rc = -EINVAL; | ||
296 | goto out; | ||
297 | } | ||
298 | |||
299 | for (i = catdatum->value; i < rngdatum->value; i++) { | ||
300 | rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1); | ||
301 | if (rc) | ||
302 | goto out; | ||
303 | } | ||
304 | } | ||
305 | |||
306 | if (delim != ',') | ||
307 | break; | ||
308 | } | ||
309 | } | ||
310 | if (delim == '-') { | ||
311 | /* Extract high sensitivity. */ | ||
312 | scontextp = p; | ||
313 | while (*p && *p != ':') | ||
314 | p++; | ||
315 | |||
316 | delim = *p; | ||
317 | if (delim != 0) | ||
318 | *p++ = 0; | ||
319 | } else | ||
320 | break; | ||
321 | } | ||
322 | |||
323 | if (l == 0) { | ||
324 | context->range.level[1].sens = context->range.level[0].sens; | ||
325 | rc = ebitmap_cpy(&context->range.level[1].cat, | ||
326 | &context->range.level[0].cat); | ||
327 | if (rc) | ||
328 | goto out; | ||
329 | } | ||
330 | *scontext = ++p; | ||
331 | rc = 0; | ||
332 | out: | ||
333 | return rc; | ||
334 | } | ||
335 | |||
336 | /* | ||
337 | * Copies the MLS range from `src' into `dst'. | ||
338 | */ | ||
339 | static inline int mls_copy_context(struct context *dst, | ||
340 | struct context *src) | ||
341 | { | ||
342 | int l, rc = 0; | ||
343 | |||
344 | /* Copy the MLS range from the source context */ | ||
345 | for (l = 0; l < 2; l++) { | ||
346 | dst->range.level[l].sens = src->range.level[l].sens; | ||
347 | rc = ebitmap_cpy(&dst->range.level[l].cat, | ||
348 | &src->range.level[l].cat); | ||
349 | if (rc) | ||
350 | break; | ||
351 | } | ||
352 | |||
353 | return rc; | ||
354 | } | ||
355 | |||
356 | /* | ||
357 | * Copies the effective MLS range from `src' into `dst'. | ||
358 | */ | ||
359 | static inline int mls_scopy_context(struct context *dst, | ||
360 | struct context *src) | ||
361 | { | ||
362 | int l, rc = 0; | ||
363 | |||
364 | /* Copy the MLS range from the source context */ | ||
365 | for (l = 0; l < 2; l++) { | ||
366 | dst->range.level[l].sens = src->range.level[0].sens; | ||
367 | rc = ebitmap_cpy(&dst->range.level[l].cat, | ||
368 | &src->range.level[0].cat); | ||
369 | if (rc) | ||
370 | break; | ||
371 | } | ||
372 | |||
373 | return rc; | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * Copies the MLS range `range' into `context'. | ||
378 | */ | ||
379 | static inline int mls_range_set(struct context *context, | ||
380 | struct mls_range *range) | ||
381 | { | ||
382 | int l, rc = 0; | ||
383 | |||
384 | /* Copy the MLS range into the context */ | ||
385 | for (l = 0; l < 2; l++) { | ||
386 | context->range.level[l].sens = range->level[l].sens; | ||
387 | rc = ebitmap_cpy(&context->range.level[l].cat, | ||
388 | &range->level[l].cat); | ||
389 | if (rc) | ||
390 | break; | ||
391 | } | ||
392 | |||
393 | return rc; | ||
394 | } | ||
395 | |||
396 | int mls_setup_user_range(struct context *fromcon, struct user_datum *user, | ||
397 | struct context *usercon) | ||
398 | { | ||
399 | if (selinux_mls_enabled) { | ||
400 | struct mls_level *fromcon_sen = &(fromcon->range.level[0]); | ||
401 | struct mls_level *fromcon_clr = &(fromcon->range.level[1]); | ||
402 | struct mls_level *user_low = &(user->range.level[0]); | ||
403 | struct mls_level *user_clr = &(user->range.level[1]); | ||
404 | struct mls_level *user_def = &(user->dfltlevel); | ||
405 | struct mls_level *usercon_sen = &(usercon->range.level[0]); | ||
406 | struct mls_level *usercon_clr = &(usercon->range.level[1]); | ||
407 | |||
408 | /* Honor the user's default level if we can */ | ||
409 | if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) { | ||
410 | *usercon_sen = *user_def; | ||
411 | } else if (mls_level_between(fromcon_sen, user_def, user_clr)) { | ||
412 | *usercon_sen = *fromcon_sen; | ||
413 | } else if (mls_level_between(fromcon_clr, user_low, user_def)) { | ||
414 | *usercon_sen = *user_low; | ||
415 | } else | ||
416 | return -EINVAL; | ||
417 | |||
418 | /* Lower the clearance of available contexts | ||
419 | if the clearance of "fromcon" is lower than | ||
420 | that of the user's default clearance (but | ||
421 | only if the "fromcon" clearance dominates | ||
422 | the user's computed sensitivity level) */ | ||
423 | if (mls_level_dom(user_clr, fromcon_clr)) { | ||
424 | *usercon_clr = *fromcon_clr; | ||
425 | } else if (mls_level_dom(fromcon_clr, user_clr)) { | ||
426 | *usercon_clr = *user_clr; | ||
427 | } else | ||
428 | return -EINVAL; | ||
429 | } | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | /* | ||
435 | * Convert the MLS fields in the security context | ||
436 | * structure `c' from the values specified in the | ||
437 | * policy `oldp' to the values specified in the policy `newp'. | ||
438 | */ | ||
439 | int mls_convert_context(struct policydb *oldp, | ||
440 | struct policydb *newp, | ||
441 | struct context *c) | ||
442 | { | ||
443 | struct level_datum *levdatum; | ||
444 | struct cat_datum *catdatum; | ||
445 | struct ebitmap bitmap; | ||
446 | int l, i; | ||
447 | |||
448 | if (!selinux_mls_enabled) | ||
449 | return 0; | ||
450 | |||
451 | for (l = 0; l < 2; l++) { | ||
452 | levdatum = hashtab_search(newp->p_levels.table, | ||
453 | oldp->p_sens_val_to_name[c->range.level[l].sens - 1]); | ||
454 | |||
455 | if (!levdatum) | ||
456 | return -EINVAL; | ||
457 | c->range.level[l].sens = levdatum->level->sens; | ||
458 | |||
459 | ebitmap_init(&bitmap); | ||
460 | for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) { | ||
461 | if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) { | ||
462 | int rc; | ||
463 | |||
464 | catdatum = hashtab_search(newp->p_cats.table, | ||
465 | oldp->p_cat_val_to_name[i - 1]); | ||
466 | if (!catdatum) | ||
467 | return -EINVAL; | ||
468 | rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); | ||
469 | if (rc) | ||
470 | return rc; | ||
471 | } | ||
472 | } | ||
473 | ebitmap_destroy(&c->range.level[l].cat); | ||
474 | c->range.level[l].cat = bitmap; | ||
475 | } | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | int mls_compute_sid(struct context *scontext, | ||
481 | struct context *tcontext, | ||
482 | u16 tclass, | ||
483 | u32 specified, | ||
484 | struct context *newcontext) | ||
485 | { | ||
486 | if (!selinux_mls_enabled) | ||
487 | return 0; | ||
488 | |||
489 | switch (specified) { | ||
490 | case AVTAB_TRANSITION: | ||
491 | if (tclass == SECCLASS_PROCESS) { | ||
492 | struct range_trans *rangetr; | ||
493 | /* Look for a range transition rule. */ | ||
494 | for (rangetr = policydb.range_tr; rangetr; | ||
495 | rangetr = rangetr->next) { | ||
496 | if (rangetr->dom == scontext->type && | ||
497 | rangetr->type == tcontext->type) { | ||
498 | /* Set the range from the rule */ | ||
499 | return mls_range_set(newcontext, | ||
500 | &rangetr->range); | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | /* Fallthrough */ | ||
505 | case AVTAB_CHANGE: | ||
506 | if (tclass == SECCLASS_PROCESS) | ||
507 | /* Use the process MLS attributes. */ | ||
508 | return mls_copy_context(newcontext, scontext); | ||
509 | else | ||
510 | /* Use the process effective MLS attributes. */ | ||
511 | return mls_scopy_context(newcontext, scontext); | ||
512 | case AVTAB_MEMBER: | ||
513 | /* Only polyinstantiate the MLS attributes if | ||
514 | the type is being polyinstantiated */ | ||
515 | if (newcontext->type != tcontext->type) { | ||
516 | /* Use the process effective MLS attributes. */ | ||
517 | return mls_scopy_context(newcontext, scontext); | ||
518 | } else { | ||
519 | /* Use the related object MLS attributes. */ | ||
520 | return mls_copy_context(newcontext, tcontext); | ||
521 | } | ||
522 | default: | ||
523 | return -EINVAL; | ||
524 | } | ||
525 | return -EINVAL; | ||
526 | } | ||
527 | |||
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h new file mode 100644 index 000000000000..0d37beaa85e2 --- /dev/null +++ b/security/selinux/ss/mls.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * Multi-level security (MLS) policy operations. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | /* | ||
7 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
8 | * | ||
9 | * Support for enhanced MLS infrastructure. | ||
10 | * | ||
11 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
12 | */ | ||
13 | |||
14 | #ifndef _SS_MLS_H_ | ||
15 | #define _SS_MLS_H_ | ||
16 | |||
17 | #include "context.h" | ||
18 | #include "policydb.h" | ||
19 | |||
20 | int mls_compute_context_len(struct context *context); | ||
21 | void mls_sid_to_context(struct context *context, char **scontext); | ||
22 | int mls_context_isvalid(struct policydb *p, struct context *c); | ||
23 | |||
24 | int mls_context_to_sid(char oldc, | ||
25 | char **scontext, | ||
26 | struct context *context); | ||
27 | |||
28 | int mls_convert_context(struct policydb *oldp, | ||
29 | struct policydb *newp, | ||
30 | struct context *context); | ||
31 | |||
32 | int mls_compute_sid(struct context *scontext, | ||
33 | struct context *tcontext, | ||
34 | u16 tclass, | ||
35 | u32 specified, | ||
36 | struct context *newcontext); | ||
37 | |||
38 | int mls_setup_user_range(struct context *fromcon, struct user_datum *user, | ||
39 | struct context *usercon); | ||
40 | |||
41 | #endif /* _SS_MLS_H */ | ||
42 | |||
diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h new file mode 100644 index 000000000000..0c692d58d489 --- /dev/null +++ b/security/selinux/ss/mls_types.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * Type definitions for the multi-level security (MLS) policy. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | /* | ||
7 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
8 | * | ||
9 | * Support for enhanced MLS infrastructure. | ||
10 | * | ||
11 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
12 | */ | ||
13 | |||
14 | #ifndef _SS_MLS_TYPES_H_ | ||
15 | #define _SS_MLS_TYPES_H_ | ||
16 | |||
17 | #include "security.h" | ||
18 | |||
19 | struct mls_level { | ||
20 | u32 sens; /* sensitivity */ | ||
21 | struct ebitmap cat; /* category set */ | ||
22 | }; | ||
23 | |||
24 | struct mls_range { | ||
25 | struct mls_level level[2]; /* low == level[0], high == level[1] */ | ||
26 | }; | ||
27 | |||
28 | static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2) | ||
29 | { | ||
30 | if (!selinux_mls_enabled) | ||
31 | return 1; | ||
32 | |||
33 | return ((l1->sens == l2->sens) && | ||
34 | ebitmap_cmp(&l1->cat, &l2->cat)); | ||
35 | } | ||
36 | |||
37 | static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2) | ||
38 | { | ||
39 | if (!selinux_mls_enabled) | ||
40 | return 1; | ||
41 | |||
42 | return ((l1->sens >= l2->sens) && | ||
43 | ebitmap_contains(&l1->cat, &l2->cat)); | ||
44 | } | ||
45 | |||
46 | #define mls_level_incomp(l1, l2) \ | ||
47 | (!mls_level_dom((l1), (l2)) && !mls_level_dom((l2), (l1))) | ||
48 | |||
49 | #define mls_level_between(l1, l2, l3) \ | ||
50 | (mls_level_dom((l1), (l2)) && mls_level_dom((l3), (l1))) | ||
51 | |||
52 | #define mls_range_contains(r1, r2) \ | ||
53 | (mls_level_dom(&(r2).level[0], &(r1).level[0]) && \ | ||
54 | mls_level_dom(&(r1).level[1], &(r2).level[1])) | ||
55 | |||
56 | #endif /* _SS_MLS_TYPES_H_ */ | ||
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c new file mode 100644 index 000000000000..14190efbf333 --- /dev/null +++ b/security/selinux/ss/policydb.c | |||
@@ -0,0 +1,1843 @@ | |||
1 | /* | ||
2 | * Implementation of the policy database. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
9 | * | ||
10 | * Support for enhanced MLS infrastructure. | ||
11 | * | ||
12 | * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
13 | * | ||
14 | * Added conditional policy language extensions | ||
15 | * | ||
16 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
17 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
18 | * This program is free software; you can redistribute it and/or modify | ||
19 | * it under the terms of the GNU General Public License as published by | ||
20 | * the Free Software Foundation, version 2. | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/string.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include "security.h" | ||
28 | |||
29 | #include "policydb.h" | ||
30 | #include "conditional.h" | ||
31 | #include "mls.h" | ||
32 | |||
33 | #define _DEBUG_HASHES | ||
34 | |||
35 | #ifdef DEBUG_HASHES | ||
36 | static char *symtab_name[SYM_NUM] = { | ||
37 | "common prefixes", | ||
38 | "classes", | ||
39 | "roles", | ||
40 | "types", | ||
41 | "users", | ||
42 | "bools", | ||
43 | "levels", | ||
44 | "categories", | ||
45 | }; | ||
46 | #endif | ||
47 | |||
48 | int selinux_mls_enabled = 0; | ||
49 | |||
50 | static unsigned int symtab_sizes[SYM_NUM] = { | ||
51 | 2, | ||
52 | 32, | ||
53 | 16, | ||
54 | 512, | ||
55 | 128, | ||
56 | 16, | ||
57 | 16, | ||
58 | 16, | ||
59 | }; | ||
60 | |||
61 | struct policydb_compat_info { | ||
62 | int version; | ||
63 | int sym_num; | ||
64 | int ocon_num; | ||
65 | }; | ||
66 | |||
67 | /* These need to be updated if SYM_NUM or OCON_NUM changes */ | ||
68 | static struct policydb_compat_info policydb_compat[] = { | ||
69 | { | ||
70 | .version = POLICYDB_VERSION_BASE, | ||
71 | .sym_num = SYM_NUM - 3, | ||
72 | .ocon_num = OCON_NUM - 1, | ||
73 | }, | ||
74 | { | ||
75 | .version = POLICYDB_VERSION_BOOL, | ||
76 | .sym_num = SYM_NUM - 2, | ||
77 | .ocon_num = OCON_NUM - 1, | ||
78 | }, | ||
79 | { | ||
80 | .version = POLICYDB_VERSION_IPV6, | ||
81 | .sym_num = SYM_NUM - 2, | ||
82 | .ocon_num = OCON_NUM, | ||
83 | }, | ||
84 | { | ||
85 | .version = POLICYDB_VERSION_NLCLASS, | ||
86 | .sym_num = SYM_NUM - 2, | ||
87 | .ocon_num = OCON_NUM, | ||
88 | }, | ||
89 | { | ||
90 | .version = POLICYDB_VERSION_MLS, | ||
91 | .sym_num = SYM_NUM, | ||
92 | .ocon_num = OCON_NUM, | ||
93 | }, | ||
94 | }; | ||
95 | |||
96 | static struct policydb_compat_info *policydb_lookup_compat(int version) | ||
97 | { | ||
98 | int i; | ||
99 | struct policydb_compat_info *info = NULL; | ||
100 | |||
101 | for (i = 0; i < sizeof(policydb_compat)/sizeof(*info); i++) { | ||
102 | if (policydb_compat[i].version == version) { | ||
103 | info = &policydb_compat[i]; | ||
104 | break; | ||
105 | } | ||
106 | } | ||
107 | return info; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * Initialize the role table. | ||
112 | */ | ||
113 | static int roles_init(struct policydb *p) | ||
114 | { | ||
115 | char *key = NULL; | ||
116 | int rc; | ||
117 | struct role_datum *role; | ||
118 | |||
119 | role = kmalloc(sizeof(*role), GFP_KERNEL); | ||
120 | if (!role) { | ||
121 | rc = -ENOMEM; | ||
122 | goto out; | ||
123 | } | ||
124 | memset(role, 0, sizeof(*role)); | ||
125 | role->value = ++p->p_roles.nprim; | ||
126 | if (role->value != OBJECT_R_VAL) { | ||
127 | rc = -EINVAL; | ||
128 | goto out_free_role; | ||
129 | } | ||
130 | key = kmalloc(strlen(OBJECT_R)+1,GFP_KERNEL); | ||
131 | if (!key) { | ||
132 | rc = -ENOMEM; | ||
133 | goto out_free_role; | ||
134 | } | ||
135 | strcpy(key, OBJECT_R); | ||
136 | rc = hashtab_insert(p->p_roles.table, key, role); | ||
137 | if (rc) | ||
138 | goto out_free_key; | ||
139 | out: | ||
140 | return rc; | ||
141 | |||
142 | out_free_key: | ||
143 | kfree(key); | ||
144 | out_free_role: | ||
145 | kfree(role); | ||
146 | goto out; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Initialize a policy database structure. | ||
151 | */ | ||
152 | static int policydb_init(struct policydb *p) | ||
153 | { | ||
154 | int i, rc; | ||
155 | |||
156 | memset(p, 0, sizeof(*p)); | ||
157 | |||
158 | for (i = 0; i < SYM_NUM; i++) { | ||
159 | rc = symtab_init(&p->symtab[i], symtab_sizes[i]); | ||
160 | if (rc) | ||
161 | goto out_free_symtab; | ||
162 | } | ||
163 | |||
164 | rc = avtab_init(&p->te_avtab); | ||
165 | if (rc) | ||
166 | goto out_free_symtab; | ||
167 | |||
168 | rc = roles_init(p); | ||
169 | if (rc) | ||
170 | goto out_free_avtab; | ||
171 | |||
172 | rc = cond_policydb_init(p); | ||
173 | if (rc) | ||
174 | goto out_free_avtab; | ||
175 | |||
176 | out: | ||
177 | return rc; | ||
178 | |||
179 | out_free_avtab: | ||
180 | avtab_destroy(&p->te_avtab); | ||
181 | |||
182 | out_free_symtab: | ||
183 | for (i = 0; i < SYM_NUM; i++) | ||
184 | hashtab_destroy(p->symtab[i].table); | ||
185 | goto out; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * The following *_index functions are used to | ||
190 | * define the val_to_name and val_to_struct arrays | ||
191 | * in a policy database structure. The val_to_name | ||
192 | * arrays are used when converting security context | ||
193 | * structures into string representations. The | ||
194 | * val_to_struct arrays are used when the attributes | ||
195 | * of a class, role, or user are needed. | ||
196 | */ | ||
197 | |||
198 | static int common_index(void *key, void *datum, void *datap) | ||
199 | { | ||
200 | struct policydb *p; | ||
201 | struct common_datum *comdatum; | ||
202 | |||
203 | comdatum = datum; | ||
204 | p = datap; | ||
205 | if (!comdatum->value || comdatum->value > p->p_commons.nprim) | ||
206 | return -EINVAL; | ||
207 | p->p_common_val_to_name[comdatum->value - 1] = key; | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int class_index(void *key, void *datum, void *datap) | ||
212 | { | ||
213 | struct policydb *p; | ||
214 | struct class_datum *cladatum; | ||
215 | |||
216 | cladatum = datum; | ||
217 | p = datap; | ||
218 | if (!cladatum->value || cladatum->value > p->p_classes.nprim) | ||
219 | return -EINVAL; | ||
220 | p->p_class_val_to_name[cladatum->value - 1] = key; | ||
221 | p->class_val_to_struct[cladatum->value - 1] = cladatum; | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int role_index(void *key, void *datum, void *datap) | ||
226 | { | ||
227 | struct policydb *p; | ||
228 | struct role_datum *role; | ||
229 | |||
230 | role = datum; | ||
231 | p = datap; | ||
232 | if (!role->value || role->value > p->p_roles.nprim) | ||
233 | return -EINVAL; | ||
234 | p->p_role_val_to_name[role->value - 1] = key; | ||
235 | p->role_val_to_struct[role->value - 1] = role; | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static int type_index(void *key, void *datum, void *datap) | ||
240 | { | ||
241 | struct policydb *p; | ||
242 | struct type_datum *typdatum; | ||
243 | |||
244 | typdatum = datum; | ||
245 | p = datap; | ||
246 | |||
247 | if (typdatum->primary) { | ||
248 | if (!typdatum->value || typdatum->value > p->p_types.nprim) | ||
249 | return -EINVAL; | ||
250 | p->p_type_val_to_name[typdatum->value - 1] = key; | ||
251 | } | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static int user_index(void *key, void *datum, void *datap) | ||
257 | { | ||
258 | struct policydb *p; | ||
259 | struct user_datum *usrdatum; | ||
260 | |||
261 | usrdatum = datum; | ||
262 | p = datap; | ||
263 | if (!usrdatum->value || usrdatum->value > p->p_users.nprim) | ||
264 | return -EINVAL; | ||
265 | p->p_user_val_to_name[usrdatum->value - 1] = key; | ||
266 | p->user_val_to_struct[usrdatum->value - 1] = usrdatum; | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static int sens_index(void *key, void *datum, void *datap) | ||
271 | { | ||
272 | struct policydb *p; | ||
273 | struct level_datum *levdatum; | ||
274 | |||
275 | levdatum = datum; | ||
276 | p = datap; | ||
277 | |||
278 | if (!levdatum->isalias) { | ||
279 | if (!levdatum->level->sens || | ||
280 | levdatum->level->sens > p->p_levels.nprim) | ||
281 | return -EINVAL; | ||
282 | p->p_sens_val_to_name[levdatum->level->sens - 1] = key; | ||
283 | } | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int cat_index(void *key, void *datum, void *datap) | ||
289 | { | ||
290 | struct policydb *p; | ||
291 | struct cat_datum *catdatum; | ||
292 | |||
293 | catdatum = datum; | ||
294 | p = datap; | ||
295 | |||
296 | if (!catdatum->isalias) { | ||
297 | if (!catdatum->value || catdatum->value > p->p_cats.nprim) | ||
298 | return -EINVAL; | ||
299 | p->p_cat_val_to_name[catdatum->value - 1] = key; | ||
300 | } | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) = | ||
306 | { | ||
307 | common_index, | ||
308 | class_index, | ||
309 | role_index, | ||
310 | type_index, | ||
311 | user_index, | ||
312 | cond_index_bool, | ||
313 | sens_index, | ||
314 | cat_index, | ||
315 | }; | ||
316 | |||
317 | /* | ||
318 | * Define the common val_to_name array and the class | ||
319 | * val_to_name and val_to_struct arrays in a policy | ||
320 | * database structure. | ||
321 | * | ||
322 | * Caller must clean up upon failure. | ||
323 | */ | ||
324 | static int policydb_index_classes(struct policydb *p) | ||
325 | { | ||
326 | int rc; | ||
327 | |||
328 | p->p_common_val_to_name = | ||
329 | kmalloc(p->p_commons.nprim * sizeof(char *), GFP_KERNEL); | ||
330 | if (!p->p_common_val_to_name) { | ||
331 | rc = -ENOMEM; | ||
332 | goto out; | ||
333 | } | ||
334 | |||
335 | rc = hashtab_map(p->p_commons.table, common_index, p); | ||
336 | if (rc) | ||
337 | goto out; | ||
338 | |||
339 | p->class_val_to_struct = | ||
340 | kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), GFP_KERNEL); | ||
341 | if (!p->class_val_to_struct) { | ||
342 | rc = -ENOMEM; | ||
343 | goto out; | ||
344 | } | ||
345 | |||
346 | p->p_class_val_to_name = | ||
347 | kmalloc(p->p_classes.nprim * sizeof(char *), GFP_KERNEL); | ||
348 | if (!p->p_class_val_to_name) { | ||
349 | rc = -ENOMEM; | ||
350 | goto out; | ||
351 | } | ||
352 | |||
353 | rc = hashtab_map(p->p_classes.table, class_index, p); | ||
354 | out: | ||
355 | return rc; | ||
356 | } | ||
357 | |||
358 | #ifdef DEBUG_HASHES | ||
359 | static void symtab_hash_eval(struct symtab *s) | ||
360 | { | ||
361 | int i; | ||
362 | |||
363 | for (i = 0; i < SYM_NUM; i++) { | ||
364 | struct hashtab *h = s[i].table; | ||
365 | struct hashtab_info info; | ||
366 | |||
367 | hashtab_stat(h, &info); | ||
368 | printk(KERN_INFO "%s: %d entries and %d/%d buckets used, " | ||
369 | "longest chain length %d\n", symtab_name[i], h->nel, | ||
370 | info.slots_used, h->size, info.max_chain_len); | ||
371 | } | ||
372 | } | ||
373 | #endif | ||
374 | |||
375 | /* | ||
376 | * Define the other val_to_name and val_to_struct arrays | ||
377 | * in a policy database structure. | ||
378 | * | ||
379 | * Caller must clean up on failure. | ||
380 | */ | ||
381 | static int policydb_index_others(struct policydb *p) | ||
382 | { | ||
383 | int i, rc = 0; | ||
384 | |||
385 | printk(KERN_INFO "security: %d users, %d roles, %d types, %d bools", | ||
386 | p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim); | ||
387 | if (selinux_mls_enabled) | ||
388 | printk(", %d sens, %d cats", p->p_levels.nprim, | ||
389 | p->p_cats.nprim); | ||
390 | printk("\n"); | ||
391 | |||
392 | printk(KERN_INFO "security: %d classes, %d rules\n", | ||
393 | p->p_classes.nprim, p->te_avtab.nel); | ||
394 | |||
395 | #ifdef DEBUG_HASHES | ||
396 | avtab_hash_eval(&p->te_avtab, "rules"); | ||
397 | symtab_hash_eval(p->symtab); | ||
398 | #endif | ||
399 | |||
400 | p->role_val_to_struct = | ||
401 | kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)), | ||
402 | GFP_KERNEL); | ||
403 | if (!p->role_val_to_struct) { | ||
404 | rc = -ENOMEM; | ||
405 | goto out; | ||
406 | } | ||
407 | |||
408 | p->user_val_to_struct = | ||
409 | kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)), | ||
410 | GFP_KERNEL); | ||
411 | if (!p->user_val_to_struct) { | ||
412 | rc = -ENOMEM; | ||
413 | goto out; | ||
414 | } | ||
415 | |||
416 | if (cond_init_bool_indexes(p)) { | ||
417 | rc = -ENOMEM; | ||
418 | goto out; | ||
419 | } | ||
420 | |||
421 | for (i = SYM_ROLES; i < SYM_NUM; i++) { | ||
422 | p->sym_val_to_name[i] = | ||
423 | kmalloc(p->symtab[i].nprim * sizeof(char *), GFP_KERNEL); | ||
424 | if (!p->sym_val_to_name[i]) { | ||
425 | rc = -ENOMEM; | ||
426 | goto out; | ||
427 | } | ||
428 | rc = hashtab_map(p->symtab[i].table, index_f[i], p); | ||
429 | if (rc) | ||
430 | goto out; | ||
431 | } | ||
432 | |||
433 | out: | ||
434 | return rc; | ||
435 | } | ||
436 | |||
437 | /* | ||
438 | * The following *_destroy functions are used to | ||
439 | * free any memory allocated for each kind of | ||
440 | * symbol data in the policy database. | ||
441 | */ | ||
442 | |||
443 | static int perm_destroy(void *key, void *datum, void *p) | ||
444 | { | ||
445 | kfree(key); | ||
446 | kfree(datum); | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static int common_destroy(void *key, void *datum, void *p) | ||
451 | { | ||
452 | struct common_datum *comdatum; | ||
453 | |||
454 | kfree(key); | ||
455 | comdatum = datum; | ||
456 | hashtab_map(comdatum->permissions.table, perm_destroy, NULL); | ||
457 | hashtab_destroy(comdatum->permissions.table); | ||
458 | kfree(datum); | ||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static int class_destroy(void *key, void *datum, void *p) | ||
463 | { | ||
464 | struct class_datum *cladatum; | ||
465 | struct constraint_node *constraint, *ctemp; | ||
466 | struct constraint_expr *e, *etmp; | ||
467 | |||
468 | kfree(key); | ||
469 | cladatum = datum; | ||
470 | hashtab_map(cladatum->permissions.table, perm_destroy, NULL); | ||
471 | hashtab_destroy(cladatum->permissions.table); | ||
472 | constraint = cladatum->constraints; | ||
473 | while (constraint) { | ||
474 | e = constraint->expr; | ||
475 | while (e) { | ||
476 | ebitmap_destroy(&e->names); | ||
477 | etmp = e; | ||
478 | e = e->next; | ||
479 | kfree(etmp); | ||
480 | } | ||
481 | ctemp = constraint; | ||
482 | constraint = constraint->next; | ||
483 | kfree(ctemp); | ||
484 | } | ||
485 | |||
486 | constraint = cladatum->validatetrans; | ||
487 | while (constraint) { | ||
488 | e = constraint->expr; | ||
489 | while (e) { | ||
490 | ebitmap_destroy(&e->names); | ||
491 | etmp = e; | ||
492 | e = e->next; | ||
493 | kfree(etmp); | ||
494 | } | ||
495 | ctemp = constraint; | ||
496 | constraint = constraint->next; | ||
497 | kfree(ctemp); | ||
498 | } | ||
499 | |||
500 | kfree(cladatum->comkey); | ||
501 | kfree(datum); | ||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static int role_destroy(void *key, void *datum, void *p) | ||
506 | { | ||
507 | struct role_datum *role; | ||
508 | |||
509 | kfree(key); | ||
510 | role = datum; | ||
511 | ebitmap_destroy(&role->dominates); | ||
512 | ebitmap_destroy(&role->types); | ||
513 | kfree(datum); | ||
514 | return 0; | ||
515 | } | ||
516 | |||
517 | static int type_destroy(void *key, void *datum, void *p) | ||
518 | { | ||
519 | kfree(key); | ||
520 | kfree(datum); | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | static int user_destroy(void *key, void *datum, void *p) | ||
525 | { | ||
526 | struct user_datum *usrdatum; | ||
527 | |||
528 | kfree(key); | ||
529 | usrdatum = datum; | ||
530 | ebitmap_destroy(&usrdatum->roles); | ||
531 | ebitmap_destroy(&usrdatum->range.level[0].cat); | ||
532 | ebitmap_destroy(&usrdatum->range.level[1].cat); | ||
533 | ebitmap_destroy(&usrdatum->dfltlevel.cat); | ||
534 | kfree(datum); | ||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | static int sens_destroy(void *key, void *datum, void *p) | ||
539 | { | ||
540 | struct level_datum *levdatum; | ||
541 | |||
542 | kfree(key); | ||
543 | levdatum = datum; | ||
544 | ebitmap_destroy(&levdatum->level->cat); | ||
545 | kfree(levdatum->level); | ||
546 | kfree(datum); | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int cat_destroy(void *key, void *datum, void *p) | ||
551 | { | ||
552 | kfree(key); | ||
553 | kfree(datum); | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = | ||
558 | { | ||
559 | common_destroy, | ||
560 | class_destroy, | ||
561 | role_destroy, | ||
562 | type_destroy, | ||
563 | user_destroy, | ||
564 | cond_destroy_bool, | ||
565 | sens_destroy, | ||
566 | cat_destroy, | ||
567 | }; | ||
568 | |||
569 | static void ocontext_destroy(struct ocontext *c, int i) | ||
570 | { | ||
571 | context_destroy(&c->context[0]); | ||
572 | context_destroy(&c->context[1]); | ||
573 | if (i == OCON_ISID || i == OCON_FS || | ||
574 | i == OCON_NETIF || i == OCON_FSUSE) | ||
575 | kfree(c->u.name); | ||
576 | kfree(c); | ||
577 | } | ||
578 | |||
579 | /* | ||
580 | * Free any memory allocated by a policy database structure. | ||
581 | */ | ||
582 | void policydb_destroy(struct policydb *p) | ||
583 | { | ||
584 | struct ocontext *c, *ctmp; | ||
585 | struct genfs *g, *gtmp; | ||
586 | int i; | ||
587 | |||
588 | for (i = 0; i < SYM_NUM; i++) { | ||
589 | hashtab_map(p->symtab[i].table, destroy_f[i], NULL); | ||
590 | hashtab_destroy(p->symtab[i].table); | ||
591 | } | ||
592 | |||
593 | for (i = 0; i < SYM_NUM; i++) { | ||
594 | if (p->sym_val_to_name[i]) | ||
595 | kfree(p->sym_val_to_name[i]); | ||
596 | } | ||
597 | |||
598 | if (p->class_val_to_struct) | ||
599 | kfree(p->class_val_to_struct); | ||
600 | if (p->role_val_to_struct) | ||
601 | kfree(p->role_val_to_struct); | ||
602 | if (p->user_val_to_struct) | ||
603 | kfree(p->user_val_to_struct); | ||
604 | |||
605 | avtab_destroy(&p->te_avtab); | ||
606 | |||
607 | for (i = 0; i < OCON_NUM; i++) { | ||
608 | c = p->ocontexts[i]; | ||
609 | while (c) { | ||
610 | ctmp = c; | ||
611 | c = c->next; | ||
612 | ocontext_destroy(ctmp,i); | ||
613 | } | ||
614 | } | ||
615 | |||
616 | g = p->genfs; | ||
617 | while (g) { | ||
618 | kfree(g->fstype); | ||
619 | c = g->head; | ||
620 | while (c) { | ||
621 | ctmp = c; | ||
622 | c = c->next; | ||
623 | ocontext_destroy(ctmp,OCON_FSUSE); | ||
624 | } | ||
625 | gtmp = g; | ||
626 | g = g->next; | ||
627 | kfree(gtmp); | ||
628 | } | ||
629 | |||
630 | cond_policydb_destroy(p); | ||
631 | |||
632 | return; | ||
633 | } | ||
634 | |||
635 | /* | ||
636 | * Load the initial SIDs specified in a policy database | ||
637 | * structure into a SID table. | ||
638 | */ | ||
639 | int policydb_load_isids(struct policydb *p, struct sidtab *s) | ||
640 | { | ||
641 | struct ocontext *head, *c; | ||
642 | int rc; | ||
643 | |||
644 | rc = sidtab_init(s); | ||
645 | if (rc) { | ||
646 | printk(KERN_ERR "security: out of memory on SID table init\n"); | ||
647 | goto out; | ||
648 | } | ||
649 | |||
650 | head = p->ocontexts[OCON_ISID]; | ||
651 | for (c = head; c; c = c->next) { | ||
652 | if (!c->context[0].user) { | ||
653 | printk(KERN_ERR "security: SID %s was never " | ||
654 | "defined.\n", c->u.name); | ||
655 | rc = -EINVAL; | ||
656 | goto out; | ||
657 | } | ||
658 | if (sidtab_insert(s, c->sid[0], &c->context[0])) { | ||
659 | printk(KERN_ERR "security: unable to load initial " | ||
660 | "SID %s.\n", c->u.name); | ||
661 | rc = -EINVAL; | ||
662 | goto out; | ||
663 | } | ||
664 | } | ||
665 | out: | ||
666 | return rc; | ||
667 | } | ||
668 | |||
669 | /* | ||
670 | * Return 1 if the fields in the security context | ||
671 | * structure `c' are valid. Return 0 otherwise. | ||
672 | */ | ||
673 | int policydb_context_isvalid(struct policydb *p, struct context *c) | ||
674 | { | ||
675 | struct role_datum *role; | ||
676 | struct user_datum *usrdatum; | ||
677 | |||
678 | if (!c->role || c->role > p->p_roles.nprim) | ||
679 | return 0; | ||
680 | |||
681 | if (!c->user || c->user > p->p_users.nprim) | ||
682 | return 0; | ||
683 | |||
684 | if (!c->type || c->type > p->p_types.nprim) | ||
685 | return 0; | ||
686 | |||
687 | if (c->role != OBJECT_R_VAL) { | ||
688 | /* | ||
689 | * Role must be authorized for the type. | ||
690 | */ | ||
691 | role = p->role_val_to_struct[c->role - 1]; | ||
692 | if (!ebitmap_get_bit(&role->types, | ||
693 | c->type - 1)) | ||
694 | /* role may not be associated with type */ | ||
695 | return 0; | ||
696 | |||
697 | /* | ||
698 | * User must be authorized for the role. | ||
699 | */ | ||
700 | usrdatum = p->user_val_to_struct[c->user - 1]; | ||
701 | if (!usrdatum) | ||
702 | return 0; | ||
703 | |||
704 | if (!ebitmap_get_bit(&usrdatum->roles, | ||
705 | c->role - 1)) | ||
706 | /* user may not be associated with role */ | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | if (!mls_context_isvalid(p, c)) | ||
711 | return 0; | ||
712 | |||
713 | return 1; | ||
714 | } | ||
715 | |||
716 | /* | ||
717 | * Read a MLS range structure from a policydb binary | ||
718 | * representation file. | ||
719 | */ | ||
720 | static int mls_read_range_helper(struct mls_range *r, void *fp) | ||
721 | { | ||
722 | u32 buf[2], items; | ||
723 | int rc; | ||
724 | |||
725 | rc = next_entry(buf, fp, sizeof(u32)); | ||
726 | if (rc < 0) | ||
727 | goto out; | ||
728 | |||
729 | items = le32_to_cpu(buf[0]); | ||
730 | if (items > ARRAY_SIZE(buf)) { | ||
731 | printk(KERN_ERR "security: mls: range overflow\n"); | ||
732 | rc = -EINVAL; | ||
733 | goto out; | ||
734 | } | ||
735 | rc = next_entry(buf, fp, sizeof(u32) * items); | ||
736 | if (rc < 0) { | ||
737 | printk(KERN_ERR "security: mls: truncated range\n"); | ||
738 | goto out; | ||
739 | } | ||
740 | r->level[0].sens = le32_to_cpu(buf[0]); | ||
741 | if (items > 1) | ||
742 | r->level[1].sens = le32_to_cpu(buf[1]); | ||
743 | else | ||
744 | r->level[1].sens = r->level[0].sens; | ||
745 | |||
746 | rc = ebitmap_read(&r->level[0].cat, fp); | ||
747 | if (rc) { | ||
748 | printk(KERN_ERR "security: mls: error reading low " | ||
749 | "categories\n"); | ||
750 | goto out; | ||
751 | } | ||
752 | if (items > 1) { | ||
753 | rc = ebitmap_read(&r->level[1].cat, fp); | ||
754 | if (rc) { | ||
755 | printk(KERN_ERR "security: mls: error reading high " | ||
756 | "categories\n"); | ||
757 | goto bad_high; | ||
758 | } | ||
759 | } else { | ||
760 | rc = ebitmap_cpy(&r->level[1].cat, &r->level[0].cat); | ||
761 | if (rc) { | ||
762 | printk(KERN_ERR "security: mls: out of memory\n"); | ||
763 | goto bad_high; | ||
764 | } | ||
765 | } | ||
766 | |||
767 | rc = 0; | ||
768 | out: | ||
769 | return rc; | ||
770 | bad_high: | ||
771 | ebitmap_destroy(&r->level[0].cat); | ||
772 | goto out; | ||
773 | } | ||
774 | |||
775 | /* | ||
776 | * Read and validate a security context structure | ||
777 | * from a policydb binary representation file. | ||
778 | */ | ||
779 | static int context_read_and_validate(struct context *c, | ||
780 | struct policydb *p, | ||
781 | void *fp) | ||
782 | { | ||
783 | u32 buf[3]; | ||
784 | int rc; | ||
785 | |||
786 | rc = next_entry(buf, fp, sizeof buf); | ||
787 | if (rc < 0) { | ||
788 | printk(KERN_ERR "security: context truncated\n"); | ||
789 | goto out; | ||
790 | } | ||
791 | c->user = le32_to_cpu(buf[0]); | ||
792 | c->role = le32_to_cpu(buf[1]); | ||
793 | c->type = le32_to_cpu(buf[2]); | ||
794 | if (p->policyvers >= POLICYDB_VERSION_MLS) { | ||
795 | if (mls_read_range_helper(&c->range, fp)) { | ||
796 | printk(KERN_ERR "security: error reading MLS range of " | ||
797 | "context\n"); | ||
798 | rc = -EINVAL; | ||
799 | goto out; | ||
800 | } | ||
801 | } | ||
802 | |||
803 | if (!policydb_context_isvalid(p, c)) { | ||
804 | printk(KERN_ERR "security: invalid security context\n"); | ||
805 | context_destroy(c); | ||
806 | rc = -EINVAL; | ||
807 | } | ||
808 | out: | ||
809 | return rc; | ||
810 | } | ||
811 | |||
812 | /* | ||
813 | * The following *_read functions are used to | ||
814 | * read the symbol data from a policy database | ||
815 | * binary representation file. | ||
816 | */ | ||
817 | |||
818 | static int perm_read(struct policydb *p, struct hashtab *h, void *fp) | ||
819 | { | ||
820 | char *key = NULL; | ||
821 | struct perm_datum *perdatum; | ||
822 | int rc; | ||
823 | u32 buf[2], len; | ||
824 | |||
825 | perdatum = kmalloc(sizeof(*perdatum), GFP_KERNEL); | ||
826 | if (!perdatum) { | ||
827 | rc = -ENOMEM; | ||
828 | goto out; | ||
829 | } | ||
830 | memset(perdatum, 0, sizeof(*perdatum)); | ||
831 | |||
832 | rc = next_entry(buf, fp, sizeof buf); | ||
833 | if (rc < 0) | ||
834 | goto bad; | ||
835 | |||
836 | len = le32_to_cpu(buf[0]); | ||
837 | perdatum->value = le32_to_cpu(buf[1]); | ||
838 | |||
839 | key = kmalloc(len + 1,GFP_KERNEL); | ||
840 | if (!key) { | ||
841 | rc = -ENOMEM; | ||
842 | goto bad; | ||
843 | } | ||
844 | rc = next_entry(key, fp, len); | ||
845 | if (rc < 0) | ||
846 | goto bad; | ||
847 | key[len] = 0; | ||
848 | |||
849 | rc = hashtab_insert(h, key, perdatum); | ||
850 | if (rc) | ||
851 | goto bad; | ||
852 | out: | ||
853 | return rc; | ||
854 | bad: | ||
855 | perm_destroy(key, perdatum, NULL); | ||
856 | goto out; | ||
857 | } | ||
858 | |||
859 | static int common_read(struct policydb *p, struct hashtab *h, void *fp) | ||
860 | { | ||
861 | char *key = NULL; | ||
862 | struct common_datum *comdatum; | ||
863 | u32 buf[4], len, nel; | ||
864 | int i, rc; | ||
865 | |||
866 | comdatum = kmalloc(sizeof(*comdatum), GFP_KERNEL); | ||
867 | if (!comdatum) { | ||
868 | rc = -ENOMEM; | ||
869 | goto out; | ||
870 | } | ||
871 | memset(comdatum, 0, sizeof(*comdatum)); | ||
872 | |||
873 | rc = next_entry(buf, fp, sizeof buf); | ||
874 | if (rc < 0) | ||
875 | goto bad; | ||
876 | |||
877 | len = le32_to_cpu(buf[0]); | ||
878 | comdatum->value = le32_to_cpu(buf[1]); | ||
879 | |||
880 | rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE); | ||
881 | if (rc) | ||
882 | goto bad; | ||
883 | comdatum->permissions.nprim = le32_to_cpu(buf[2]); | ||
884 | nel = le32_to_cpu(buf[3]); | ||
885 | |||
886 | key = kmalloc(len + 1,GFP_KERNEL); | ||
887 | if (!key) { | ||
888 | rc = -ENOMEM; | ||
889 | goto bad; | ||
890 | } | ||
891 | rc = next_entry(key, fp, len); | ||
892 | if (rc < 0) | ||
893 | goto bad; | ||
894 | key[len] = 0; | ||
895 | |||
896 | for (i = 0; i < nel; i++) { | ||
897 | rc = perm_read(p, comdatum->permissions.table, fp); | ||
898 | if (rc) | ||
899 | goto bad; | ||
900 | } | ||
901 | |||
902 | rc = hashtab_insert(h, key, comdatum); | ||
903 | if (rc) | ||
904 | goto bad; | ||
905 | out: | ||
906 | return rc; | ||
907 | bad: | ||
908 | common_destroy(key, comdatum, NULL); | ||
909 | goto out; | ||
910 | } | ||
911 | |||
912 | static int read_cons_helper(struct constraint_node **nodep, int ncons, | ||
913 | int allowxtarget, void *fp) | ||
914 | { | ||
915 | struct constraint_node *c, *lc; | ||
916 | struct constraint_expr *e, *le; | ||
917 | u32 buf[3], nexpr; | ||
918 | int rc, i, j, depth; | ||
919 | |||
920 | lc = NULL; | ||
921 | for (i = 0; i < ncons; i++) { | ||
922 | c = kmalloc(sizeof(*c), GFP_KERNEL); | ||
923 | if (!c) | ||
924 | return -ENOMEM; | ||
925 | memset(c, 0, sizeof(*c)); | ||
926 | |||
927 | if (lc) { | ||
928 | lc->next = c; | ||
929 | } else { | ||
930 | *nodep = c; | ||
931 | } | ||
932 | |||
933 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); | ||
934 | if (rc < 0) | ||
935 | return rc; | ||
936 | c->permissions = le32_to_cpu(buf[0]); | ||
937 | nexpr = le32_to_cpu(buf[1]); | ||
938 | le = NULL; | ||
939 | depth = -1; | ||
940 | for (j = 0; j < nexpr; j++) { | ||
941 | e = kmalloc(sizeof(*e), GFP_KERNEL); | ||
942 | if (!e) | ||
943 | return -ENOMEM; | ||
944 | memset(e, 0, sizeof(*e)); | ||
945 | |||
946 | if (le) { | ||
947 | le->next = e; | ||
948 | } else { | ||
949 | c->expr = e; | ||
950 | } | ||
951 | |||
952 | rc = next_entry(buf, fp, (sizeof(u32) * 3)); | ||
953 | if (rc < 0) | ||
954 | return rc; | ||
955 | e->expr_type = le32_to_cpu(buf[0]); | ||
956 | e->attr = le32_to_cpu(buf[1]); | ||
957 | e->op = le32_to_cpu(buf[2]); | ||
958 | |||
959 | switch (e->expr_type) { | ||
960 | case CEXPR_NOT: | ||
961 | if (depth < 0) | ||
962 | return -EINVAL; | ||
963 | break; | ||
964 | case CEXPR_AND: | ||
965 | case CEXPR_OR: | ||
966 | if (depth < 1) | ||
967 | return -EINVAL; | ||
968 | depth--; | ||
969 | break; | ||
970 | case CEXPR_ATTR: | ||
971 | if (depth == (CEXPR_MAXDEPTH - 1)) | ||
972 | return -EINVAL; | ||
973 | depth++; | ||
974 | break; | ||
975 | case CEXPR_NAMES: | ||
976 | if (!allowxtarget && (e->attr & CEXPR_XTARGET)) | ||
977 | return -EINVAL; | ||
978 | if (depth == (CEXPR_MAXDEPTH - 1)) | ||
979 | return -EINVAL; | ||
980 | depth++; | ||
981 | if (ebitmap_read(&e->names, fp)) | ||
982 | return -EINVAL; | ||
983 | break; | ||
984 | default: | ||
985 | return -EINVAL; | ||
986 | } | ||
987 | le = e; | ||
988 | } | ||
989 | if (depth != 0) | ||
990 | return -EINVAL; | ||
991 | lc = c; | ||
992 | } | ||
993 | |||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | static int class_read(struct policydb *p, struct hashtab *h, void *fp) | ||
998 | { | ||
999 | char *key = NULL; | ||
1000 | struct class_datum *cladatum; | ||
1001 | u32 buf[6], len, len2, ncons, nel; | ||
1002 | int i, rc; | ||
1003 | |||
1004 | cladatum = kmalloc(sizeof(*cladatum), GFP_KERNEL); | ||
1005 | if (!cladatum) { | ||
1006 | rc = -ENOMEM; | ||
1007 | goto out; | ||
1008 | } | ||
1009 | memset(cladatum, 0, sizeof(*cladatum)); | ||
1010 | |||
1011 | rc = next_entry(buf, fp, sizeof(u32)*6); | ||
1012 | if (rc < 0) | ||
1013 | goto bad; | ||
1014 | |||
1015 | len = le32_to_cpu(buf[0]); | ||
1016 | len2 = le32_to_cpu(buf[1]); | ||
1017 | cladatum->value = le32_to_cpu(buf[2]); | ||
1018 | |||
1019 | rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE); | ||
1020 | if (rc) | ||
1021 | goto bad; | ||
1022 | cladatum->permissions.nprim = le32_to_cpu(buf[3]); | ||
1023 | nel = le32_to_cpu(buf[4]); | ||
1024 | |||
1025 | ncons = le32_to_cpu(buf[5]); | ||
1026 | |||
1027 | key = kmalloc(len + 1,GFP_KERNEL); | ||
1028 | if (!key) { | ||
1029 | rc = -ENOMEM; | ||
1030 | goto bad; | ||
1031 | } | ||
1032 | rc = next_entry(key, fp, len); | ||
1033 | if (rc < 0) | ||
1034 | goto bad; | ||
1035 | key[len] = 0; | ||
1036 | |||
1037 | if (len2) { | ||
1038 | cladatum->comkey = kmalloc(len2 + 1,GFP_KERNEL); | ||
1039 | if (!cladatum->comkey) { | ||
1040 | rc = -ENOMEM; | ||
1041 | goto bad; | ||
1042 | } | ||
1043 | rc = next_entry(cladatum->comkey, fp, len2); | ||
1044 | if (rc < 0) | ||
1045 | goto bad; | ||
1046 | cladatum->comkey[len2] = 0; | ||
1047 | |||
1048 | cladatum->comdatum = hashtab_search(p->p_commons.table, | ||
1049 | cladatum->comkey); | ||
1050 | if (!cladatum->comdatum) { | ||
1051 | printk(KERN_ERR "security: unknown common %s\n", | ||
1052 | cladatum->comkey); | ||
1053 | rc = -EINVAL; | ||
1054 | goto bad; | ||
1055 | } | ||
1056 | } | ||
1057 | for (i = 0; i < nel; i++) { | ||
1058 | rc = perm_read(p, cladatum->permissions.table, fp); | ||
1059 | if (rc) | ||
1060 | goto bad; | ||
1061 | } | ||
1062 | |||
1063 | rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp); | ||
1064 | if (rc) | ||
1065 | goto bad; | ||
1066 | |||
1067 | if (p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) { | ||
1068 | /* grab the validatetrans rules */ | ||
1069 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1070 | if (rc < 0) | ||
1071 | goto bad; | ||
1072 | ncons = le32_to_cpu(buf[0]); | ||
1073 | rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp); | ||
1074 | if (rc) | ||
1075 | goto bad; | ||
1076 | } | ||
1077 | |||
1078 | rc = hashtab_insert(h, key, cladatum); | ||
1079 | if (rc) | ||
1080 | goto bad; | ||
1081 | |||
1082 | rc = 0; | ||
1083 | out: | ||
1084 | return rc; | ||
1085 | bad: | ||
1086 | class_destroy(key, cladatum, NULL); | ||
1087 | goto out; | ||
1088 | } | ||
1089 | |||
1090 | static int role_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1091 | { | ||
1092 | char *key = NULL; | ||
1093 | struct role_datum *role; | ||
1094 | int rc; | ||
1095 | u32 buf[2], len; | ||
1096 | |||
1097 | role = kmalloc(sizeof(*role), GFP_KERNEL); | ||
1098 | if (!role) { | ||
1099 | rc = -ENOMEM; | ||
1100 | goto out; | ||
1101 | } | ||
1102 | memset(role, 0, sizeof(*role)); | ||
1103 | |||
1104 | rc = next_entry(buf, fp, sizeof buf); | ||
1105 | if (rc < 0) | ||
1106 | goto bad; | ||
1107 | |||
1108 | len = le32_to_cpu(buf[0]); | ||
1109 | role->value = le32_to_cpu(buf[1]); | ||
1110 | |||
1111 | key = kmalloc(len + 1,GFP_KERNEL); | ||
1112 | if (!key) { | ||
1113 | rc = -ENOMEM; | ||
1114 | goto bad; | ||
1115 | } | ||
1116 | rc = next_entry(key, fp, len); | ||
1117 | if (rc < 0) | ||
1118 | goto bad; | ||
1119 | key[len] = 0; | ||
1120 | |||
1121 | rc = ebitmap_read(&role->dominates, fp); | ||
1122 | if (rc) | ||
1123 | goto bad; | ||
1124 | |||
1125 | rc = ebitmap_read(&role->types, fp); | ||
1126 | if (rc) | ||
1127 | goto bad; | ||
1128 | |||
1129 | if (strcmp(key, OBJECT_R) == 0) { | ||
1130 | if (role->value != OBJECT_R_VAL) { | ||
1131 | printk(KERN_ERR "Role %s has wrong value %d\n", | ||
1132 | OBJECT_R, role->value); | ||
1133 | rc = -EINVAL; | ||
1134 | goto bad; | ||
1135 | } | ||
1136 | rc = 0; | ||
1137 | goto bad; | ||
1138 | } | ||
1139 | |||
1140 | rc = hashtab_insert(h, key, role); | ||
1141 | if (rc) | ||
1142 | goto bad; | ||
1143 | out: | ||
1144 | return rc; | ||
1145 | bad: | ||
1146 | role_destroy(key, role, NULL); | ||
1147 | goto out; | ||
1148 | } | ||
1149 | |||
1150 | static int type_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1151 | { | ||
1152 | char *key = NULL; | ||
1153 | struct type_datum *typdatum; | ||
1154 | int rc; | ||
1155 | u32 buf[3], len; | ||
1156 | |||
1157 | typdatum = kmalloc(sizeof(*typdatum),GFP_KERNEL); | ||
1158 | if (!typdatum) { | ||
1159 | rc = -ENOMEM; | ||
1160 | return rc; | ||
1161 | } | ||
1162 | memset(typdatum, 0, sizeof(*typdatum)); | ||
1163 | |||
1164 | rc = next_entry(buf, fp, sizeof buf); | ||
1165 | if (rc < 0) | ||
1166 | goto bad; | ||
1167 | |||
1168 | len = le32_to_cpu(buf[0]); | ||
1169 | typdatum->value = le32_to_cpu(buf[1]); | ||
1170 | typdatum->primary = le32_to_cpu(buf[2]); | ||
1171 | |||
1172 | key = kmalloc(len + 1,GFP_KERNEL); | ||
1173 | if (!key) { | ||
1174 | rc = -ENOMEM; | ||
1175 | goto bad; | ||
1176 | } | ||
1177 | rc = next_entry(key, fp, len); | ||
1178 | if (rc < 0) | ||
1179 | goto bad; | ||
1180 | key[len] = 0; | ||
1181 | |||
1182 | rc = hashtab_insert(h, key, typdatum); | ||
1183 | if (rc) | ||
1184 | goto bad; | ||
1185 | out: | ||
1186 | return rc; | ||
1187 | bad: | ||
1188 | type_destroy(key, typdatum, NULL); | ||
1189 | goto out; | ||
1190 | } | ||
1191 | |||
1192 | |||
1193 | /* | ||
1194 | * Read a MLS level structure from a policydb binary | ||
1195 | * representation file. | ||
1196 | */ | ||
1197 | static int mls_read_level(struct mls_level *lp, void *fp) | ||
1198 | { | ||
1199 | u32 buf[1]; | ||
1200 | int rc; | ||
1201 | |||
1202 | memset(lp, 0, sizeof(*lp)); | ||
1203 | |||
1204 | rc = next_entry(buf, fp, sizeof buf); | ||
1205 | if (rc < 0) { | ||
1206 | printk(KERN_ERR "security: mls: truncated level\n"); | ||
1207 | goto bad; | ||
1208 | } | ||
1209 | lp->sens = le32_to_cpu(buf[0]); | ||
1210 | |||
1211 | if (ebitmap_read(&lp->cat, fp)) { | ||
1212 | printk(KERN_ERR "security: mls: error reading level " | ||
1213 | "categories\n"); | ||
1214 | goto bad; | ||
1215 | } | ||
1216 | return 0; | ||
1217 | |||
1218 | bad: | ||
1219 | return -EINVAL; | ||
1220 | } | ||
1221 | |||
1222 | static int user_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1223 | { | ||
1224 | char *key = NULL; | ||
1225 | struct user_datum *usrdatum; | ||
1226 | int rc; | ||
1227 | u32 buf[2], len; | ||
1228 | |||
1229 | usrdatum = kmalloc(sizeof(*usrdatum), GFP_KERNEL); | ||
1230 | if (!usrdatum) { | ||
1231 | rc = -ENOMEM; | ||
1232 | goto out; | ||
1233 | } | ||
1234 | memset(usrdatum, 0, sizeof(*usrdatum)); | ||
1235 | |||
1236 | rc = next_entry(buf, fp, sizeof buf); | ||
1237 | if (rc < 0) | ||
1238 | goto bad; | ||
1239 | |||
1240 | len = le32_to_cpu(buf[0]); | ||
1241 | usrdatum->value = le32_to_cpu(buf[1]); | ||
1242 | |||
1243 | key = kmalloc(len + 1,GFP_KERNEL); | ||
1244 | if (!key) { | ||
1245 | rc = -ENOMEM; | ||
1246 | goto bad; | ||
1247 | } | ||
1248 | rc = next_entry(key, fp, len); | ||
1249 | if (rc < 0) | ||
1250 | goto bad; | ||
1251 | key[len] = 0; | ||
1252 | |||
1253 | rc = ebitmap_read(&usrdatum->roles, fp); | ||
1254 | if (rc) | ||
1255 | goto bad; | ||
1256 | |||
1257 | if (p->policyvers >= POLICYDB_VERSION_MLS) { | ||
1258 | rc = mls_read_range_helper(&usrdatum->range, fp); | ||
1259 | if (rc) | ||
1260 | goto bad; | ||
1261 | rc = mls_read_level(&usrdatum->dfltlevel, fp); | ||
1262 | if (rc) | ||
1263 | goto bad; | ||
1264 | } | ||
1265 | |||
1266 | rc = hashtab_insert(h, key, usrdatum); | ||
1267 | if (rc) | ||
1268 | goto bad; | ||
1269 | out: | ||
1270 | return rc; | ||
1271 | bad: | ||
1272 | user_destroy(key, usrdatum, NULL); | ||
1273 | goto out; | ||
1274 | } | ||
1275 | |||
1276 | static int sens_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1277 | { | ||
1278 | char *key = NULL; | ||
1279 | struct level_datum *levdatum; | ||
1280 | int rc; | ||
1281 | u32 buf[2], len; | ||
1282 | |||
1283 | levdatum = kmalloc(sizeof(*levdatum), GFP_ATOMIC); | ||
1284 | if (!levdatum) { | ||
1285 | rc = -ENOMEM; | ||
1286 | goto out; | ||
1287 | } | ||
1288 | memset(levdatum, 0, sizeof(*levdatum)); | ||
1289 | |||
1290 | rc = next_entry(buf, fp, sizeof buf); | ||
1291 | if (rc < 0) | ||
1292 | goto bad; | ||
1293 | |||
1294 | len = le32_to_cpu(buf[0]); | ||
1295 | levdatum->isalias = le32_to_cpu(buf[1]); | ||
1296 | |||
1297 | key = kmalloc(len + 1,GFP_ATOMIC); | ||
1298 | if (!key) { | ||
1299 | rc = -ENOMEM; | ||
1300 | goto bad; | ||
1301 | } | ||
1302 | rc = next_entry(key, fp, len); | ||
1303 | if (rc < 0) | ||
1304 | goto bad; | ||
1305 | key[len] = 0; | ||
1306 | |||
1307 | levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC); | ||
1308 | if (!levdatum->level) { | ||
1309 | rc = -ENOMEM; | ||
1310 | goto bad; | ||
1311 | } | ||
1312 | if (mls_read_level(levdatum->level, fp)) { | ||
1313 | rc = -EINVAL; | ||
1314 | goto bad; | ||
1315 | } | ||
1316 | |||
1317 | rc = hashtab_insert(h, key, levdatum); | ||
1318 | if (rc) | ||
1319 | goto bad; | ||
1320 | out: | ||
1321 | return rc; | ||
1322 | bad: | ||
1323 | sens_destroy(key, levdatum, NULL); | ||
1324 | goto out; | ||
1325 | } | ||
1326 | |||
1327 | static int cat_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1328 | { | ||
1329 | char *key = NULL; | ||
1330 | struct cat_datum *catdatum; | ||
1331 | int rc; | ||
1332 | u32 buf[3], len; | ||
1333 | |||
1334 | catdatum = kmalloc(sizeof(*catdatum), GFP_ATOMIC); | ||
1335 | if (!catdatum) { | ||
1336 | rc = -ENOMEM; | ||
1337 | goto out; | ||
1338 | } | ||
1339 | memset(catdatum, 0, sizeof(*catdatum)); | ||
1340 | |||
1341 | rc = next_entry(buf, fp, sizeof buf); | ||
1342 | if (rc < 0) | ||
1343 | goto bad; | ||
1344 | |||
1345 | len = le32_to_cpu(buf[0]); | ||
1346 | catdatum->value = le32_to_cpu(buf[1]); | ||
1347 | catdatum->isalias = le32_to_cpu(buf[2]); | ||
1348 | |||
1349 | key = kmalloc(len + 1,GFP_ATOMIC); | ||
1350 | if (!key) { | ||
1351 | rc = -ENOMEM; | ||
1352 | goto bad; | ||
1353 | } | ||
1354 | rc = next_entry(key, fp, len); | ||
1355 | if (rc < 0) | ||
1356 | goto bad; | ||
1357 | key[len] = 0; | ||
1358 | |||
1359 | rc = hashtab_insert(h, key, catdatum); | ||
1360 | if (rc) | ||
1361 | goto bad; | ||
1362 | out: | ||
1363 | return rc; | ||
1364 | |||
1365 | bad: | ||
1366 | cat_destroy(key, catdatum, NULL); | ||
1367 | goto out; | ||
1368 | } | ||
1369 | |||
1370 | static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) = | ||
1371 | { | ||
1372 | common_read, | ||
1373 | class_read, | ||
1374 | role_read, | ||
1375 | type_read, | ||
1376 | user_read, | ||
1377 | cond_read_bool, | ||
1378 | sens_read, | ||
1379 | cat_read, | ||
1380 | }; | ||
1381 | |||
1382 | extern int ss_initialized; | ||
1383 | |||
1384 | /* | ||
1385 | * Read the configuration data from a policy database binary | ||
1386 | * representation file into a policy database structure. | ||
1387 | */ | ||
1388 | int policydb_read(struct policydb *p, void *fp) | ||
1389 | { | ||
1390 | struct role_allow *ra, *lra; | ||
1391 | struct role_trans *tr, *ltr; | ||
1392 | struct ocontext *l, *c, *newc; | ||
1393 | struct genfs *genfs_p, *genfs, *newgenfs; | ||
1394 | int i, j, rc; | ||
1395 | u32 buf[8], len, len2, config, nprim, nel, nel2; | ||
1396 | char *policydb_str; | ||
1397 | struct policydb_compat_info *info; | ||
1398 | struct range_trans *rt, *lrt; | ||
1399 | |||
1400 | config = 0; | ||
1401 | |||
1402 | rc = policydb_init(p); | ||
1403 | if (rc) | ||
1404 | goto out; | ||
1405 | |||
1406 | /* Read the magic number and string length. */ | ||
1407 | rc = next_entry(buf, fp, sizeof(u32)* 2); | ||
1408 | if (rc < 0) | ||
1409 | goto bad; | ||
1410 | |||
1411 | for (i = 0; i < 2; i++) | ||
1412 | buf[i] = le32_to_cpu(buf[i]); | ||
1413 | |||
1414 | if (buf[0] != POLICYDB_MAGIC) { | ||
1415 | printk(KERN_ERR "security: policydb magic number 0x%x does " | ||
1416 | "not match expected magic number 0x%x\n", | ||
1417 | buf[0], POLICYDB_MAGIC); | ||
1418 | goto bad; | ||
1419 | } | ||
1420 | |||
1421 | len = buf[1]; | ||
1422 | if (len != strlen(POLICYDB_STRING)) { | ||
1423 | printk(KERN_ERR "security: policydb string length %d does not " | ||
1424 | "match expected length %Zu\n", | ||
1425 | len, strlen(POLICYDB_STRING)); | ||
1426 | goto bad; | ||
1427 | } | ||
1428 | policydb_str = kmalloc(len + 1,GFP_KERNEL); | ||
1429 | if (!policydb_str) { | ||
1430 | printk(KERN_ERR "security: unable to allocate memory for policydb " | ||
1431 | "string of length %d\n", len); | ||
1432 | rc = -ENOMEM; | ||
1433 | goto bad; | ||
1434 | } | ||
1435 | rc = next_entry(policydb_str, fp, len); | ||
1436 | if (rc < 0) { | ||
1437 | printk(KERN_ERR "security: truncated policydb string identifier\n"); | ||
1438 | kfree(policydb_str); | ||
1439 | goto bad; | ||
1440 | } | ||
1441 | policydb_str[len] = 0; | ||
1442 | if (strcmp(policydb_str, POLICYDB_STRING)) { | ||
1443 | printk(KERN_ERR "security: policydb string %s does not match " | ||
1444 | "my string %s\n", policydb_str, POLICYDB_STRING); | ||
1445 | kfree(policydb_str); | ||
1446 | goto bad; | ||
1447 | } | ||
1448 | /* Done with policydb_str. */ | ||
1449 | kfree(policydb_str); | ||
1450 | policydb_str = NULL; | ||
1451 | |||
1452 | /* Read the version, config, and table sizes. */ | ||
1453 | rc = next_entry(buf, fp, sizeof(u32)*4); | ||
1454 | if (rc < 0) | ||
1455 | goto bad; | ||
1456 | for (i = 0; i < 4; i++) | ||
1457 | buf[i] = le32_to_cpu(buf[i]); | ||
1458 | |||
1459 | p->policyvers = buf[0]; | ||
1460 | if (p->policyvers < POLICYDB_VERSION_MIN || | ||
1461 | p->policyvers > POLICYDB_VERSION_MAX) { | ||
1462 | printk(KERN_ERR "security: policydb version %d does not match " | ||
1463 | "my version range %d-%d\n", | ||
1464 | buf[0], POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); | ||
1465 | goto bad; | ||
1466 | } | ||
1467 | |||
1468 | if ((buf[1] & POLICYDB_CONFIG_MLS)) { | ||
1469 | if (ss_initialized && !selinux_mls_enabled) { | ||
1470 | printk(KERN_ERR "Cannot switch between non-MLS and MLS " | ||
1471 | "policies\n"); | ||
1472 | goto bad; | ||
1473 | } | ||
1474 | selinux_mls_enabled = 1; | ||
1475 | config |= POLICYDB_CONFIG_MLS; | ||
1476 | |||
1477 | if (p->policyvers < POLICYDB_VERSION_MLS) { | ||
1478 | printk(KERN_ERR "security policydb version %d (MLS) " | ||
1479 | "not backwards compatible\n", p->policyvers); | ||
1480 | goto bad; | ||
1481 | } | ||
1482 | } else { | ||
1483 | if (ss_initialized && selinux_mls_enabled) { | ||
1484 | printk(KERN_ERR "Cannot switch between MLS and non-MLS " | ||
1485 | "policies\n"); | ||
1486 | goto bad; | ||
1487 | } | ||
1488 | } | ||
1489 | |||
1490 | info = policydb_lookup_compat(p->policyvers); | ||
1491 | if (!info) { | ||
1492 | printk(KERN_ERR "security: unable to find policy compat info " | ||
1493 | "for version %d\n", p->policyvers); | ||
1494 | goto bad; | ||
1495 | } | ||
1496 | |||
1497 | if (buf[2] != info->sym_num || buf[3] != info->ocon_num) { | ||
1498 | printk(KERN_ERR "security: policydb table sizes (%d,%d) do " | ||
1499 | "not match mine (%d,%d)\n", buf[2], buf[3], | ||
1500 | info->sym_num, info->ocon_num); | ||
1501 | goto bad; | ||
1502 | } | ||
1503 | |||
1504 | for (i = 0; i < info->sym_num; i++) { | ||
1505 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
1506 | if (rc < 0) | ||
1507 | goto bad; | ||
1508 | nprim = le32_to_cpu(buf[0]); | ||
1509 | nel = le32_to_cpu(buf[1]); | ||
1510 | for (j = 0; j < nel; j++) { | ||
1511 | rc = read_f[i](p, p->symtab[i].table, fp); | ||
1512 | if (rc) | ||
1513 | goto bad; | ||
1514 | } | ||
1515 | |||
1516 | p->symtab[i].nprim = nprim; | ||
1517 | } | ||
1518 | |||
1519 | rc = avtab_read(&p->te_avtab, fp, config); | ||
1520 | if (rc) | ||
1521 | goto bad; | ||
1522 | |||
1523 | if (p->policyvers >= POLICYDB_VERSION_BOOL) { | ||
1524 | rc = cond_read_list(p, fp); | ||
1525 | if (rc) | ||
1526 | goto bad; | ||
1527 | } | ||
1528 | |||
1529 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1530 | if (rc < 0) | ||
1531 | goto bad; | ||
1532 | nel = le32_to_cpu(buf[0]); | ||
1533 | ltr = NULL; | ||
1534 | for (i = 0; i < nel; i++) { | ||
1535 | tr = kmalloc(sizeof(*tr), GFP_KERNEL); | ||
1536 | if (!tr) { | ||
1537 | rc = -ENOMEM; | ||
1538 | goto bad; | ||
1539 | } | ||
1540 | memset(tr, 0, sizeof(*tr)); | ||
1541 | if (ltr) { | ||
1542 | ltr->next = tr; | ||
1543 | } else { | ||
1544 | p->role_tr = tr; | ||
1545 | } | ||
1546 | rc = next_entry(buf, fp, sizeof(u32)*3); | ||
1547 | if (rc < 0) | ||
1548 | goto bad; | ||
1549 | tr->role = le32_to_cpu(buf[0]); | ||
1550 | tr->type = le32_to_cpu(buf[1]); | ||
1551 | tr->new_role = le32_to_cpu(buf[2]); | ||
1552 | ltr = tr; | ||
1553 | } | ||
1554 | |||
1555 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1556 | if (rc < 0) | ||
1557 | goto bad; | ||
1558 | nel = le32_to_cpu(buf[0]); | ||
1559 | lra = NULL; | ||
1560 | for (i = 0; i < nel; i++) { | ||
1561 | ra = kmalloc(sizeof(*ra), GFP_KERNEL); | ||
1562 | if (!ra) { | ||
1563 | rc = -ENOMEM; | ||
1564 | goto bad; | ||
1565 | } | ||
1566 | memset(ra, 0, sizeof(*ra)); | ||
1567 | if (lra) { | ||
1568 | lra->next = ra; | ||
1569 | } else { | ||
1570 | p->role_allow = ra; | ||
1571 | } | ||
1572 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
1573 | if (rc < 0) | ||
1574 | goto bad; | ||
1575 | ra->role = le32_to_cpu(buf[0]); | ||
1576 | ra->new_role = le32_to_cpu(buf[1]); | ||
1577 | lra = ra; | ||
1578 | } | ||
1579 | |||
1580 | rc = policydb_index_classes(p); | ||
1581 | if (rc) | ||
1582 | goto bad; | ||
1583 | |||
1584 | rc = policydb_index_others(p); | ||
1585 | if (rc) | ||
1586 | goto bad; | ||
1587 | |||
1588 | for (i = 0; i < info->ocon_num; i++) { | ||
1589 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1590 | if (rc < 0) | ||
1591 | goto bad; | ||
1592 | nel = le32_to_cpu(buf[0]); | ||
1593 | l = NULL; | ||
1594 | for (j = 0; j < nel; j++) { | ||
1595 | c = kmalloc(sizeof(*c), GFP_KERNEL); | ||
1596 | if (!c) { | ||
1597 | rc = -ENOMEM; | ||
1598 | goto bad; | ||
1599 | } | ||
1600 | memset(c, 0, sizeof(*c)); | ||
1601 | if (l) { | ||
1602 | l->next = c; | ||
1603 | } else { | ||
1604 | p->ocontexts[i] = c; | ||
1605 | } | ||
1606 | l = c; | ||
1607 | rc = -EINVAL; | ||
1608 | switch (i) { | ||
1609 | case OCON_ISID: | ||
1610 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1611 | if (rc < 0) | ||
1612 | goto bad; | ||
1613 | c->sid[0] = le32_to_cpu(buf[0]); | ||
1614 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1615 | if (rc) | ||
1616 | goto bad; | ||
1617 | break; | ||
1618 | case OCON_FS: | ||
1619 | case OCON_NETIF: | ||
1620 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1621 | if (rc < 0) | ||
1622 | goto bad; | ||
1623 | len = le32_to_cpu(buf[0]); | ||
1624 | c->u.name = kmalloc(len + 1,GFP_KERNEL); | ||
1625 | if (!c->u.name) { | ||
1626 | rc = -ENOMEM; | ||
1627 | goto bad; | ||
1628 | } | ||
1629 | rc = next_entry(c->u.name, fp, len); | ||
1630 | if (rc < 0) | ||
1631 | goto bad; | ||
1632 | c->u.name[len] = 0; | ||
1633 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1634 | if (rc) | ||
1635 | goto bad; | ||
1636 | rc = context_read_and_validate(&c->context[1], p, fp); | ||
1637 | if (rc) | ||
1638 | goto bad; | ||
1639 | break; | ||
1640 | case OCON_PORT: | ||
1641 | rc = next_entry(buf, fp, sizeof(u32)*3); | ||
1642 | if (rc < 0) | ||
1643 | goto bad; | ||
1644 | c->u.port.protocol = le32_to_cpu(buf[0]); | ||
1645 | c->u.port.low_port = le32_to_cpu(buf[1]); | ||
1646 | c->u.port.high_port = le32_to_cpu(buf[2]); | ||
1647 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1648 | if (rc) | ||
1649 | goto bad; | ||
1650 | break; | ||
1651 | case OCON_NODE: | ||
1652 | rc = next_entry(buf, fp, sizeof(u32)* 2); | ||
1653 | if (rc < 0) | ||
1654 | goto bad; | ||
1655 | c->u.node.addr = le32_to_cpu(buf[0]); | ||
1656 | c->u.node.mask = le32_to_cpu(buf[1]); | ||
1657 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1658 | if (rc) | ||
1659 | goto bad; | ||
1660 | break; | ||
1661 | case OCON_FSUSE: | ||
1662 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
1663 | if (rc < 0) | ||
1664 | goto bad; | ||
1665 | c->v.behavior = le32_to_cpu(buf[0]); | ||
1666 | if (c->v.behavior > SECURITY_FS_USE_NONE) | ||
1667 | goto bad; | ||
1668 | len = le32_to_cpu(buf[1]); | ||
1669 | c->u.name = kmalloc(len + 1,GFP_KERNEL); | ||
1670 | if (!c->u.name) { | ||
1671 | rc = -ENOMEM; | ||
1672 | goto bad; | ||
1673 | } | ||
1674 | rc = next_entry(c->u.name, fp, len); | ||
1675 | if (rc < 0) | ||
1676 | goto bad; | ||
1677 | c->u.name[len] = 0; | ||
1678 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1679 | if (rc) | ||
1680 | goto bad; | ||
1681 | break; | ||
1682 | case OCON_NODE6: { | ||
1683 | int k; | ||
1684 | |||
1685 | rc = next_entry(buf, fp, sizeof(u32) * 8); | ||
1686 | if (rc < 0) | ||
1687 | goto bad; | ||
1688 | for (k = 0; k < 4; k++) | ||
1689 | c->u.node6.addr[k] = le32_to_cpu(buf[k]); | ||
1690 | for (k = 0; k < 4; k++) | ||
1691 | c->u.node6.mask[k] = le32_to_cpu(buf[k+4]); | ||
1692 | if (context_read_and_validate(&c->context[0], p, fp)) | ||
1693 | goto bad; | ||
1694 | break; | ||
1695 | } | ||
1696 | } | ||
1697 | } | ||
1698 | } | ||
1699 | |||
1700 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1701 | if (rc < 0) | ||
1702 | goto bad; | ||
1703 | nel = le32_to_cpu(buf[0]); | ||
1704 | genfs_p = NULL; | ||
1705 | rc = -EINVAL; | ||
1706 | for (i = 0; i < nel; i++) { | ||
1707 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1708 | if (rc < 0) | ||
1709 | goto bad; | ||
1710 | len = le32_to_cpu(buf[0]); | ||
1711 | newgenfs = kmalloc(sizeof(*newgenfs), GFP_KERNEL); | ||
1712 | if (!newgenfs) { | ||
1713 | rc = -ENOMEM; | ||
1714 | goto bad; | ||
1715 | } | ||
1716 | memset(newgenfs, 0, sizeof(*newgenfs)); | ||
1717 | |||
1718 | newgenfs->fstype = kmalloc(len + 1,GFP_KERNEL); | ||
1719 | if (!newgenfs->fstype) { | ||
1720 | rc = -ENOMEM; | ||
1721 | kfree(newgenfs); | ||
1722 | goto bad; | ||
1723 | } | ||
1724 | rc = next_entry(newgenfs->fstype, fp, len); | ||
1725 | if (rc < 0) { | ||
1726 | kfree(newgenfs->fstype); | ||
1727 | kfree(newgenfs); | ||
1728 | goto bad; | ||
1729 | } | ||
1730 | newgenfs->fstype[len] = 0; | ||
1731 | for (genfs_p = NULL, genfs = p->genfs; genfs; | ||
1732 | genfs_p = genfs, genfs = genfs->next) { | ||
1733 | if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { | ||
1734 | printk(KERN_ERR "security: dup genfs " | ||
1735 | "fstype %s\n", newgenfs->fstype); | ||
1736 | kfree(newgenfs->fstype); | ||
1737 | kfree(newgenfs); | ||
1738 | goto bad; | ||
1739 | } | ||
1740 | if (strcmp(newgenfs->fstype, genfs->fstype) < 0) | ||
1741 | break; | ||
1742 | } | ||
1743 | newgenfs->next = genfs; | ||
1744 | if (genfs_p) | ||
1745 | genfs_p->next = newgenfs; | ||
1746 | else | ||
1747 | p->genfs = newgenfs; | ||
1748 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1749 | if (rc < 0) | ||
1750 | goto bad; | ||
1751 | nel2 = le32_to_cpu(buf[0]); | ||
1752 | for (j = 0; j < nel2; j++) { | ||
1753 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1754 | if (rc < 0) | ||
1755 | goto bad; | ||
1756 | len = le32_to_cpu(buf[0]); | ||
1757 | |||
1758 | newc = kmalloc(sizeof(*newc), GFP_KERNEL); | ||
1759 | if (!newc) { | ||
1760 | rc = -ENOMEM; | ||
1761 | goto bad; | ||
1762 | } | ||
1763 | memset(newc, 0, sizeof(*newc)); | ||
1764 | |||
1765 | newc->u.name = kmalloc(len + 1,GFP_KERNEL); | ||
1766 | if (!newc->u.name) { | ||
1767 | rc = -ENOMEM; | ||
1768 | goto bad_newc; | ||
1769 | } | ||
1770 | rc = next_entry(newc->u.name, fp, len); | ||
1771 | if (rc < 0) | ||
1772 | goto bad_newc; | ||
1773 | newc->u.name[len] = 0; | ||
1774 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1775 | if (rc < 0) | ||
1776 | goto bad_newc; | ||
1777 | newc->v.sclass = le32_to_cpu(buf[0]); | ||
1778 | if (context_read_and_validate(&newc->context[0], p, fp)) | ||
1779 | goto bad_newc; | ||
1780 | for (l = NULL, c = newgenfs->head; c; | ||
1781 | l = c, c = c->next) { | ||
1782 | if (!strcmp(newc->u.name, c->u.name) && | ||
1783 | (!c->v.sclass || !newc->v.sclass || | ||
1784 | newc->v.sclass == c->v.sclass)) { | ||
1785 | printk(KERN_ERR "security: dup genfs " | ||
1786 | "entry (%s,%s)\n", | ||
1787 | newgenfs->fstype, c->u.name); | ||
1788 | goto bad_newc; | ||
1789 | } | ||
1790 | len = strlen(newc->u.name); | ||
1791 | len2 = strlen(c->u.name); | ||
1792 | if (len > len2) | ||
1793 | break; | ||
1794 | } | ||
1795 | |||
1796 | newc->next = c; | ||
1797 | if (l) | ||
1798 | l->next = newc; | ||
1799 | else | ||
1800 | newgenfs->head = newc; | ||
1801 | } | ||
1802 | } | ||
1803 | |||
1804 | if (p->policyvers >= POLICYDB_VERSION_MLS) { | ||
1805 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1806 | if (rc < 0) | ||
1807 | goto bad; | ||
1808 | nel = le32_to_cpu(buf[0]); | ||
1809 | lrt = NULL; | ||
1810 | for (i = 0; i < nel; i++) { | ||
1811 | rt = kmalloc(sizeof(*rt), GFP_KERNEL); | ||
1812 | if (!rt) { | ||
1813 | rc = -ENOMEM; | ||
1814 | goto bad; | ||
1815 | } | ||
1816 | memset(rt, 0, sizeof(*rt)); | ||
1817 | if (lrt) | ||
1818 | lrt->next = rt; | ||
1819 | else | ||
1820 | p->range_tr = rt; | ||
1821 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); | ||
1822 | if (rc < 0) | ||
1823 | goto bad; | ||
1824 | rt->dom = le32_to_cpu(buf[0]); | ||
1825 | rt->type = le32_to_cpu(buf[1]); | ||
1826 | rc = mls_read_range_helper(&rt->range, fp); | ||
1827 | if (rc) | ||
1828 | goto bad; | ||
1829 | lrt = rt; | ||
1830 | } | ||
1831 | } | ||
1832 | |||
1833 | rc = 0; | ||
1834 | out: | ||
1835 | return rc; | ||
1836 | bad_newc: | ||
1837 | ocontext_destroy(newc,OCON_FSUSE); | ||
1838 | bad: | ||
1839 | if (!rc) | ||
1840 | rc = -EINVAL; | ||
1841 | policydb_destroy(p); | ||
1842 | goto out; | ||
1843 | } | ||
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h new file mode 100644 index 000000000000..2470e2a1a1c3 --- /dev/null +++ b/security/selinux/ss/policydb.h | |||
@@ -0,0 +1,275 @@ | |||
1 | /* | ||
2 | * A policy database (policydb) specifies the | ||
3 | * configuration data for the security policy. | ||
4 | * | ||
5 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
10 | * | ||
11 | * Support for enhanced MLS infrastructure. | ||
12 | * | ||
13 | * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
14 | * | ||
15 | * Added conditional policy language extensions | ||
16 | * | ||
17 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
18 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
19 | * This program is free software; you can redistribute it and/or modify | ||
20 | * it under the terms of the GNU General Public License as published by | ||
21 | * the Free Software Foundation, version 2. | ||
22 | */ | ||
23 | |||
24 | #ifndef _SS_POLICYDB_H_ | ||
25 | #define _SS_POLICYDB_H_ | ||
26 | |||
27 | #include "symtab.h" | ||
28 | #include "avtab.h" | ||
29 | #include "sidtab.h" | ||
30 | #include "context.h" | ||
31 | #include "constraint.h" | ||
32 | |||
33 | /* | ||
34 | * A datum type is defined for each kind of symbol | ||
35 | * in the configuration data: individual permissions, | ||
36 | * common prefixes for access vectors, classes, | ||
37 | * users, roles, types, sensitivities, categories, etc. | ||
38 | */ | ||
39 | |||
40 | /* Permission attributes */ | ||
41 | struct perm_datum { | ||
42 | u32 value; /* permission bit + 1 */ | ||
43 | }; | ||
44 | |||
45 | /* Attributes of a common prefix for access vectors */ | ||
46 | struct common_datum { | ||
47 | u32 value; /* internal common value */ | ||
48 | struct symtab permissions; /* common permissions */ | ||
49 | }; | ||
50 | |||
51 | /* Class attributes */ | ||
52 | struct class_datum { | ||
53 | u32 value; /* class value */ | ||
54 | char *comkey; /* common name */ | ||
55 | struct common_datum *comdatum; /* common datum */ | ||
56 | struct symtab permissions; /* class-specific permission symbol table */ | ||
57 | struct constraint_node *constraints; /* constraints on class permissions */ | ||
58 | struct constraint_node *validatetrans; /* special transition rules */ | ||
59 | }; | ||
60 | |||
61 | /* Role attributes */ | ||
62 | struct role_datum { | ||
63 | u32 value; /* internal role value */ | ||
64 | struct ebitmap dominates; /* set of roles dominated by this role */ | ||
65 | struct ebitmap types; /* set of authorized types for role */ | ||
66 | }; | ||
67 | |||
68 | struct role_trans { | ||
69 | u32 role; /* current role */ | ||
70 | u32 type; /* program executable type */ | ||
71 | u32 new_role; /* new role */ | ||
72 | struct role_trans *next; | ||
73 | }; | ||
74 | |||
75 | struct role_allow { | ||
76 | u32 role; /* current role */ | ||
77 | u32 new_role; /* new role */ | ||
78 | struct role_allow *next; | ||
79 | }; | ||
80 | |||
81 | /* Type attributes */ | ||
82 | struct type_datum { | ||
83 | u32 value; /* internal type value */ | ||
84 | unsigned char primary; /* primary name? */ | ||
85 | }; | ||
86 | |||
87 | /* User attributes */ | ||
88 | struct user_datum { | ||
89 | u32 value; /* internal user value */ | ||
90 | struct ebitmap roles; /* set of authorized roles for user */ | ||
91 | struct mls_range range; /* MLS range (min - max) for user */ | ||
92 | struct mls_level dfltlevel; /* default login MLS level for user */ | ||
93 | }; | ||
94 | |||
95 | |||
96 | /* Sensitivity attributes */ | ||
97 | struct level_datum { | ||
98 | struct mls_level *level; /* sensitivity and associated categories */ | ||
99 | unsigned char isalias; /* is this sensitivity an alias for another? */ | ||
100 | }; | ||
101 | |||
102 | /* Category attributes */ | ||
103 | struct cat_datum { | ||
104 | u32 value; /* internal category bit + 1 */ | ||
105 | unsigned char isalias; /* is this category an alias for another? */ | ||
106 | }; | ||
107 | |||
108 | struct range_trans { | ||
109 | u32 dom; /* current process domain */ | ||
110 | u32 type; /* program executable type */ | ||
111 | struct mls_range range; /* new range */ | ||
112 | struct range_trans *next; | ||
113 | }; | ||
114 | |||
115 | /* Boolean data type */ | ||
116 | struct cond_bool_datum { | ||
117 | __u32 value; /* internal type value */ | ||
118 | int state; | ||
119 | }; | ||
120 | |||
121 | struct cond_node; | ||
122 | |||
123 | /* | ||
124 | * The configuration data includes security contexts for | ||
125 | * initial SIDs, unlabeled file systems, TCP and UDP port numbers, | ||
126 | * network interfaces, and nodes. This structure stores the | ||
127 | * relevant data for one such entry. Entries of the same kind | ||
128 | * (e.g. all initial SIDs) are linked together into a list. | ||
129 | */ | ||
130 | struct ocontext { | ||
131 | union { | ||
132 | char *name; /* name of initial SID, fs, netif, fstype, path */ | ||
133 | struct { | ||
134 | u8 protocol; | ||
135 | u16 low_port; | ||
136 | u16 high_port; | ||
137 | } port; /* TCP or UDP port information */ | ||
138 | struct { | ||
139 | u32 addr; | ||
140 | u32 mask; | ||
141 | } node; /* node information */ | ||
142 | struct { | ||
143 | u32 addr[4]; | ||
144 | u32 mask[4]; | ||
145 | } node6; /* IPv6 node information */ | ||
146 | } u; | ||
147 | union { | ||
148 | u32 sclass; /* security class for genfs */ | ||
149 | u32 behavior; /* labeling behavior for fs_use */ | ||
150 | } v; | ||
151 | struct context context[2]; /* security context(s) */ | ||
152 | u32 sid[2]; /* SID(s) */ | ||
153 | struct ocontext *next; | ||
154 | }; | ||
155 | |||
156 | struct genfs { | ||
157 | char *fstype; | ||
158 | struct ocontext *head; | ||
159 | struct genfs *next; | ||
160 | }; | ||
161 | |||
162 | /* symbol table array indices */ | ||
163 | #define SYM_COMMONS 0 | ||
164 | #define SYM_CLASSES 1 | ||
165 | #define SYM_ROLES 2 | ||
166 | #define SYM_TYPES 3 | ||
167 | #define SYM_USERS 4 | ||
168 | #define SYM_BOOLS 5 | ||
169 | #define SYM_LEVELS 6 | ||
170 | #define SYM_CATS 7 | ||
171 | #define SYM_NUM 8 | ||
172 | |||
173 | /* object context array indices */ | ||
174 | #define OCON_ISID 0 /* initial SIDs */ | ||
175 | #define OCON_FS 1 /* unlabeled file systems */ | ||
176 | #define OCON_PORT 2 /* TCP and UDP port numbers */ | ||
177 | #define OCON_NETIF 3 /* network interfaces */ | ||
178 | #define OCON_NODE 4 /* nodes */ | ||
179 | #define OCON_FSUSE 5 /* fs_use */ | ||
180 | #define OCON_NODE6 6 /* IPv6 nodes */ | ||
181 | #define OCON_NUM 7 | ||
182 | |||
183 | /* The policy database */ | ||
184 | struct policydb { | ||
185 | /* symbol tables */ | ||
186 | struct symtab symtab[SYM_NUM]; | ||
187 | #define p_commons symtab[SYM_COMMONS] | ||
188 | #define p_classes symtab[SYM_CLASSES] | ||
189 | #define p_roles symtab[SYM_ROLES] | ||
190 | #define p_types symtab[SYM_TYPES] | ||
191 | #define p_users symtab[SYM_USERS] | ||
192 | #define p_bools symtab[SYM_BOOLS] | ||
193 | #define p_levels symtab[SYM_LEVELS] | ||
194 | #define p_cats symtab[SYM_CATS] | ||
195 | |||
196 | /* symbol names indexed by (value - 1) */ | ||
197 | char **sym_val_to_name[SYM_NUM]; | ||
198 | #define p_common_val_to_name sym_val_to_name[SYM_COMMONS] | ||
199 | #define p_class_val_to_name sym_val_to_name[SYM_CLASSES] | ||
200 | #define p_role_val_to_name sym_val_to_name[SYM_ROLES] | ||
201 | #define p_type_val_to_name sym_val_to_name[SYM_TYPES] | ||
202 | #define p_user_val_to_name sym_val_to_name[SYM_USERS] | ||
203 | #define p_bool_val_to_name sym_val_to_name[SYM_BOOLS] | ||
204 | #define p_sens_val_to_name sym_val_to_name[SYM_LEVELS] | ||
205 | #define p_cat_val_to_name sym_val_to_name[SYM_CATS] | ||
206 | |||
207 | /* class, role, and user attributes indexed by (value - 1) */ | ||
208 | struct class_datum **class_val_to_struct; | ||
209 | struct role_datum **role_val_to_struct; | ||
210 | struct user_datum **user_val_to_struct; | ||
211 | |||
212 | /* type enforcement access vectors and transitions */ | ||
213 | struct avtab te_avtab; | ||
214 | |||
215 | /* role transitions */ | ||
216 | struct role_trans *role_tr; | ||
217 | |||
218 | /* bools indexed by (value - 1) */ | ||
219 | struct cond_bool_datum **bool_val_to_struct; | ||
220 | /* type enforcement conditional access vectors and transitions */ | ||
221 | struct avtab te_cond_avtab; | ||
222 | /* linked list indexing te_cond_avtab by conditional */ | ||
223 | struct cond_node* cond_list; | ||
224 | |||
225 | /* role allows */ | ||
226 | struct role_allow *role_allow; | ||
227 | |||
228 | /* security contexts of initial SIDs, unlabeled file systems, | ||
229 | TCP or UDP port numbers, network interfaces and nodes */ | ||
230 | struct ocontext *ocontexts[OCON_NUM]; | ||
231 | |||
232 | /* security contexts for files in filesystems that cannot support | ||
233 | a persistent label mapping or use another | ||
234 | fixed labeling behavior. */ | ||
235 | struct genfs *genfs; | ||
236 | |||
237 | /* range transitions */ | ||
238 | struct range_trans *range_tr; | ||
239 | |||
240 | unsigned int policyvers; | ||
241 | }; | ||
242 | |||
243 | extern void policydb_destroy(struct policydb *p); | ||
244 | extern int policydb_load_isids(struct policydb *p, struct sidtab *s); | ||
245 | extern int policydb_context_isvalid(struct policydb *p, struct context *c); | ||
246 | extern int policydb_read(struct policydb *p, void *fp); | ||
247 | |||
248 | #define PERM_SYMTAB_SIZE 32 | ||
249 | |||
250 | #define POLICYDB_CONFIG_MLS 1 | ||
251 | |||
252 | #define OBJECT_R "object_r" | ||
253 | #define OBJECT_R_VAL 1 | ||
254 | |||
255 | #define POLICYDB_MAGIC SELINUX_MAGIC | ||
256 | #define POLICYDB_STRING "SE Linux" | ||
257 | |||
258 | struct policy_file { | ||
259 | char *data; | ||
260 | size_t len; | ||
261 | }; | ||
262 | |||
263 | static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes) | ||
264 | { | ||
265 | if (bytes > fp->len) | ||
266 | return -EINVAL; | ||
267 | |||
268 | memcpy(buf, fp->data, bytes); | ||
269 | fp->data += bytes; | ||
270 | fp->len -= bytes; | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | #endif /* _SS_POLICYDB_H_ */ | ||
275 | |||
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c new file mode 100644 index 000000000000..5a820cf88c9c --- /dev/null +++ b/security/selinux/ss/services.c | |||
@@ -0,0 +1,1777 @@ | |||
1 | /* | ||
2 | * Implementation of the security services. | ||
3 | * | ||
4 | * Authors : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | * James Morris <jmorris@redhat.com> | ||
6 | * | ||
7 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
8 | * | ||
9 | * Support for enhanced MLS infrastructure. | ||
10 | * | ||
11 | * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
12 | * | ||
13 | * Added conditional policy language extensions | ||
14 | * | ||
15 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
16 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
17 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
18 | * This program is free software; you can redistribute it and/or modify | ||
19 | * it under the terms of the GNU General Public License as published by | ||
20 | * the Free Software Foundation, version 2. | ||
21 | */ | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/in.h> | ||
28 | #include <linux/sched.h> | ||
29 | #include <linux/audit.h> | ||
30 | #include <asm/semaphore.h> | ||
31 | #include "flask.h" | ||
32 | #include "avc.h" | ||
33 | #include "avc_ss.h" | ||
34 | #include "security.h" | ||
35 | #include "context.h" | ||
36 | #include "policydb.h" | ||
37 | #include "sidtab.h" | ||
38 | #include "services.h" | ||
39 | #include "conditional.h" | ||
40 | #include "mls.h" | ||
41 | |||
42 | extern void selnl_notify_policyload(u32 seqno); | ||
43 | unsigned int policydb_loaded_version; | ||
44 | |||
45 | static DEFINE_RWLOCK(policy_rwlock); | ||
46 | #define POLICY_RDLOCK read_lock(&policy_rwlock) | ||
47 | #define POLICY_WRLOCK write_lock_irq(&policy_rwlock) | ||
48 | #define POLICY_RDUNLOCK read_unlock(&policy_rwlock) | ||
49 | #define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock) | ||
50 | |||
51 | static DECLARE_MUTEX(load_sem); | ||
52 | #define LOAD_LOCK down(&load_sem) | ||
53 | #define LOAD_UNLOCK up(&load_sem) | ||
54 | |||
55 | static struct sidtab sidtab; | ||
56 | struct policydb policydb; | ||
57 | int ss_initialized = 0; | ||
58 | |||
59 | /* | ||
60 | * The largest sequence number that has been used when | ||
61 | * providing an access decision to the access vector cache. | ||
62 | * The sequence number only changes when a policy change | ||
63 | * occurs. | ||
64 | */ | ||
65 | static u32 latest_granting = 0; | ||
66 | |||
67 | /* Forward declaration. */ | ||
68 | static int context_struct_to_string(struct context *context, char **scontext, | ||
69 | u32 *scontext_len); | ||
70 | |||
71 | /* | ||
72 | * Return the boolean value of a constraint expression | ||
73 | * when it is applied to the specified source and target | ||
74 | * security contexts. | ||
75 | * | ||
76 | * xcontext is a special beast... It is used by the validatetrans rules | ||
77 | * only. For these rules, scontext is the context before the transition, | ||
78 | * tcontext is the context after the transition, and xcontext is the context | ||
79 | * of the process performing the transition. All other callers of | ||
80 | * constraint_expr_eval should pass in NULL for xcontext. | ||
81 | */ | ||
82 | static int constraint_expr_eval(struct context *scontext, | ||
83 | struct context *tcontext, | ||
84 | struct context *xcontext, | ||
85 | struct constraint_expr *cexpr) | ||
86 | { | ||
87 | u32 val1, val2; | ||
88 | struct context *c; | ||
89 | struct role_datum *r1, *r2; | ||
90 | struct mls_level *l1, *l2; | ||
91 | struct constraint_expr *e; | ||
92 | int s[CEXPR_MAXDEPTH]; | ||
93 | int sp = -1; | ||
94 | |||
95 | for (e = cexpr; e; e = e->next) { | ||
96 | switch (e->expr_type) { | ||
97 | case CEXPR_NOT: | ||
98 | BUG_ON(sp < 0); | ||
99 | s[sp] = !s[sp]; | ||
100 | break; | ||
101 | case CEXPR_AND: | ||
102 | BUG_ON(sp < 1); | ||
103 | sp--; | ||
104 | s[sp] &= s[sp+1]; | ||
105 | break; | ||
106 | case CEXPR_OR: | ||
107 | BUG_ON(sp < 1); | ||
108 | sp--; | ||
109 | s[sp] |= s[sp+1]; | ||
110 | break; | ||
111 | case CEXPR_ATTR: | ||
112 | if (sp == (CEXPR_MAXDEPTH-1)) | ||
113 | return 0; | ||
114 | switch (e->attr) { | ||
115 | case CEXPR_USER: | ||
116 | val1 = scontext->user; | ||
117 | val2 = tcontext->user; | ||
118 | break; | ||
119 | case CEXPR_TYPE: | ||
120 | val1 = scontext->type; | ||
121 | val2 = tcontext->type; | ||
122 | break; | ||
123 | case CEXPR_ROLE: | ||
124 | val1 = scontext->role; | ||
125 | val2 = tcontext->role; | ||
126 | r1 = policydb.role_val_to_struct[val1 - 1]; | ||
127 | r2 = policydb.role_val_to_struct[val2 - 1]; | ||
128 | switch (e->op) { | ||
129 | case CEXPR_DOM: | ||
130 | s[++sp] = ebitmap_get_bit(&r1->dominates, | ||
131 | val2 - 1); | ||
132 | continue; | ||
133 | case CEXPR_DOMBY: | ||
134 | s[++sp] = ebitmap_get_bit(&r2->dominates, | ||
135 | val1 - 1); | ||
136 | continue; | ||
137 | case CEXPR_INCOMP: | ||
138 | s[++sp] = ( !ebitmap_get_bit(&r1->dominates, | ||
139 | val2 - 1) && | ||
140 | !ebitmap_get_bit(&r2->dominates, | ||
141 | val1 - 1) ); | ||
142 | continue; | ||
143 | default: | ||
144 | break; | ||
145 | } | ||
146 | break; | ||
147 | case CEXPR_L1L2: | ||
148 | l1 = &(scontext->range.level[0]); | ||
149 | l2 = &(tcontext->range.level[0]); | ||
150 | goto mls_ops; | ||
151 | case CEXPR_L1H2: | ||
152 | l1 = &(scontext->range.level[0]); | ||
153 | l2 = &(tcontext->range.level[1]); | ||
154 | goto mls_ops; | ||
155 | case CEXPR_H1L2: | ||
156 | l1 = &(scontext->range.level[1]); | ||
157 | l2 = &(tcontext->range.level[0]); | ||
158 | goto mls_ops; | ||
159 | case CEXPR_H1H2: | ||
160 | l1 = &(scontext->range.level[1]); | ||
161 | l2 = &(tcontext->range.level[1]); | ||
162 | goto mls_ops; | ||
163 | case CEXPR_L1H1: | ||
164 | l1 = &(scontext->range.level[0]); | ||
165 | l2 = &(scontext->range.level[1]); | ||
166 | goto mls_ops; | ||
167 | case CEXPR_L2H2: | ||
168 | l1 = &(tcontext->range.level[0]); | ||
169 | l2 = &(tcontext->range.level[1]); | ||
170 | goto mls_ops; | ||
171 | mls_ops: | ||
172 | switch (e->op) { | ||
173 | case CEXPR_EQ: | ||
174 | s[++sp] = mls_level_eq(l1, l2); | ||
175 | continue; | ||
176 | case CEXPR_NEQ: | ||
177 | s[++sp] = !mls_level_eq(l1, l2); | ||
178 | continue; | ||
179 | case CEXPR_DOM: | ||
180 | s[++sp] = mls_level_dom(l1, l2); | ||
181 | continue; | ||
182 | case CEXPR_DOMBY: | ||
183 | s[++sp] = mls_level_dom(l2, l1); | ||
184 | continue; | ||
185 | case CEXPR_INCOMP: | ||
186 | s[++sp] = mls_level_incomp(l2, l1); | ||
187 | continue; | ||
188 | default: | ||
189 | BUG(); | ||
190 | return 0; | ||
191 | } | ||
192 | break; | ||
193 | default: | ||
194 | BUG(); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | switch (e->op) { | ||
199 | case CEXPR_EQ: | ||
200 | s[++sp] = (val1 == val2); | ||
201 | break; | ||
202 | case CEXPR_NEQ: | ||
203 | s[++sp] = (val1 != val2); | ||
204 | break; | ||
205 | default: | ||
206 | BUG(); | ||
207 | return 0; | ||
208 | } | ||
209 | break; | ||
210 | case CEXPR_NAMES: | ||
211 | if (sp == (CEXPR_MAXDEPTH-1)) | ||
212 | return 0; | ||
213 | c = scontext; | ||
214 | if (e->attr & CEXPR_TARGET) | ||
215 | c = tcontext; | ||
216 | else if (e->attr & CEXPR_XTARGET) { | ||
217 | c = xcontext; | ||
218 | if (!c) { | ||
219 | BUG(); | ||
220 | return 0; | ||
221 | } | ||
222 | } | ||
223 | if (e->attr & CEXPR_USER) | ||
224 | val1 = c->user; | ||
225 | else if (e->attr & CEXPR_ROLE) | ||
226 | val1 = c->role; | ||
227 | else if (e->attr & CEXPR_TYPE) | ||
228 | val1 = c->type; | ||
229 | else { | ||
230 | BUG(); | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | switch (e->op) { | ||
235 | case CEXPR_EQ: | ||
236 | s[++sp] = ebitmap_get_bit(&e->names, val1 - 1); | ||
237 | break; | ||
238 | case CEXPR_NEQ: | ||
239 | s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1); | ||
240 | break; | ||
241 | default: | ||
242 | BUG(); | ||
243 | return 0; | ||
244 | } | ||
245 | break; | ||
246 | default: | ||
247 | BUG(); | ||
248 | return 0; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | BUG_ON(sp != 0); | ||
253 | return s[0]; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Compute access vectors based on a context structure pair for | ||
258 | * the permissions in a particular class. | ||
259 | */ | ||
260 | static int context_struct_compute_av(struct context *scontext, | ||
261 | struct context *tcontext, | ||
262 | u16 tclass, | ||
263 | u32 requested, | ||
264 | struct av_decision *avd) | ||
265 | { | ||
266 | struct constraint_node *constraint; | ||
267 | struct role_allow *ra; | ||
268 | struct avtab_key avkey; | ||
269 | struct avtab_datum *avdatum; | ||
270 | struct class_datum *tclass_datum; | ||
271 | |||
272 | /* | ||
273 | * Remap extended Netlink classes for old policy versions. | ||
274 | * Do this here rather than socket_type_to_security_class() | ||
275 | * in case a newer policy version is loaded, allowing sockets | ||
276 | * to remain in the correct class. | ||
277 | */ | ||
278 | if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) | ||
279 | if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && | ||
280 | tclass <= SECCLASS_NETLINK_DNRT_SOCKET) | ||
281 | tclass = SECCLASS_NETLINK_SOCKET; | ||
282 | |||
283 | if (!tclass || tclass > policydb.p_classes.nprim) { | ||
284 | printk(KERN_ERR "security_compute_av: unrecognized class %d\n", | ||
285 | tclass); | ||
286 | return -EINVAL; | ||
287 | } | ||
288 | tclass_datum = policydb.class_val_to_struct[tclass - 1]; | ||
289 | |||
290 | /* | ||
291 | * Initialize the access vectors to the default values. | ||
292 | */ | ||
293 | avd->allowed = 0; | ||
294 | avd->decided = 0xffffffff; | ||
295 | avd->auditallow = 0; | ||
296 | avd->auditdeny = 0xffffffff; | ||
297 | avd->seqno = latest_granting; | ||
298 | |||
299 | /* | ||
300 | * If a specific type enforcement rule was defined for | ||
301 | * this permission check, then use it. | ||
302 | */ | ||
303 | avkey.source_type = scontext->type; | ||
304 | avkey.target_type = tcontext->type; | ||
305 | avkey.target_class = tclass; | ||
306 | avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_AV); | ||
307 | if (avdatum) { | ||
308 | if (avdatum->specified & AVTAB_ALLOWED) | ||
309 | avd->allowed = avtab_allowed(avdatum); | ||
310 | if (avdatum->specified & AVTAB_AUDITDENY) | ||
311 | avd->auditdeny = avtab_auditdeny(avdatum); | ||
312 | if (avdatum->specified & AVTAB_AUDITALLOW) | ||
313 | avd->auditallow = avtab_auditallow(avdatum); | ||
314 | } | ||
315 | |||
316 | /* Check conditional av table for additional permissions */ | ||
317 | cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); | ||
318 | |||
319 | /* | ||
320 | * Remove any permissions prohibited by a constraint (this includes | ||
321 | * the MLS policy). | ||
322 | */ | ||
323 | constraint = tclass_datum->constraints; | ||
324 | while (constraint) { | ||
325 | if ((constraint->permissions & (avd->allowed)) && | ||
326 | !constraint_expr_eval(scontext, tcontext, NULL, | ||
327 | constraint->expr)) { | ||
328 | avd->allowed = (avd->allowed) & ~(constraint->permissions); | ||
329 | } | ||
330 | constraint = constraint->next; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * If checking process transition permission and the | ||
335 | * role is changing, then check the (current_role, new_role) | ||
336 | * pair. | ||
337 | */ | ||
338 | if (tclass == SECCLASS_PROCESS && | ||
339 | (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) && | ||
340 | scontext->role != tcontext->role) { | ||
341 | for (ra = policydb.role_allow; ra; ra = ra->next) { | ||
342 | if (scontext->role == ra->role && | ||
343 | tcontext->role == ra->new_role) | ||
344 | break; | ||
345 | } | ||
346 | if (!ra) | ||
347 | avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION | | ||
348 | PROCESS__DYNTRANSITION); | ||
349 | } | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int security_validtrans_handle_fail(struct context *ocontext, | ||
355 | struct context *ncontext, | ||
356 | struct context *tcontext, | ||
357 | u16 tclass) | ||
358 | { | ||
359 | char *o = NULL, *n = NULL, *t = NULL; | ||
360 | u32 olen, nlen, tlen; | ||
361 | |||
362 | if (context_struct_to_string(ocontext, &o, &olen) < 0) | ||
363 | goto out; | ||
364 | if (context_struct_to_string(ncontext, &n, &nlen) < 0) | ||
365 | goto out; | ||
366 | if (context_struct_to_string(tcontext, &t, &tlen) < 0) | ||
367 | goto out; | ||
368 | audit_log(current->audit_context, | ||
369 | "security_validate_transition: denied for" | ||
370 | " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", | ||
371 | o, n, t, policydb.p_class_val_to_name[tclass-1]); | ||
372 | out: | ||
373 | kfree(o); | ||
374 | kfree(n); | ||
375 | kfree(t); | ||
376 | |||
377 | if (!selinux_enforcing) | ||
378 | return 0; | ||
379 | return -EPERM; | ||
380 | } | ||
381 | |||
382 | int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, | ||
383 | u16 tclass) | ||
384 | { | ||
385 | struct context *ocontext; | ||
386 | struct context *ncontext; | ||
387 | struct context *tcontext; | ||
388 | struct class_datum *tclass_datum; | ||
389 | struct constraint_node *constraint; | ||
390 | int rc = 0; | ||
391 | |||
392 | if (!ss_initialized) | ||
393 | return 0; | ||
394 | |||
395 | POLICY_RDLOCK; | ||
396 | |||
397 | /* | ||
398 | * Remap extended Netlink classes for old policy versions. | ||
399 | * Do this here rather than socket_type_to_security_class() | ||
400 | * in case a newer policy version is loaded, allowing sockets | ||
401 | * to remain in the correct class. | ||
402 | */ | ||
403 | if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) | ||
404 | if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && | ||
405 | tclass <= SECCLASS_NETLINK_DNRT_SOCKET) | ||
406 | tclass = SECCLASS_NETLINK_SOCKET; | ||
407 | |||
408 | if (!tclass || tclass > policydb.p_classes.nprim) { | ||
409 | printk(KERN_ERR "security_validate_transition: " | ||
410 | "unrecognized class %d\n", tclass); | ||
411 | rc = -EINVAL; | ||
412 | goto out; | ||
413 | } | ||
414 | tclass_datum = policydb.class_val_to_struct[tclass - 1]; | ||
415 | |||
416 | ocontext = sidtab_search(&sidtab, oldsid); | ||
417 | if (!ocontext) { | ||
418 | printk(KERN_ERR "security_validate_transition: " | ||
419 | " unrecognized SID %d\n", oldsid); | ||
420 | rc = -EINVAL; | ||
421 | goto out; | ||
422 | } | ||
423 | |||
424 | ncontext = sidtab_search(&sidtab, newsid); | ||
425 | if (!ncontext) { | ||
426 | printk(KERN_ERR "security_validate_transition: " | ||
427 | " unrecognized SID %d\n", newsid); | ||
428 | rc = -EINVAL; | ||
429 | goto out; | ||
430 | } | ||
431 | |||
432 | tcontext = sidtab_search(&sidtab, tasksid); | ||
433 | if (!tcontext) { | ||
434 | printk(KERN_ERR "security_validate_transition: " | ||
435 | " unrecognized SID %d\n", tasksid); | ||
436 | rc = -EINVAL; | ||
437 | goto out; | ||
438 | } | ||
439 | |||
440 | constraint = tclass_datum->validatetrans; | ||
441 | while (constraint) { | ||
442 | if (!constraint_expr_eval(ocontext, ncontext, tcontext, | ||
443 | constraint->expr)) { | ||
444 | rc = security_validtrans_handle_fail(ocontext, ncontext, | ||
445 | tcontext, tclass); | ||
446 | goto out; | ||
447 | } | ||
448 | constraint = constraint->next; | ||
449 | } | ||
450 | |||
451 | out: | ||
452 | POLICY_RDUNLOCK; | ||
453 | return rc; | ||
454 | } | ||
455 | |||
456 | /** | ||
457 | * security_compute_av - Compute access vector decisions. | ||
458 | * @ssid: source security identifier | ||
459 | * @tsid: target security identifier | ||
460 | * @tclass: target security class | ||
461 | * @requested: requested permissions | ||
462 | * @avd: access vector decisions | ||
463 | * | ||
464 | * Compute a set of access vector decisions based on the | ||
465 | * SID pair (@ssid, @tsid) for the permissions in @tclass. | ||
466 | * Return -%EINVAL if any of the parameters are invalid or %0 | ||
467 | * if the access vector decisions were computed successfully. | ||
468 | */ | ||
469 | int security_compute_av(u32 ssid, | ||
470 | u32 tsid, | ||
471 | u16 tclass, | ||
472 | u32 requested, | ||
473 | struct av_decision *avd) | ||
474 | { | ||
475 | struct context *scontext = NULL, *tcontext = NULL; | ||
476 | int rc = 0; | ||
477 | |||
478 | if (!ss_initialized) { | ||
479 | avd->allowed = requested; | ||
480 | avd->decided = requested; | ||
481 | avd->auditallow = 0; | ||
482 | avd->auditdeny = 0xffffffff; | ||
483 | avd->seqno = latest_granting; | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | POLICY_RDLOCK; | ||
488 | |||
489 | scontext = sidtab_search(&sidtab, ssid); | ||
490 | if (!scontext) { | ||
491 | printk(KERN_ERR "security_compute_av: unrecognized SID %d\n", | ||
492 | ssid); | ||
493 | rc = -EINVAL; | ||
494 | goto out; | ||
495 | } | ||
496 | tcontext = sidtab_search(&sidtab, tsid); | ||
497 | if (!tcontext) { | ||
498 | printk(KERN_ERR "security_compute_av: unrecognized SID %d\n", | ||
499 | tsid); | ||
500 | rc = -EINVAL; | ||
501 | goto out; | ||
502 | } | ||
503 | |||
504 | rc = context_struct_compute_av(scontext, tcontext, tclass, | ||
505 | requested, avd); | ||
506 | out: | ||
507 | POLICY_RDUNLOCK; | ||
508 | return rc; | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * Write the security context string representation of | ||
513 | * the context structure `context' into a dynamically | ||
514 | * allocated string of the correct size. Set `*scontext' | ||
515 | * to point to this string and set `*scontext_len' to | ||
516 | * the length of the string. | ||
517 | */ | ||
518 | static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len) | ||
519 | { | ||
520 | char *scontextp; | ||
521 | |||
522 | *scontext = NULL; | ||
523 | *scontext_len = 0; | ||
524 | |||
525 | /* Compute the size of the context. */ | ||
526 | *scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1; | ||
527 | *scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1; | ||
528 | *scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1; | ||
529 | *scontext_len += mls_compute_context_len(context); | ||
530 | |||
531 | /* Allocate space for the context; caller must free this space. */ | ||
532 | scontextp = kmalloc(*scontext_len, GFP_ATOMIC); | ||
533 | if (!scontextp) { | ||
534 | return -ENOMEM; | ||
535 | } | ||
536 | *scontext = scontextp; | ||
537 | |||
538 | /* | ||
539 | * Copy the user name, role name and type name into the context. | ||
540 | */ | ||
541 | sprintf(scontextp, "%s:%s:%s", | ||
542 | policydb.p_user_val_to_name[context->user - 1], | ||
543 | policydb.p_role_val_to_name[context->role - 1], | ||
544 | policydb.p_type_val_to_name[context->type - 1]); | ||
545 | scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) + | ||
546 | 1 + strlen(policydb.p_role_val_to_name[context->role - 1]) + | ||
547 | 1 + strlen(policydb.p_type_val_to_name[context->type - 1]); | ||
548 | |||
549 | mls_sid_to_context(context, &scontextp); | ||
550 | |||
551 | *scontextp = 0; | ||
552 | |||
553 | return 0; | ||
554 | } | ||
555 | |||
556 | #include "initial_sid_to_string.h" | ||
557 | |||
558 | /** | ||
559 | * security_sid_to_context - Obtain a context for a given SID. | ||
560 | * @sid: security identifier, SID | ||
561 | * @scontext: security context | ||
562 | * @scontext_len: length in bytes | ||
563 | * | ||
564 | * Write the string representation of the context associated with @sid | ||
565 | * into a dynamically allocated string of the correct size. Set @scontext | ||
566 | * to point to this string and set @scontext_len to the length of the string. | ||
567 | */ | ||
568 | int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len) | ||
569 | { | ||
570 | struct context *context; | ||
571 | int rc = 0; | ||
572 | |||
573 | if (!ss_initialized) { | ||
574 | if (sid <= SECINITSID_NUM) { | ||
575 | char *scontextp; | ||
576 | |||
577 | *scontext_len = strlen(initial_sid_to_string[sid]) + 1; | ||
578 | scontextp = kmalloc(*scontext_len,GFP_ATOMIC); | ||
579 | strcpy(scontextp, initial_sid_to_string[sid]); | ||
580 | *scontext = scontextp; | ||
581 | goto out; | ||
582 | } | ||
583 | printk(KERN_ERR "security_sid_to_context: called before initial " | ||
584 | "load_policy on unknown SID %d\n", sid); | ||
585 | rc = -EINVAL; | ||
586 | goto out; | ||
587 | } | ||
588 | POLICY_RDLOCK; | ||
589 | context = sidtab_search(&sidtab, sid); | ||
590 | if (!context) { | ||
591 | printk(KERN_ERR "security_sid_to_context: unrecognized SID " | ||
592 | "%d\n", sid); | ||
593 | rc = -EINVAL; | ||
594 | goto out_unlock; | ||
595 | } | ||
596 | rc = context_struct_to_string(context, scontext, scontext_len); | ||
597 | out_unlock: | ||
598 | POLICY_RDUNLOCK; | ||
599 | out: | ||
600 | return rc; | ||
601 | |||
602 | } | ||
603 | |||
604 | /** | ||
605 | * security_context_to_sid - Obtain a SID for a given security context. | ||
606 | * @scontext: security context | ||
607 | * @scontext_len: length in bytes | ||
608 | * @sid: security identifier, SID | ||
609 | * | ||
610 | * Obtains a SID associated with the security context that | ||
611 | * has the string representation specified by @scontext. | ||
612 | * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient | ||
613 | * memory is available, or 0 on success. | ||
614 | */ | ||
615 | int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid) | ||
616 | { | ||
617 | char *scontext2; | ||
618 | struct context context; | ||
619 | struct role_datum *role; | ||
620 | struct type_datum *typdatum; | ||
621 | struct user_datum *usrdatum; | ||
622 | char *scontextp, *p, oldc; | ||
623 | int rc = 0; | ||
624 | |||
625 | if (!ss_initialized) { | ||
626 | int i; | ||
627 | |||
628 | for (i = 1; i < SECINITSID_NUM; i++) { | ||
629 | if (!strcmp(initial_sid_to_string[i], scontext)) { | ||
630 | *sid = i; | ||
631 | goto out; | ||
632 | } | ||
633 | } | ||
634 | *sid = SECINITSID_KERNEL; | ||
635 | goto out; | ||
636 | } | ||
637 | *sid = SECSID_NULL; | ||
638 | |||
639 | /* Copy the string so that we can modify the copy as we parse it. | ||
640 | The string should already by null terminated, but we append a | ||
641 | null suffix to the copy to avoid problems with the existing | ||
642 | attr package, which doesn't view the null terminator as part | ||
643 | of the attribute value. */ | ||
644 | scontext2 = kmalloc(scontext_len+1,GFP_KERNEL); | ||
645 | if (!scontext2) { | ||
646 | rc = -ENOMEM; | ||
647 | goto out; | ||
648 | } | ||
649 | memcpy(scontext2, scontext, scontext_len); | ||
650 | scontext2[scontext_len] = 0; | ||
651 | |||
652 | context_init(&context); | ||
653 | *sid = SECSID_NULL; | ||
654 | |||
655 | POLICY_RDLOCK; | ||
656 | |||
657 | /* Parse the security context. */ | ||
658 | |||
659 | rc = -EINVAL; | ||
660 | scontextp = (char *) scontext2; | ||
661 | |||
662 | /* Extract the user. */ | ||
663 | p = scontextp; | ||
664 | while (*p && *p != ':') | ||
665 | p++; | ||
666 | |||
667 | if (*p == 0) | ||
668 | goto out_unlock; | ||
669 | |||
670 | *p++ = 0; | ||
671 | |||
672 | usrdatum = hashtab_search(policydb.p_users.table, scontextp); | ||
673 | if (!usrdatum) | ||
674 | goto out_unlock; | ||
675 | |||
676 | context.user = usrdatum->value; | ||
677 | |||
678 | /* Extract role. */ | ||
679 | scontextp = p; | ||
680 | while (*p && *p != ':') | ||
681 | p++; | ||
682 | |||
683 | if (*p == 0) | ||
684 | goto out_unlock; | ||
685 | |||
686 | *p++ = 0; | ||
687 | |||
688 | role = hashtab_search(policydb.p_roles.table, scontextp); | ||
689 | if (!role) | ||
690 | goto out_unlock; | ||
691 | context.role = role->value; | ||
692 | |||
693 | /* Extract type. */ | ||
694 | scontextp = p; | ||
695 | while (*p && *p != ':') | ||
696 | p++; | ||
697 | oldc = *p; | ||
698 | *p++ = 0; | ||
699 | |||
700 | typdatum = hashtab_search(policydb.p_types.table, scontextp); | ||
701 | if (!typdatum) | ||
702 | goto out_unlock; | ||
703 | |||
704 | context.type = typdatum->value; | ||
705 | |||
706 | rc = mls_context_to_sid(oldc, &p, &context); | ||
707 | if (rc) | ||
708 | goto out_unlock; | ||
709 | |||
710 | if ((p - scontext2) < scontext_len) { | ||
711 | rc = -EINVAL; | ||
712 | goto out_unlock; | ||
713 | } | ||
714 | |||
715 | /* Check the validity of the new context. */ | ||
716 | if (!policydb_context_isvalid(&policydb, &context)) { | ||
717 | rc = -EINVAL; | ||
718 | goto out_unlock; | ||
719 | } | ||
720 | /* Obtain the new sid. */ | ||
721 | rc = sidtab_context_to_sid(&sidtab, &context, sid); | ||
722 | out_unlock: | ||
723 | POLICY_RDUNLOCK; | ||
724 | context_destroy(&context); | ||
725 | kfree(scontext2); | ||
726 | out: | ||
727 | return rc; | ||
728 | } | ||
729 | |||
730 | static int compute_sid_handle_invalid_context( | ||
731 | struct context *scontext, | ||
732 | struct context *tcontext, | ||
733 | u16 tclass, | ||
734 | struct context *newcontext) | ||
735 | { | ||
736 | char *s = NULL, *t = NULL, *n = NULL; | ||
737 | u32 slen, tlen, nlen; | ||
738 | |||
739 | if (context_struct_to_string(scontext, &s, &slen) < 0) | ||
740 | goto out; | ||
741 | if (context_struct_to_string(tcontext, &t, &tlen) < 0) | ||
742 | goto out; | ||
743 | if (context_struct_to_string(newcontext, &n, &nlen) < 0) | ||
744 | goto out; | ||
745 | audit_log(current->audit_context, | ||
746 | "security_compute_sid: invalid context %s" | ||
747 | " for scontext=%s" | ||
748 | " tcontext=%s" | ||
749 | " tclass=%s", | ||
750 | n, s, t, policydb.p_class_val_to_name[tclass-1]); | ||
751 | out: | ||
752 | kfree(s); | ||
753 | kfree(t); | ||
754 | kfree(n); | ||
755 | if (!selinux_enforcing) | ||
756 | return 0; | ||
757 | return -EACCES; | ||
758 | } | ||
759 | |||
760 | static int security_compute_sid(u32 ssid, | ||
761 | u32 tsid, | ||
762 | u16 tclass, | ||
763 | u32 specified, | ||
764 | u32 *out_sid) | ||
765 | { | ||
766 | struct context *scontext = NULL, *tcontext = NULL, newcontext; | ||
767 | struct role_trans *roletr = NULL; | ||
768 | struct avtab_key avkey; | ||
769 | struct avtab_datum *avdatum; | ||
770 | struct avtab_node *node; | ||
771 | unsigned int type_change = 0; | ||
772 | int rc = 0; | ||
773 | |||
774 | if (!ss_initialized) { | ||
775 | switch (tclass) { | ||
776 | case SECCLASS_PROCESS: | ||
777 | *out_sid = ssid; | ||
778 | break; | ||
779 | default: | ||
780 | *out_sid = tsid; | ||
781 | break; | ||
782 | } | ||
783 | goto out; | ||
784 | } | ||
785 | |||
786 | POLICY_RDLOCK; | ||
787 | |||
788 | scontext = sidtab_search(&sidtab, ssid); | ||
789 | if (!scontext) { | ||
790 | printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n", | ||
791 | ssid); | ||
792 | rc = -EINVAL; | ||
793 | goto out_unlock; | ||
794 | } | ||
795 | tcontext = sidtab_search(&sidtab, tsid); | ||
796 | if (!tcontext) { | ||
797 | printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n", | ||
798 | tsid); | ||
799 | rc = -EINVAL; | ||
800 | goto out_unlock; | ||
801 | } | ||
802 | |||
803 | context_init(&newcontext); | ||
804 | |||
805 | /* Set the user identity. */ | ||
806 | switch (specified) { | ||
807 | case AVTAB_TRANSITION: | ||
808 | case AVTAB_CHANGE: | ||
809 | /* Use the process user identity. */ | ||
810 | newcontext.user = scontext->user; | ||
811 | break; | ||
812 | case AVTAB_MEMBER: | ||
813 | /* Use the related object owner. */ | ||
814 | newcontext.user = tcontext->user; | ||
815 | break; | ||
816 | } | ||
817 | |||
818 | /* Set the role and type to default values. */ | ||
819 | switch (tclass) { | ||
820 | case SECCLASS_PROCESS: | ||
821 | /* Use the current role and type of process. */ | ||
822 | newcontext.role = scontext->role; | ||
823 | newcontext.type = scontext->type; | ||
824 | break; | ||
825 | default: | ||
826 | /* Use the well-defined object role. */ | ||
827 | newcontext.role = OBJECT_R_VAL; | ||
828 | /* Use the type of the related object. */ | ||
829 | newcontext.type = tcontext->type; | ||
830 | } | ||
831 | |||
832 | /* Look for a type transition/member/change rule. */ | ||
833 | avkey.source_type = scontext->type; | ||
834 | avkey.target_type = tcontext->type; | ||
835 | avkey.target_class = tclass; | ||
836 | avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_TYPE); | ||
837 | |||
838 | /* If no permanent rule, also check for enabled conditional rules */ | ||
839 | if(!avdatum) { | ||
840 | node = avtab_search_node(&policydb.te_cond_avtab, &avkey, specified); | ||
841 | for (; node != NULL; node = avtab_search_node_next(node, specified)) { | ||
842 | if (node->datum.specified & AVTAB_ENABLED) { | ||
843 | avdatum = &node->datum; | ||
844 | break; | ||
845 | } | ||
846 | } | ||
847 | } | ||
848 | |||
849 | type_change = (avdatum && (avdatum->specified & specified)); | ||
850 | if (type_change) { | ||
851 | /* Use the type from the type transition/member/change rule. */ | ||
852 | switch (specified) { | ||
853 | case AVTAB_TRANSITION: | ||
854 | newcontext.type = avtab_transition(avdatum); | ||
855 | break; | ||
856 | case AVTAB_MEMBER: | ||
857 | newcontext.type = avtab_member(avdatum); | ||
858 | break; | ||
859 | case AVTAB_CHANGE: | ||
860 | newcontext.type = avtab_change(avdatum); | ||
861 | break; | ||
862 | } | ||
863 | } | ||
864 | |||
865 | /* Check for class-specific changes. */ | ||
866 | switch (tclass) { | ||
867 | case SECCLASS_PROCESS: | ||
868 | if (specified & AVTAB_TRANSITION) { | ||
869 | /* Look for a role transition rule. */ | ||
870 | for (roletr = policydb.role_tr; roletr; | ||
871 | roletr = roletr->next) { | ||
872 | if (roletr->role == scontext->role && | ||
873 | roletr->type == tcontext->type) { | ||
874 | /* Use the role transition rule. */ | ||
875 | newcontext.role = roletr->new_role; | ||
876 | break; | ||
877 | } | ||
878 | } | ||
879 | } | ||
880 | break; | ||
881 | default: | ||
882 | break; | ||
883 | } | ||
884 | |||
885 | /* Set the MLS attributes. | ||
886 | This is done last because it may allocate memory. */ | ||
887 | rc = mls_compute_sid(scontext, tcontext, tclass, specified, &newcontext); | ||
888 | if (rc) | ||
889 | goto out_unlock; | ||
890 | |||
891 | /* Check the validity of the context. */ | ||
892 | if (!policydb_context_isvalid(&policydb, &newcontext)) { | ||
893 | rc = compute_sid_handle_invalid_context(scontext, | ||
894 | tcontext, | ||
895 | tclass, | ||
896 | &newcontext); | ||
897 | if (rc) | ||
898 | goto out_unlock; | ||
899 | } | ||
900 | /* Obtain the sid for the context. */ | ||
901 | rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid); | ||
902 | out_unlock: | ||
903 | POLICY_RDUNLOCK; | ||
904 | context_destroy(&newcontext); | ||
905 | out: | ||
906 | return rc; | ||
907 | } | ||
908 | |||
909 | /** | ||
910 | * security_transition_sid - Compute the SID for a new subject/object. | ||
911 | * @ssid: source security identifier | ||
912 | * @tsid: target security identifier | ||
913 | * @tclass: target security class | ||
914 | * @out_sid: security identifier for new subject/object | ||
915 | * | ||
916 | * Compute a SID to use for labeling a new subject or object in the | ||
917 | * class @tclass based on a SID pair (@ssid, @tsid). | ||
918 | * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM | ||
919 | * if insufficient memory is available, or %0 if the new SID was | ||
920 | * computed successfully. | ||
921 | */ | ||
922 | int security_transition_sid(u32 ssid, | ||
923 | u32 tsid, | ||
924 | u16 tclass, | ||
925 | u32 *out_sid) | ||
926 | { | ||
927 | return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid); | ||
928 | } | ||
929 | |||
930 | /** | ||
931 | * security_member_sid - Compute the SID for member selection. | ||
932 | * @ssid: source security identifier | ||
933 | * @tsid: target security identifier | ||
934 | * @tclass: target security class | ||
935 | * @out_sid: security identifier for selected member | ||
936 | * | ||
937 | * Compute a SID to use when selecting a member of a polyinstantiated | ||
938 | * object of class @tclass based on a SID pair (@ssid, @tsid). | ||
939 | * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM | ||
940 | * if insufficient memory is available, or %0 if the SID was | ||
941 | * computed successfully. | ||
942 | */ | ||
943 | int security_member_sid(u32 ssid, | ||
944 | u32 tsid, | ||
945 | u16 tclass, | ||
946 | u32 *out_sid) | ||
947 | { | ||
948 | return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid); | ||
949 | } | ||
950 | |||
951 | /** | ||
952 | * security_change_sid - Compute the SID for object relabeling. | ||
953 | * @ssid: source security identifier | ||
954 | * @tsid: target security identifier | ||
955 | * @tclass: target security class | ||
956 | * @out_sid: security identifier for selected member | ||
957 | * | ||
958 | * Compute a SID to use for relabeling an object of class @tclass | ||
959 | * based on a SID pair (@ssid, @tsid). | ||
960 | * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM | ||
961 | * if insufficient memory is available, or %0 if the SID was | ||
962 | * computed successfully. | ||
963 | */ | ||
964 | int security_change_sid(u32 ssid, | ||
965 | u32 tsid, | ||
966 | u16 tclass, | ||
967 | u32 *out_sid) | ||
968 | { | ||
969 | return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid); | ||
970 | } | ||
971 | |||
972 | /* | ||
973 | * Verify that each permission that is defined under the | ||
974 | * existing policy is still defined with the same value | ||
975 | * in the new policy. | ||
976 | */ | ||
977 | static int validate_perm(void *key, void *datum, void *p) | ||
978 | { | ||
979 | struct hashtab *h; | ||
980 | struct perm_datum *perdatum, *perdatum2; | ||
981 | int rc = 0; | ||
982 | |||
983 | |||
984 | h = p; | ||
985 | perdatum = datum; | ||
986 | |||
987 | perdatum2 = hashtab_search(h, key); | ||
988 | if (!perdatum2) { | ||
989 | printk(KERN_ERR "security: permission %s disappeared", | ||
990 | (char *)key); | ||
991 | rc = -ENOENT; | ||
992 | goto out; | ||
993 | } | ||
994 | if (perdatum->value != perdatum2->value) { | ||
995 | printk(KERN_ERR "security: the value of permission %s changed", | ||
996 | (char *)key); | ||
997 | rc = -EINVAL; | ||
998 | } | ||
999 | out: | ||
1000 | return rc; | ||
1001 | } | ||
1002 | |||
1003 | /* | ||
1004 | * Verify that each class that is defined under the | ||
1005 | * existing policy is still defined with the same | ||
1006 | * attributes in the new policy. | ||
1007 | */ | ||
1008 | static int validate_class(void *key, void *datum, void *p) | ||
1009 | { | ||
1010 | struct policydb *newp; | ||
1011 | struct class_datum *cladatum, *cladatum2; | ||
1012 | int rc; | ||
1013 | |||
1014 | newp = p; | ||
1015 | cladatum = datum; | ||
1016 | |||
1017 | cladatum2 = hashtab_search(newp->p_classes.table, key); | ||
1018 | if (!cladatum2) { | ||
1019 | printk(KERN_ERR "security: class %s disappeared\n", | ||
1020 | (char *)key); | ||
1021 | rc = -ENOENT; | ||
1022 | goto out; | ||
1023 | } | ||
1024 | if (cladatum->value != cladatum2->value) { | ||
1025 | printk(KERN_ERR "security: the value of class %s changed\n", | ||
1026 | (char *)key); | ||
1027 | rc = -EINVAL; | ||
1028 | goto out; | ||
1029 | } | ||
1030 | if ((cladatum->comdatum && !cladatum2->comdatum) || | ||
1031 | (!cladatum->comdatum && cladatum2->comdatum)) { | ||
1032 | printk(KERN_ERR "security: the inherits clause for the access " | ||
1033 | "vector definition for class %s changed\n", (char *)key); | ||
1034 | rc = -EINVAL; | ||
1035 | goto out; | ||
1036 | } | ||
1037 | if (cladatum->comdatum) { | ||
1038 | rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm, | ||
1039 | cladatum2->comdatum->permissions.table); | ||
1040 | if (rc) { | ||
1041 | printk(" in the access vector definition for class " | ||
1042 | "%s\n", (char *)key); | ||
1043 | goto out; | ||
1044 | } | ||
1045 | } | ||
1046 | rc = hashtab_map(cladatum->permissions.table, validate_perm, | ||
1047 | cladatum2->permissions.table); | ||
1048 | if (rc) | ||
1049 | printk(" in access vector definition for class %s\n", | ||
1050 | (char *)key); | ||
1051 | out: | ||
1052 | return rc; | ||
1053 | } | ||
1054 | |||
1055 | /* Clone the SID into the new SID table. */ | ||
1056 | static int clone_sid(u32 sid, | ||
1057 | struct context *context, | ||
1058 | void *arg) | ||
1059 | { | ||
1060 | struct sidtab *s = arg; | ||
1061 | |||
1062 | return sidtab_insert(s, sid, context); | ||
1063 | } | ||
1064 | |||
1065 | static inline int convert_context_handle_invalid_context(struct context *context) | ||
1066 | { | ||
1067 | int rc = 0; | ||
1068 | |||
1069 | if (selinux_enforcing) { | ||
1070 | rc = -EINVAL; | ||
1071 | } else { | ||
1072 | char *s; | ||
1073 | u32 len; | ||
1074 | |||
1075 | context_struct_to_string(context, &s, &len); | ||
1076 | printk(KERN_ERR "security: context %s is invalid\n", s); | ||
1077 | kfree(s); | ||
1078 | } | ||
1079 | return rc; | ||
1080 | } | ||
1081 | |||
1082 | struct convert_context_args { | ||
1083 | struct policydb *oldp; | ||
1084 | struct policydb *newp; | ||
1085 | }; | ||
1086 | |||
1087 | /* | ||
1088 | * Convert the values in the security context | ||
1089 | * structure `c' from the values specified | ||
1090 | * in the policy `p->oldp' to the values specified | ||
1091 | * in the policy `p->newp'. Verify that the | ||
1092 | * context is valid under the new policy. | ||
1093 | */ | ||
1094 | static int convert_context(u32 key, | ||
1095 | struct context *c, | ||
1096 | void *p) | ||
1097 | { | ||
1098 | struct convert_context_args *args; | ||
1099 | struct context oldc; | ||
1100 | struct role_datum *role; | ||
1101 | struct type_datum *typdatum; | ||
1102 | struct user_datum *usrdatum; | ||
1103 | char *s; | ||
1104 | u32 len; | ||
1105 | int rc; | ||
1106 | |||
1107 | args = p; | ||
1108 | |||
1109 | rc = context_cpy(&oldc, c); | ||
1110 | if (rc) | ||
1111 | goto out; | ||
1112 | |||
1113 | rc = -EINVAL; | ||
1114 | |||
1115 | /* Convert the user. */ | ||
1116 | usrdatum = hashtab_search(args->newp->p_users.table, | ||
1117 | args->oldp->p_user_val_to_name[c->user - 1]); | ||
1118 | if (!usrdatum) { | ||
1119 | goto bad; | ||
1120 | } | ||
1121 | c->user = usrdatum->value; | ||
1122 | |||
1123 | /* Convert the role. */ | ||
1124 | role = hashtab_search(args->newp->p_roles.table, | ||
1125 | args->oldp->p_role_val_to_name[c->role - 1]); | ||
1126 | if (!role) { | ||
1127 | goto bad; | ||
1128 | } | ||
1129 | c->role = role->value; | ||
1130 | |||
1131 | /* Convert the type. */ | ||
1132 | typdatum = hashtab_search(args->newp->p_types.table, | ||
1133 | args->oldp->p_type_val_to_name[c->type - 1]); | ||
1134 | if (!typdatum) { | ||
1135 | goto bad; | ||
1136 | } | ||
1137 | c->type = typdatum->value; | ||
1138 | |||
1139 | rc = mls_convert_context(args->oldp, args->newp, c); | ||
1140 | if (rc) | ||
1141 | goto bad; | ||
1142 | |||
1143 | /* Check the validity of the new context. */ | ||
1144 | if (!policydb_context_isvalid(args->newp, c)) { | ||
1145 | rc = convert_context_handle_invalid_context(&oldc); | ||
1146 | if (rc) | ||
1147 | goto bad; | ||
1148 | } | ||
1149 | |||
1150 | context_destroy(&oldc); | ||
1151 | out: | ||
1152 | return rc; | ||
1153 | bad: | ||
1154 | context_struct_to_string(&oldc, &s, &len); | ||
1155 | context_destroy(&oldc); | ||
1156 | printk(KERN_ERR "security: invalidating context %s\n", s); | ||
1157 | kfree(s); | ||
1158 | goto out; | ||
1159 | } | ||
1160 | |||
1161 | extern void selinux_complete_init(void); | ||
1162 | |||
1163 | /** | ||
1164 | * security_load_policy - Load a security policy configuration. | ||
1165 | * @data: binary policy data | ||
1166 | * @len: length of data in bytes | ||
1167 | * | ||
1168 | * Load a new set of security policy configuration data, | ||
1169 | * validate it and convert the SID table as necessary. | ||
1170 | * This function will flush the access vector cache after | ||
1171 | * loading the new policy. | ||
1172 | */ | ||
1173 | int security_load_policy(void *data, size_t len) | ||
1174 | { | ||
1175 | struct policydb oldpolicydb, newpolicydb; | ||
1176 | struct sidtab oldsidtab, newsidtab; | ||
1177 | struct convert_context_args args; | ||
1178 | u32 seqno; | ||
1179 | int rc = 0; | ||
1180 | struct policy_file file = { data, len }, *fp = &file; | ||
1181 | |||
1182 | LOAD_LOCK; | ||
1183 | |||
1184 | if (!ss_initialized) { | ||
1185 | avtab_cache_init(); | ||
1186 | if (policydb_read(&policydb, fp)) { | ||
1187 | LOAD_UNLOCK; | ||
1188 | avtab_cache_destroy(); | ||
1189 | return -EINVAL; | ||
1190 | } | ||
1191 | if (policydb_load_isids(&policydb, &sidtab)) { | ||
1192 | LOAD_UNLOCK; | ||
1193 | policydb_destroy(&policydb); | ||
1194 | avtab_cache_destroy(); | ||
1195 | return -EINVAL; | ||
1196 | } | ||
1197 | policydb_loaded_version = policydb.policyvers; | ||
1198 | ss_initialized = 1; | ||
1199 | |||
1200 | LOAD_UNLOCK; | ||
1201 | selinux_complete_init(); | ||
1202 | return 0; | ||
1203 | } | ||
1204 | |||
1205 | #if 0 | ||
1206 | sidtab_hash_eval(&sidtab, "sids"); | ||
1207 | #endif | ||
1208 | |||
1209 | if (policydb_read(&newpolicydb, fp)) { | ||
1210 | LOAD_UNLOCK; | ||
1211 | return -EINVAL; | ||
1212 | } | ||
1213 | |||
1214 | sidtab_init(&newsidtab); | ||
1215 | |||
1216 | /* Verify that the existing classes did not change. */ | ||
1217 | if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) { | ||
1218 | printk(KERN_ERR "security: the definition of an existing " | ||
1219 | "class changed\n"); | ||
1220 | rc = -EINVAL; | ||
1221 | goto err; | ||
1222 | } | ||
1223 | |||
1224 | /* Clone the SID table. */ | ||
1225 | sidtab_shutdown(&sidtab); | ||
1226 | if (sidtab_map(&sidtab, clone_sid, &newsidtab)) { | ||
1227 | rc = -ENOMEM; | ||
1228 | goto err; | ||
1229 | } | ||
1230 | |||
1231 | /* Convert the internal representations of contexts | ||
1232 | in the new SID table and remove invalid SIDs. */ | ||
1233 | args.oldp = &policydb; | ||
1234 | args.newp = &newpolicydb; | ||
1235 | sidtab_map_remove_on_error(&newsidtab, convert_context, &args); | ||
1236 | |||
1237 | /* Save the old policydb and SID table to free later. */ | ||
1238 | memcpy(&oldpolicydb, &policydb, sizeof policydb); | ||
1239 | sidtab_set(&oldsidtab, &sidtab); | ||
1240 | |||
1241 | /* Install the new policydb and SID table. */ | ||
1242 | POLICY_WRLOCK; | ||
1243 | memcpy(&policydb, &newpolicydb, sizeof policydb); | ||
1244 | sidtab_set(&sidtab, &newsidtab); | ||
1245 | seqno = ++latest_granting; | ||
1246 | policydb_loaded_version = policydb.policyvers; | ||
1247 | POLICY_WRUNLOCK; | ||
1248 | LOAD_UNLOCK; | ||
1249 | |||
1250 | /* Free the old policydb and SID table. */ | ||
1251 | policydb_destroy(&oldpolicydb); | ||
1252 | sidtab_destroy(&oldsidtab); | ||
1253 | |||
1254 | avc_ss_reset(seqno); | ||
1255 | selnl_notify_policyload(seqno); | ||
1256 | |||
1257 | return 0; | ||
1258 | |||
1259 | err: | ||
1260 | LOAD_UNLOCK; | ||
1261 | sidtab_destroy(&newsidtab); | ||
1262 | policydb_destroy(&newpolicydb); | ||
1263 | return rc; | ||
1264 | |||
1265 | } | ||
1266 | |||
1267 | /** | ||
1268 | * security_port_sid - Obtain the SID for a port. | ||
1269 | * @domain: communication domain aka address family | ||
1270 | * @type: socket type | ||
1271 | * @protocol: protocol number | ||
1272 | * @port: port number | ||
1273 | * @out_sid: security identifier | ||
1274 | */ | ||
1275 | int security_port_sid(u16 domain, | ||
1276 | u16 type, | ||
1277 | u8 protocol, | ||
1278 | u16 port, | ||
1279 | u32 *out_sid) | ||
1280 | { | ||
1281 | struct ocontext *c; | ||
1282 | int rc = 0; | ||
1283 | |||
1284 | POLICY_RDLOCK; | ||
1285 | |||
1286 | c = policydb.ocontexts[OCON_PORT]; | ||
1287 | while (c) { | ||
1288 | if (c->u.port.protocol == protocol && | ||
1289 | c->u.port.low_port <= port && | ||
1290 | c->u.port.high_port >= port) | ||
1291 | break; | ||
1292 | c = c->next; | ||
1293 | } | ||
1294 | |||
1295 | if (c) { | ||
1296 | if (!c->sid[0]) { | ||
1297 | rc = sidtab_context_to_sid(&sidtab, | ||
1298 | &c->context[0], | ||
1299 | &c->sid[0]); | ||
1300 | if (rc) | ||
1301 | goto out; | ||
1302 | } | ||
1303 | *out_sid = c->sid[0]; | ||
1304 | } else { | ||
1305 | *out_sid = SECINITSID_PORT; | ||
1306 | } | ||
1307 | |||
1308 | out: | ||
1309 | POLICY_RDUNLOCK; | ||
1310 | return rc; | ||
1311 | } | ||
1312 | |||
1313 | /** | ||
1314 | * security_netif_sid - Obtain the SID for a network interface. | ||
1315 | * @name: interface name | ||
1316 | * @if_sid: interface SID | ||
1317 | * @msg_sid: default SID for received packets | ||
1318 | */ | ||
1319 | int security_netif_sid(char *name, | ||
1320 | u32 *if_sid, | ||
1321 | u32 *msg_sid) | ||
1322 | { | ||
1323 | int rc = 0; | ||
1324 | struct ocontext *c; | ||
1325 | |||
1326 | POLICY_RDLOCK; | ||
1327 | |||
1328 | c = policydb.ocontexts[OCON_NETIF]; | ||
1329 | while (c) { | ||
1330 | if (strcmp(name, c->u.name) == 0) | ||
1331 | break; | ||
1332 | c = c->next; | ||
1333 | } | ||
1334 | |||
1335 | if (c) { | ||
1336 | if (!c->sid[0] || !c->sid[1]) { | ||
1337 | rc = sidtab_context_to_sid(&sidtab, | ||
1338 | &c->context[0], | ||
1339 | &c->sid[0]); | ||
1340 | if (rc) | ||
1341 | goto out; | ||
1342 | rc = sidtab_context_to_sid(&sidtab, | ||
1343 | &c->context[1], | ||
1344 | &c->sid[1]); | ||
1345 | if (rc) | ||
1346 | goto out; | ||
1347 | } | ||
1348 | *if_sid = c->sid[0]; | ||
1349 | *msg_sid = c->sid[1]; | ||
1350 | } else { | ||
1351 | *if_sid = SECINITSID_NETIF; | ||
1352 | *msg_sid = SECINITSID_NETMSG; | ||
1353 | } | ||
1354 | |||
1355 | out: | ||
1356 | POLICY_RDUNLOCK; | ||
1357 | return rc; | ||
1358 | } | ||
1359 | |||
1360 | static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask) | ||
1361 | { | ||
1362 | int i, fail = 0; | ||
1363 | |||
1364 | for(i = 0; i < 4; i++) | ||
1365 | if(addr[i] != (input[i] & mask[i])) { | ||
1366 | fail = 1; | ||
1367 | break; | ||
1368 | } | ||
1369 | |||
1370 | return !fail; | ||
1371 | } | ||
1372 | |||
1373 | /** | ||
1374 | * security_node_sid - Obtain the SID for a node (host). | ||
1375 | * @domain: communication domain aka address family | ||
1376 | * @addrp: address | ||
1377 | * @addrlen: address length in bytes | ||
1378 | * @out_sid: security identifier | ||
1379 | */ | ||
1380 | int security_node_sid(u16 domain, | ||
1381 | void *addrp, | ||
1382 | u32 addrlen, | ||
1383 | u32 *out_sid) | ||
1384 | { | ||
1385 | int rc = 0; | ||
1386 | struct ocontext *c; | ||
1387 | |||
1388 | POLICY_RDLOCK; | ||
1389 | |||
1390 | switch (domain) { | ||
1391 | case AF_INET: { | ||
1392 | u32 addr; | ||
1393 | |||
1394 | if (addrlen != sizeof(u32)) { | ||
1395 | rc = -EINVAL; | ||
1396 | goto out; | ||
1397 | } | ||
1398 | |||
1399 | addr = *((u32 *)addrp); | ||
1400 | |||
1401 | c = policydb.ocontexts[OCON_NODE]; | ||
1402 | while (c) { | ||
1403 | if (c->u.node.addr == (addr & c->u.node.mask)) | ||
1404 | break; | ||
1405 | c = c->next; | ||
1406 | } | ||
1407 | break; | ||
1408 | } | ||
1409 | |||
1410 | case AF_INET6: | ||
1411 | if (addrlen != sizeof(u64) * 2) { | ||
1412 | rc = -EINVAL; | ||
1413 | goto out; | ||
1414 | } | ||
1415 | c = policydb.ocontexts[OCON_NODE6]; | ||
1416 | while (c) { | ||
1417 | if (match_ipv6_addrmask(addrp, c->u.node6.addr, | ||
1418 | c->u.node6.mask)) | ||
1419 | break; | ||
1420 | c = c->next; | ||
1421 | } | ||
1422 | break; | ||
1423 | |||
1424 | default: | ||
1425 | *out_sid = SECINITSID_NODE; | ||
1426 | goto out; | ||
1427 | } | ||
1428 | |||
1429 | if (c) { | ||
1430 | if (!c->sid[0]) { | ||
1431 | rc = sidtab_context_to_sid(&sidtab, | ||
1432 | &c->context[0], | ||
1433 | &c->sid[0]); | ||
1434 | if (rc) | ||
1435 | goto out; | ||
1436 | } | ||
1437 | *out_sid = c->sid[0]; | ||
1438 | } else { | ||
1439 | *out_sid = SECINITSID_NODE; | ||
1440 | } | ||
1441 | |||
1442 | out: | ||
1443 | POLICY_RDUNLOCK; | ||
1444 | return rc; | ||
1445 | } | ||
1446 | |||
1447 | #define SIDS_NEL 25 | ||
1448 | |||
1449 | /** | ||
1450 | * security_get_user_sids - Obtain reachable SIDs for a user. | ||
1451 | * @fromsid: starting SID | ||
1452 | * @username: username | ||
1453 | * @sids: array of reachable SIDs for user | ||
1454 | * @nel: number of elements in @sids | ||
1455 | * | ||
1456 | * Generate the set of SIDs for legal security contexts | ||
1457 | * for a given user that can be reached by @fromsid. | ||
1458 | * Set *@sids to point to a dynamically allocated | ||
1459 | * array containing the set of SIDs. Set *@nel to the | ||
1460 | * number of elements in the array. | ||
1461 | */ | ||
1462 | |||
1463 | int security_get_user_sids(u32 fromsid, | ||
1464 | char *username, | ||
1465 | u32 **sids, | ||
1466 | u32 *nel) | ||
1467 | { | ||
1468 | struct context *fromcon, usercon; | ||
1469 | u32 *mysids, *mysids2, sid; | ||
1470 | u32 mynel = 0, maxnel = SIDS_NEL; | ||
1471 | struct user_datum *user; | ||
1472 | struct role_datum *role; | ||
1473 | struct av_decision avd; | ||
1474 | int rc = 0, i, j; | ||
1475 | |||
1476 | if (!ss_initialized) { | ||
1477 | *sids = NULL; | ||
1478 | *nel = 0; | ||
1479 | goto out; | ||
1480 | } | ||
1481 | |||
1482 | POLICY_RDLOCK; | ||
1483 | |||
1484 | fromcon = sidtab_search(&sidtab, fromsid); | ||
1485 | if (!fromcon) { | ||
1486 | rc = -EINVAL; | ||
1487 | goto out_unlock; | ||
1488 | } | ||
1489 | |||
1490 | user = hashtab_search(policydb.p_users.table, username); | ||
1491 | if (!user) { | ||
1492 | rc = -EINVAL; | ||
1493 | goto out_unlock; | ||
1494 | } | ||
1495 | usercon.user = user->value; | ||
1496 | |||
1497 | mysids = kmalloc(maxnel*sizeof(*mysids), GFP_ATOMIC); | ||
1498 | if (!mysids) { | ||
1499 | rc = -ENOMEM; | ||
1500 | goto out_unlock; | ||
1501 | } | ||
1502 | memset(mysids, 0, maxnel*sizeof(*mysids)); | ||
1503 | |||
1504 | for (i = ebitmap_startbit(&user->roles); i < ebitmap_length(&user->roles); i++) { | ||
1505 | if (!ebitmap_get_bit(&user->roles, i)) | ||
1506 | continue; | ||
1507 | role = policydb.role_val_to_struct[i]; | ||
1508 | usercon.role = i+1; | ||
1509 | for (j = ebitmap_startbit(&role->types); j < ebitmap_length(&role->types); j++) { | ||
1510 | if (!ebitmap_get_bit(&role->types, j)) | ||
1511 | continue; | ||
1512 | usercon.type = j+1; | ||
1513 | |||
1514 | if (mls_setup_user_range(fromcon, user, &usercon)) | ||
1515 | continue; | ||
1516 | |||
1517 | rc = context_struct_compute_av(fromcon, &usercon, | ||
1518 | SECCLASS_PROCESS, | ||
1519 | PROCESS__TRANSITION, | ||
1520 | &avd); | ||
1521 | if (rc || !(avd.allowed & PROCESS__TRANSITION)) | ||
1522 | continue; | ||
1523 | rc = sidtab_context_to_sid(&sidtab, &usercon, &sid); | ||
1524 | if (rc) { | ||
1525 | kfree(mysids); | ||
1526 | goto out_unlock; | ||
1527 | } | ||
1528 | if (mynel < maxnel) { | ||
1529 | mysids[mynel++] = sid; | ||
1530 | } else { | ||
1531 | maxnel += SIDS_NEL; | ||
1532 | mysids2 = kmalloc(maxnel*sizeof(*mysids2), GFP_ATOMIC); | ||
1533 | if (!mysids2) { | ||
1534 | rc = -ENOMEM; | ||
1535 | kfree(mysids); | ||
1536 | goto out_unlock; | ||
1537 | } | ||
1538 | memset(mysids2, 0, maxnel*sizeof(*mysids2)); | ||
1539 | memcpy(mysids2, mysids, mynel * sizeof(*mysids2)); | ||
1540 | kfree(mysids); | ||
1541 | mysids = mysids2; | ||
1542 | mysids[mynel++] = sid; | ||
1543 | } | ||
1544 | } | ||
1545 | } | ||
1546 | |||
1547 | *sids = mysids; | ||
1548 | *nel = mynel; | ||
1549 | |||
1550 | out_unlock: | ||
1551 | POLICY_RDUNLOCK; | ||
1552 | out: | ||
1553 | return rc; | ||
1554 | } | ||
1555 | |||
1556 | /** | ||
1557 | * security_genfs_sid - Obtain a SID for a file in a filesystem | ||
1558 | * @fstype: filesystem type | ||
1559 | * @path: path from root of mount | ||
1560 | * @sclass: file security class | ||
1561 | * @sid: SID for path | ||
1562 | * | ||
1563 | * Obtain a SID to use for a file in a filesystem that | ||
1564 | * cannot support xattr or use a fixed labeling behavior like | ||
1565 | * transition SIDs or task SIDs. | ||
1566 | */ | ||
1567 | int security_genfs_sid(const char *fstype, | ||
1568 | char *path, | ||
1569 | u16 sclass, | ||
1570 | u32 *sid) | ||
1571 | { | ||
1572 | int len; | ||
1573 | struct genfs *genfs; | ||
1574 | struct ocontext *c; | ||
1575 | int rc = 0, cmp = 0; | ||
1576 | |||
1577 | POLICY_RDLOCK; | ||
1578 | |||
1579 | for (genfs = policydb.genfs; genfs; genfs = genfs->next) { | ||
1580 | cmp = strcmp(fstype, genfs->fstype); | ||
1581 | if (cmp <= 0) | ||
1582 | break; | ||
1583 | } | ||
1584 | |||
1585 | if (!genfs || cmp) { | ||
1586 | *sid = SECINITSID_UNLABELED; | ||
1587 | rc = -ENOENT; | ||
1588 | goto out; | ||
1589 | } | ||
1590 | |||
1591 | for (c = genfs->head; c; c = c->next) { | ||
1592 | len = strlen(c->u.name); | ||
1593 | if ((!c->v.sclass || sclass == c->v.sclass) && | ||
1594 | (strncmp(c->u.name, path, len) == 0)) | ||
1595 | break; | ||
1596 | } | ||
1597 | |||
1598 | if (!c) { | ||
1599 | *sid = SECINITSID_UNLABELED; | ||
1600 | rc = -ENOENT; | ||
1601 | goto out; | ||
1602 | } | ||
1603 | |||
1604 | if (!c->sid[0]) { | ||
1605 | rc = sidtab_context_to_sid(&sidtab, | ||
1606 | &c->context[0], | ||
1607 | &c->sid[0]); | ||
1608 | if (rc) | ||
1609 | goto out; | ||
1610 | } | ||
1611 | |||
1612 | *sid = c->sid[0]; | ||
1613 | out: | ||
1614 | POLICY_RDUNLOCK; | ||
1615 | return rc; | ||
1616 | } | ||
1617 | |||
1618 | /** | ||
1619 | * security_fs_use - Determine how to handle labeling for a filesystem. | ||
1620 | * @fstype: filesystem type | ||
1621 | * @behavior: labeling behavior | ||
1622 | * @sid: SID for filesystem (superblock) | ||
1623 | */ | ||
1624 | int security_fs_use( | ||
1625 | const char *fstype, | ||
1626 | unsigned int *behavior, | ||
1627 | u32 *sid) | ||
1628 | { | ||
1629 | int rc = 0; | ||
1630 | struct ocontext *c; | ||
1631 | |||
1632 | POLICY_RDLOCK; | ||
1633 | |||
1634 | c = policydb.ocontexts[OCON_FSUSE]; | ||
1635 | while (c) { | ||
1636 | if (strcmp(fstype, c->u.name) == 0) | ||
1637 | break; | ||
1638 | c = c->next; | ||
1639 | } | ||
1640 | |||
1641 | if (c) { | ||
1642 | *behavior = c->v.behavior; | ||
1643 | if (!c->sid[0]) { | ||
1644 | rc = sidtab_context_to_sid(&sidtab, | ||
1645 | &c->context[0], | ||
1646 | &c->sid[0]); | ||
1647 | if (rc) | ||
1648 | goto out; | ||
1649 | } | ||
1650 | *sid = c->sid[0]; | ||
1651 | } else { | ||
1652 | rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid); | ||
1653 | if (rc) { | ||
1654 | *behavior = SECURITY_FS_USE_NONE; | ||
1655 | rc = 0; | ||
1656 | } else { | ||
1657 | *behavior = SECURITY_FS_USE_GENFS; | ||
1658 | } | ||
1659 | } | ||
1660 | |||
1661 | out: | ||
1662 | POLICY_RDUNLOCK; | ||
1663 | return rc; | ||
1664 | } | ||
1665 | |||
1666 | int security_get_bools(int *len, char ***names, int **values) | ||
1667 | { | ||
1668 | int i, rc = -ENOMEM; | ||
1669 | |||
1670 | POLICY_RDLOCK; | ||
1671 | *names = NULL; | ||
1672 | *values = NULL; | ||
1673 | |||
1674 | *len = policydb.p_bools.nprim; | ||
1675 | if (!*len) { | ||
1676 | rc = 0; | ||
1677 | goto out; | ||
1678 | } | ||
1679 | |||
1680 | *names = (char**)kmalloc(sizeof(char*) * *len, GFP_ATOMIC); | ||
1681 | if (!*names) | ||
1682 | goto err; | ||
1683 | memset(*names, 0, sizeof(char*) * *len); | ||
1684 | |||
1685 | *values = (int*)kmalloc(sizeof(int) * *len, GFP_ATOMIC); | ||
1686 | if (!*values) | ||
1687 | goto err; | ||
1688 | |||
1689 | for (i = 0; i < *len; i++) { | ||
1690 | size_t name_len; | ||
1691 | (*values)[i] = policydb.bool_val_to_struct[i]->state; | ||
1692 | name_len = strlen(policydb.p_bool_val_to_name[i]) + 1; | ||
1693 | (*names)[i] = (char*)kmalloc(sizeof(char) * name_len, GFP_ATOMIC); | ||
1694 | if (!(*names)[i]) | ||
1695 | goto err; | ||
1696 | strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len); | ||
1697 | (*names)[i][name_len - 1] = 0; | ||
1698 | } | ||
1699 | rc = 0; | ||
1700 | out: | ||
1701 | POLICY_RDUNLOCK; | ||
1702 | return rc; | ||
1703 | err: | ||
1704 | if (*names) { | ||
1705 | for (i = 0; i < *len; i++) | ||
1706 | if ((*names)[i]) | ||
1707 | kfree((*names)[i]); | ||
1708 | } | ||
1709 | if (*values) | ||
1710 | kfree(*values); | ||
1711 | goto out; | ||
1712 | } | ||
1713 | |||
1714 | |||
1715 | int security_set_bools(int len, int *values) | ||
1716 | { | ||
1717 | int i, rc = 0; | ||
1718 | int lenp, seqno = 0; | ||
1719 | struct cond_node *cur; | ||
1720 | |||
1721 | POLICY_WRLOCK; | ||
1722 | |||
1723 | lenp = policydb.p_bools.nprim; | ||
1724 | if (len != lenp) { | ||
1725 | rc = -EFAULT; | ||
1726 | goto out; | ||
1727 | } | ||
1728 | |||
1729 | printk(KERN_INFO "security: committed booleans { "); | ||
1730 | for (i = 0; i < len; i++) { | ||
1731 | if (values[i]) { | ||
1732 | policydb.bool_val_to_struct[i]->state = 1; | ||
1733 | } else { | ||
1734 | policydb.bool_val_to_struct[i]->state = 0; | ||
1735 | } | ||
1736 | if (i != 0) | ||
1737 | printk(", "); | ||
1738 | printk("%s:%d", policydb.p_bool_val_to_name[i], | ||
1739 | policydb.bool_val_to_struct[i]->state); | ||
1740 | } | ||
1741 | printk(" }\n"); | ||
1742 | |||
1743 | for (cur = policydb.cond_list; cur != NULL; cur = cur->next) { | ||
1744 | rc = evaluate_cond_node(&policydb, cur); | ||
1745 | if (rc) | ||
1746 | goto out; | ||
1747 | } | ||
1748 | |||
1749 | seqno = ++latest_granting; | ||
1750 | |||
1751 | out: | ||
1752 | POLICY_WRUNLOCK; | ||
1753 | if (!rc) { | ||
1754 | avc_ss_reset(seqno); | ||
1755 | selnl_notify_policyload(seqno); | ||
1756 | } | ||
1757 | return rc; | ||
1758 | } | ||
1759 | |||
1760 | int security_get_bool_value(int bool) | ||
1761 | { | ||
1762 | int rc = 0; | ||
1763 | int len; | ||
1764 | |||
1765 | POLICY_RDLOCK; | ||
1766 | |||
1767 | len = policydb.p_bools.nprim; | ||
1768 | if (bool >= len) { | ||
1769 | rc = -EFAULT; | ||
1770 | goto out; | ||
1771 | } | ||
1772 | |||
1773 | rc = policydb.bool_val_to_struct[bool]->state; | ||
1774 | out: | ||
1775 | POLICY_RDUNLOCK; | ||
1776 | return rc; | ||
1777 | } | ||
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h new file mode 100644 index 000000000000..e8d907e903cd --- /dev/null +++ b/security/selinux/ss/services.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * Implementation of the security services. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #ifndef _SS_SERVICES_H_ | ||
7 | #define _SS_SERVICES_H_ | ||
8 | |||
9 | #include "policydb.h" | ||
10 | #include "sidtab.h" | ||
11 | |||
12 | extern struct policydb policydb; | ||
13 | |||
14 | #endif /* _SS_SERVICES_H_ */ | ||
15 | |||
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c new file mode 100644 index 000000000000..871c33bd0741 --- /dev/null +++ b/security/selinux/ss/sidtab.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * Implementation of the SID table type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <linux/spinlock.h> | ||
9 | #include <linux/errno.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include "flask.h" | ||
12 | #include "security.h" | ||
13 | #include "sidtab.h" | ||
14 | |||
15 | #define SIDTAB_HASH(sid) \ | ||
16 | (sid & SIDTAB_HASH_MASK) | ||
17 | |||
18 | #define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock) | ||
19 | #define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x) | ||
20 | #define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x) | ||
21 | |||
22 | int sidtab_init(struct sidtab *s) | ||
23 | { | ||
24 | int i; | ||
25 | |||
26 | s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC); | ||
27 | if (!s->htable) | ||
28 | return -ENOMEM; | ||
29 | for (i = 0; i < SIDTAB_SIZE; i++) | ||
30 | s->htable[i] = NULL; | ||
31 | s->nel = 0; | ||
32 | s->next_sid = 1; | ||
33 | s->shutdown = 0; | ||
34 | INIT_SIDTAB_LOCK(s); | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) | ||
39 | { | ||
40 | int hvalue, rc = 0; | ||
41 | struct sidtab_node *prev, *cur, *newnode; | ||
42 | |||
43 | if (!s) { | ||
44 | rc = -ENOMEM; | ||
45 | goto out; | ||
46 | } | ||
47 | |||
48 | hvalue = SIDTAB_HASH(sid); | ||
49 | prev = NULL; | ||
50 | cur = s->htable[hvalue]; | ||
51 | while (cur != NULL && sid > cur->sid) { | ||
52 | prev = cur; | ||
53 | cur = cur->next; | ||
54 | } | ||
55 | |||
56 | if (cur && sid == cur->sid) { | ||
57 | rc = -EEXIST; | ||
58 | goto out; | ||
59 | } | ||
60 | |||
61 | newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC); | ||
62 | if (newnode == NULL) { | ||
63 | rc = -ENOMEM; | ||
64 | goto out; | ||
65 | } | ||
66 | newnode->sid = sid; | ||
67 | if (context_cpy(&newnode->context, context)) { | ||
68 | kfree(newnode); | ||
69 | rc = -ENOMEM; | ||
70 | goto out; | ||
71 | } | ||
72 | |||
73 | if (prev) { | ||
74 | newnode->next = prev->next; | ||
75 | wmb(); | ||
76 | prev->next = newnode; | ||
77 | } else { | ||
78 | newnode->next = s->htable[hvalue]; | ||
79 | wmb(); | ||
80 | s->htable[hvalue] = newnode; | ||
81 | } | ||
82 | |||
83 | s->nel++; | ||
84 | if (sid >= s->next_sid) | ||
85 | s->next_sid = sid + 1; | ||
86 | out: | ||
87 | return rc; | ||
88 | } | ||
89 | |||
90 | struct context *sidtab_search(struct sidtab *s, u32 sid) | ||
91 | { | ||
92 | int hvalue; | ||
93 | struct sidtab_node *cur; | ||
94 | |||
95 | if (!s) | ||
96 | return NULL; | ||
97 | |||
98 | hvalue = SIDTAB_HASH(sid); | ||
99 | cur = s->htable[hvalue]; | ||
100 | while (cur != NULL && sid > cur->sid) | ||
101 | cur = cur->next; | ||
102 | |||
103 | if (cur == NULL || sid != cur->sid) { | ||
104 | /* Remap invalid SIDs to the unlabeled SID. */ | ||
105 | sid = SECINITSID_UNLABELED; | ||
106 | hvalue = SIDTAB_HASH(sid); | ||
107 | cur = s->htable[hvalue]; | ||
108 | while (cur != NULL && sid > cur->sid) | ||
109 | cur = cur->next; | ||
110 | if (!cur || sid != cur->sid) | ||
111 | return NULL; | ||
112 | } | ||
113 | |||
114 | return &cur->context; | ||
115 | } | ||
116 | |||
117 | int sidtab_map(struct sidtab *s, | ||
118 | int (*apply) (u32 sid, | ||
119 | struct context *context, | ||
120 | void *args), | ||
121 | void *args) | ||
122 | { | ||
123 | int i, rc = 0; | ||
124 | struct sidtab_node *cur; | ||
125 | |||
126 | if (!s) | ||
127 | goto out; | ||
128 | |||
129 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
130 | cur = s->htable[i]; | ||
131 | while (cur != NULL) { | ||
132 | rc = apply(cur->sid, &cur->context, args); | ||
133 | if (rc) | ||
134 | goto out; | ||
135 | cur = cur->next; | ||
136 | } | ||
137 | } | ||
138 | out: | ||
139 | return rc; | ||
140 | } | ||
141 | |||
142 | void sidtab_map_remove_on_error(struct sidtab *s, | ||
143 | int (*apply) (u32 sid, | ||
144 | struct context *context, | ||
145 | void *args), | ||
146 | void *args) | ||
147 | { | ||
148 | int i, ret; | ||
149 | struct sidtab_node *last, *cur, *temp; | ||
150 | |||
151 | if (!s) | ||
152 | return; | ||
153 | |||
154 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
155 | last = NULL; | ||
156 | cur = s->htable[i]; | ||
157 | while (cur != NULL) { | ||
158 | ret = apply(cur->sid, &cur->context, args); | ||
159 | if (ret) { | ||
160 | if (last) { | ||
161 | last->next = cur->next; | ||
162 | } else { | ||
163 | s->htable[i] = cur->next; | ||
164 | } | ||
165 | |||
166 | temp = cur; | ||
167 | cur = cur->next; | ||
168 | context_destroy(&temp->context); | ||
169 | kfree(temp); | ||
170 | s->nel--; | ||
171 | } else { | ||
172 | last = cur; | ||
173 | cur = cur->next; | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||
178 | return; | ||
179 | } | ||
180 | |||
181 | static inline u32 sidtab_search_context(struct sidtab *s, | ||
182 | struct context *context) | ||
183 | { | ||
184 | int i; | ||
185 | struct sidtab_node *cur; | ||
186 | |||
187 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
188 | cur = s->htable[i]; | ||
189 | while (cur != NULL) { | ||
190 | if (context_cmp(&cur->context, context)) | ||
191 | return cur->sid; | ||
192 | cur = cur->next; | ||
193 | } | ||
194 | } | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | int sidtab_context_to_sid(struct sidtab *s, | ||
199 | struct context *context, | ||
200 | u32 *out_sid) | ||
201 | { | ||
202 | u32 sid; | ||
203 | int ret = 0; | ||
204 | unsigned long flags; | ||
205 | |||
206 | *out_sid = SECSID_NULL; | ||
207 | |||
208 | sid = sidtab_search_context(s, context); | ||
209 | if (!sid) { | ||
210 | SIDTAB_LOCK(s, flags); | ||
211 | /* Rescan now that we hold the lock. */ | ||
212 | sid = sidtab_search_context(s, context); | ||
213 | if (sid) | ||
214 | goto unlock_out; | ||
215 | /* No SID exists for the context. Allocate a new one. */ | ||
216 | if (s->next_sid == UINT_MAX || s->shutdown) { | ||
217 | ret = -ENOMEM; | ||
218 | goto unlock_out; | ||
219 | } | ||
220 | sid = s->next_sid++; | ||
221 | ret = sidtab_insert(s, sid, context); | ||
222 | if (ret) | ||
223 | s->next_sid--; | ||
224 | unlock_out: | ||
225 | SIDTAB_UNLOCK(s, flags); | ||
226 | } | ||
227 | |||
228 | if (ret) | ||
229 | return ret; | ||
230 | |||
231 | *out_sid = sid; | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | void sidtab_hash_eval(struct sidtab *h, char *tag) | ||
236 | { | ||
237 | int i, chain_len, slots_used, max_chain_len; | ||
238 | struct sidtab_node *cur; | ||
239 | |||
240 | slots_used = 0; | ||
241 | max_chain_len = 0; | ||
242 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
243 | cur = h->htable[i]; | ||
244 | if (cur) { | ||
245 | slots_used++; | ||
246 | chain_len = 0; | ||
247 | while (cur) { | ||
248 | chain_len++; | ||
249 | cur = cur->next; | ||
250 | } | ||
251 | |||
252 | if (chain_len > max_chain_len) | ||
253 | max_chain_len = chain_len; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest " | ||
258 | "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, | ||
259 | max_chain_len); | ||
260 | } | ||
261 | |||
262 | void sidtab_destroy(struct sidtab *s) | ||
263 | { | ||
264 | int i; | ||
265 | struct sidtab_node *cur, *temp; | ||
266 | |||
267 | if (!s) | ||
268 | return; | ||
269 | |||
270 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
271 | cur = s->htable[i]; | ||
272 | while (cur != NULL) { | ||
273 | temp = cur; | ||
274 | cur = cur->next; | ||
275 | context_destroy(&temp->context); | ||
276 | kfree(temp); | ||
277 | } | ||
278 | s->htable[i] = NULL; | ||
279 | } | ||
280 | kfree(s->htable); | ||
281 | s->htable = NULL; | ||
282 | s->nel = 0; | ||
283 | s->next_sid = 1; | ||
284 | } | ||
285 | |||
286 | void sidtab_set(struct sidtab *dst, struct sidtab *src) | ||
287 | { | ||
288 | unsigned long flags; | ||
289 | |||
290 | SIDTAB_LOCK(src, flags); | ||
291 | dst->htable = src->htable; | ||
292 | dst->nel = src->nel; | ||
293 | dst->next_sid = src->next_sid; | ||
294 | dst->shutdown = 0; | ||
295 | SIDTAB_UNLOCK(src, flags); | ||
296 | } | ||
297 | |||
298 | void sidtab_shutdown(struct sidtab *s) | ||
299 | { | ||
300 | unsigned long flags; | ||
301 | |||
302 | SIDTAB_LOCK(s, flags); | ||
303 | s->shutdown = 1; | ||
304 | SIDTAB_UNLOCK(s, flags); | ||
305 | } | ||
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h new file mode 100644 index 000000000000..2fe9dfa3eb3a --- /dev/null +++ b/security/selinux/ss/sidtab.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * A security identifier table (sidtab) is a hash table | ||
3 | * of security context structures indexed by SID value. | ||
4 | * | ||
5 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
6 | */ | ||
7 | #ifndef _SS_SIDTAB_H_ | ||
8 | #define _SS_SIDTAB_H_ | ||
9 | |||
10 | #include "context.h" | ||
11 | |||
12 | struct sidtab_node { | ||
13 | u32 sid; /* security identifier */ | ||
14 | struct context context; /* security context structure */ | ||
15 | struct sidtab_node *next; | ||
16 | }; | ||
17 | |||
18 | #define SIDTAB_HASH_BITS 7 | ||
19 | #define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS) | ||
20 | #define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1) | ||
21 | |||
22 | #define SIDTAB_SIZE SIDTAB_HASH_BUCKETS | ||
23 | |||
24 | struct sidtab { | ||
25 | struct sidtab_node **htable; | ||
26 | unsigned int nel; /* number of elements */ | ||
27 | unsigned int next_sid; /* next SID to allocate */ | ||
28 | unsigned char shutdown; | ||
29 | spinlock_t lock; | ||
30 | }; | ||
31 | |||
32 | int sidtab_init(struct sidtab *s); | ||
33 | int sidtab_insert(struct sidtab *s, u32 sid, struct context *context); | ||
34 | struct context *sidtab_search(struct sidtab *s, u32 sid); | ||
35 | |||
36 | int sidtab_map(struct sidtab *s, | ||
37 | int (*apply) (u32 sid, | ||
38 | struct context *context, | ||
39 | void *args), | ||
40 | void *args); | ||
41 | |||
42 | void sidtab_map_remove_on_error(struct sidtab *s, | ||
43 | int (*apply) (u32 sid, | ||
44 | struct context *context, | ||
45 | void *args), | ||
46 | void *args); | ||
47 | |||
48 | int sidtab_context_to_sid(struct sidtab *s, | ||
49 | struct context *context, | ||
50 | u32 *sid); | ||
51 | |||
52 | void sidtab_hash_eval(struct sidtab *h, char *tag); | ||
53 | void sidtab_destroy(struct sidtab *s); | ||
54 | void sidtab_set(struct sidtab *dst, struct sidtab *src); | ||
55 | void sidtab_shutdown(struct sidtab *s); | ||
56 | |||
57 | #endif /* _SS_SIDTAB_H_ */ | ||
58 | |||
59 | |||
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c new file mode 100644 index 000000000000..24a10d36d3b6 --- /dev/null +++ b/security/selinux/ss/symtab.c | |||
@@ -0,0 +1,44 @@ | |||
1 | /* | ||
2 | * Implementation of the symbol table type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <linux/string.h> | ||
9 | #include <linux/errno.h> | ||
10 | #include "symtab.h" | ||
11 | |||
12 | static unsigned int symhash(struct hashtab *h, void *key) | ||
13 | { | ||
14 | char *p, *keyp; | ||
15 | unsigned int size; | ||
16 | unsigned int val; | ||
17 | |||
18 | val = 0; | ||
19 | keyp = key; | ||
20 | size = strlen(keyp); | ||
21 | for (p = keyp; (p - keyp) < size; p++) | ||
22 | val = (val << 4 | (val >> (8*sizeof(unsigned int)-4))) ^ (*p); | ||
23 | return val & (h->size - 1); | ||
24 | } | ||
25 | |||
26 | static int symcmp(struct hashtab *h, void *key1, void *key2) | ||
27 | { | ||
28 | char *keyp1, *keyp2; | ||
29 | |||
30 | keyp1 = key1; | ||
31 | keyp2 = key2; | ||
32 | return strcmp(keyp1, keyp2); | ||
33 | } | ||
34 | |||
35 | |||
36 | int symtab_init(struct symtab *s, unsigned int size) | ||
37 | { | ||
38 | s->table = hashtab_create(symhash, symcmp, size); | ||
39 | if (!s->table) | ||
40 | return -1; | ||
41 | s->nprim = 0; | ||
42 | return 0; | ||
43 | } | ||
44 | |||
diff --git a/security/selinux/ss/symtab.h b/security/selinux/ss/symtab.h new file mode 100644 index 000000000000..ca422b42fbc0 --- /dev/null +++ b/security/selinux/ss/symtab.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * A symbol table (symtab) maintains associations between symbol | ||
3 | * strings and datum values. The type of the datum values | ||
4 | * is arbitrary. The symbol table type is implemented | ||
5 | * using the hash table type (hashtab). | ||
6 | * | ||
7 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
8 | */ | ||
9 | #ifndef _SS_SYMTAB_H_ | ||
10 | #define _SS_SYMTAB_H_ | ||
11 | |||
12 | #include "hashtab.h" | ||
13 | |||
14 | struct symtab { | ||
15 | struct hashtab *table; /* hash table (keyed on a string) */ | ||
16 | u32 nprim; /* number of primary names in table */ | ||
17 | }; | ||
18 | |||
19 | int symtab_init(struct symtab *s, unsigned int size); | ||
20 | |||
21 | #endif /* _SS_SYMTAB_H_ */ | ||
22 | |||
23 | |||