diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /security/selinux/ss | |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
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 | |||
