diff options
author | Stephen Smalley <sds@tycho.nsa.gov> | 2007-11-07 10:08:00 -0500 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2007-11-07 16:56:23 -0500 |
commit | 45e5421eb5bbcd9efa037d682dd357284e3ef982 (patch) | |
tree | ceb24143024fe335d08ac30fb4da9ca25fbeb6e6 | |
parent | 6d2b685564ba417f4c6d80c3661f0dfee13fff85 (diff) |
SELinux: add more validity checks on policy load
Add more validity checks at policy load time to reject malformed
policies and prevent subsequent out-of-range indexing when in permissive
mode. Resolves the NULL pointer dereference reported in
https://bugzilla.redhat.com/show_bug.cgi?id=357541.
Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
-rw-r--r-- | security/selinux/ss/avtab.c | 32 | ||||
-rw-r--r-- | security/selinux/ss/avtab.h | 5 | ||||
-rw-r--r-- | security/selinux/ss/conditional.c | 3 | ||||
-rw-r--r-- | security/selinux/ss/mls.c | 66 | ||||
-rw-r--r-- | security/selinux/ss/mls.h | 2 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 45 | ||||
-rw-r--r-- | security/selinux/ss/policydb.h | 3 |
7 files changed, 118 insertions, 38 deletions
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 7551af1f7899..9e70a160d7da 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c | |||
@@ -325,7 +325,7 @@ static uint16_t spec_order[] = { | |||
325 | AVTAB_MEMBER | 325 | AVTAB_MEMBER |
326 | }; | 326 | }; |
327 | 327 | ||
328 | int avtab_read_item(void *fp, u32 vers, struct avtab *a, | 328 | int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, |
329 | int (*insertf)(struct avtab *a, struct avtab_key *k, | 329 | int (*insertf)(struct avtab *a, struct avtab_key *k, |
330 | struct avtab_datum *d, void *p), | 330 | struct avtab_datum *d, void *p), |
331 | void *p) | 331 | void *p) |
@@ -333,10 +333,11 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a, | |||
333 | __le16 buf16[4]; | 333 | __le16 buf16[4]; |
334 | u16 enabled; | 334 | u16 enabled; |
335 | __le32 buf32[7]; | 335 | __le32 buf32[7]; |
336 | u32 items, items2, val; | 336 | u32 items, items2, val, vers = pol->policyvers; |
337 | struct avtab_key key; | 337 | struct avtab_key key; |
338 | struct avtab_datum datum; | 338 | struct avtab_datum datum; |
339 | int i, rc; | 339 | int i, rc; |
340 | unsigned set; | ||
340 | 341 | ||
341 | memset(&key, 0, sizeof(struct avtab_key)); | 342 | memset(&key, 0, sizeof(struct avtab_key)); |
342 | memset(&datum, 0, sizeof(struct avtab_datum)); | 343 | memset(&datum, 0, sizeof(struct avtab_datum)); |
@@ -420,12 +421,35 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a, | |||
420 | key.target_class = le16_to_cpu(buf16[items++]); | 421 | key.target_class = le16_to_cpu(buf16[items++]); |
421 | key.specified = le16_to_cpu(buf16[items++]); | 422 | key.specified = le16_to_cpu(buf16[items++]); |
422 | 423 | ||
424 | if (!policydb_type_isvalid(pol, key.source_type) || | ||
425 | !policydb_type_isvalid(pol, key.target_type) || | ||
426 | !policydb_class_isvalid(pol, key.target_class)) { | ||
427 | printk(KERN_WARNING "security: avtab: invalid type or class\n"); | ||
428 | return -1; | ||
429 | } | ||
430 | |||
431 | set = 0; | ||
432 | for (i = 0; i < ARRAY_SIZE(spec_order); i++) { | ||
433 | if (key.specified & spec_order[i]) | ||
434 | set++; | ||
435 | } | ||
436 | if (!set || set > 1) { | ||
437 | printk(KERN_WARNING | ||
438 | "security: avtab: more than one specifier\n"); | ||
439 | return -1; | ||
440 | } | ||
441 | |||
423 | rc = next_entry(buf32, fp, sizeof(u32)); | 442 | rc = next_entry(buf32, fp, sizeof(u32)); |
424 | if (rc < 0) { | 443 | if (rc < 0) { |
425 | printk("security: avtab: truncated entry\n"); | 444 | printk("security: avtab: truncated entry\n"); |
426 | return -1; | 445 | return -1; |
427 | } | 446 | } |
428 | datum.data = le32_to_cpu(*buf32); | 447 | datum.data = le32_to_cpu(*buf32); |
448 | if ((key.specified & AVTAB_TYPE) && | ||
449 | !policydb_type_isvalid(pol, datum.data)) { | ||
450 | printk(KERN_WARNING "security: avtab: invalid type\n"); | ||
451 | return -1; | ||
452 | } | ||
429 | return insertf(a, &key, &datum, p); | 453 | return insertf(a, &key, &datum, p); |
430 | } | 454 | } |
431 | 455 | ||
@@ -435,7 +459,7 @@ static int avtab_insertf(struct avtab *a, struct avtab_key *k, | |||
435 | return avtab_insert(a, k, d); | 459 | return avtab_insert(a, k, d); |
436 | } | 460 | } |
437 | 461 | ||
438 | int avtab_read(struct avtab *a, void *fp, u32 vers) | 462 | int avtab_read(struct avtab *a, void *fp, struct policydb *pol) |
439 | { | 463 | { |
440 | int rc; | 464 | int rc; |
441 | __le32 buf[1]; | 465 | __le32 buf[1]; |
@@ -459,7 +483,7 @@ int avtab_read(struct avtab *a, void *fp, u32 vers) | |||
459 | goto bad; | 483 | goto bad; |
460 | 484 | ||
461 | for (i = 0; i < nel; i++) { | 485 | for (i = 0; i < nel; i++) { |
462 | rc = avtab_read_item(fp,vers, a, avtab_insertf, NULL); | 486 | rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL); |
463 | if (rc) { | 487 | if (rc) { |
464 | if (rc == -ENOMEM) | 488 | if (rc == -ENOMEM) |
465 | printk(KERN_ERR "security: avtab: out of memory\n"); | 489 | printk(KERN_ERR "security: avtab: out of memory\n"); |
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index d8edf8ca56d1..8da6a8428086 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h | |||
@@ -64,12 +64,13 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k); | |||
64 | void avtab_destroy(struct avtab *h); | 64 | void avtab_destroy(struct avtab *h); |
65 | void avtab_hash_eval(struct avtab *h, char *tag); | 65 | void avtab_hash_eval(struct avtab *h, char *tag); |
66 | 66 | ||
67 | int avtab_read_item(void *fp, uint32_t vers, struct avtab *a, | 67 | struct policydb; |
68 | int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | ||
68 | int (*insert)(struct avtab *a, struct avtab_key *k, | 69 | int (*insert)(struct avtab *a, struct avtab_key *k, |
69 | struct avtab_datum *d, void *p), | 70 | struct avtab_datum *d, void *p), |
70 | void *p); | 71 | void *p); |
71 | 72 | ||
72 | int avtab_read(struct avtab *a, void *fp, u32 vers); | 73 | int avtab_read(struct avtab *a, void *fp, struct policydb *pol); |
73 | 74 | ||
74 | struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, | 75 | struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, |
75 | struct avtab_datum *datum); | 76 | struct avtab_datum *datum); |
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 45b93a827c80..50ad85d4b77c 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c | |||
@@ -362,7 +362,8 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * | |||
362 | data.head = NULL; | 362 | data.head = NULL; |
363 | data.tail = NULL; | 363 | data.tail = NULL; |
364 | for (i = 0; i < len; i++) { | 364 | for (i = 0; i < len; i++) { |
365 | rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab, cond_insertf, &data); | 365 | rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf, |
366 | &data); | ||
366 | if (rc) | 367 | if (rc) |
367 | return rc; | 368 | return rc; |
368 | 369 | ||
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 9a11deaaa9e7..fb5d70a6628d 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c | |||
@@ -157,49 +157,55 @@ void mls_sid_to_context(struct context *context, | |||
157 | return; | 157 | return; |
158 | } | 158 | } |
159 | 159 | ||
160 | int mls_level_isvalid(struct policydb *p, struct mls_level *l) | ||
161 | { | ||
162 | struct level_datum *levdatum; | ||
163 | struct ebitmap_node *node; | ||
164 | int i; | ||
165 | |||
166 | if (!l->sens || l->sens > p->p_levels.nprim) | ||
167 | return 0; | ||
168 | levdatum = hashtab_search(p->p_levels.table, | ||
169 | p->p_sens_val_to_name[l->sens - 1]); | ||
170 | if (!levdatum) | ||
171 | return 0; | ||
172 | |||
173 | ebitmap_for_each_positive_bit(&l->cat, node, i) { | ||
174 | if (i > p->p_cats.nprim) | ||
175 | return 0; | ||
176 | if (!ebitmap_get_bit(&levdatum->level->cat, i)) { | ||
177 | /* | ||
178 | * Category may not be associated with | ||
179 | * sensitivity. | ||
180 | */ | ||
181 | return 0; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | return 1; | ||
186 | } | ||
187 | |||
188 | int mls_range_isvalid(struct policydb *p, struct mls_range *r) | ||
189 | { | ||
190 | return (mls_level_isvalid(p, &r->level[0]) && | ||
191 | mls_level_isvalid(p, &r->level[1]) && | ||
192 | mls_level_dom(&r->level[1], &r->level[0])); | ||
193 | } | ||
194 | |||
160 | /* | 195 | /* |
161 | * Return 1 if the MLS fields in the security context | 196 | * Return 1 if the MLS fields in the security context |
162 | * structure `c' are valid. Return 0 otherwise. | 197 | * structure `c' are valid. Return 0 otherwise. |
163 | */ | 198 | */ |
164 | int mls_context_isvalid(struct policydb *p, struct context *c) | 199 | int mls_context_isvalid(struct policydb *p, struct context *c) |
165 | { | 200 | { |
166 | struct level_datum *levdatum; | ||
167 | struct user_datum *usrdatum; | 201 | struct user_datum *usrdatum; |
168 | struct ebitmap_node *node; | ||
169 | int i, l; | ||
170 | 202 | ||
171 | if (!selinux_mls_enabled) | 203 | if (!selinux_mls_enabled) |
172 | return 1; | 204 | return 1; |
173 | 205 | ||
174 | /* | 206 | if (!mls_range_isvalid(p, &c->range)) |
175 | * MLS range validity checks: high must dominate low, low level must | ||
176 | * be valid (category set <-> sensitivity check), and high level must | ||
177 | * be valid (category set <-> sensitivity check) | ||
178 | */ | ||
179 | if (!mls_level_dom(&c->range.level[1], &c->range.level[0])) | ||
180 | /* High does not dominate low. */ | ||
181 | return 0; | 207 | return 0; |
182 | 208 | ||
183 | for (l = 0; l < 2; l++) { | ||
184 | if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim) | ||
185 | return 0; | ||
186 | levdatum = hashtab_search(p->p_levels.table, | ||
187 | p->p_sens_val_to_name[c->range.level[l].sens - 1]); | ||
188 | if (!levdatum) | ||
189 | return 0; | ||
190 | |||
191 | ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) { | ||
192 | if (i > p->p_cats.nprim) | ||
193 | return 0; | ||
194 | if (!ebitmap_get_bit(&levdatum->level->cat, i)) | ||
195 | /* | ||
196 | * Category may not be associated with | ||
197 | * sensitivity in low level. | ||
198 | */ | ||
199 | return 0; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | if (c->role == OBJECT_R_VAL) | 209 | if (c->role == OBJECT_R_VAL) |
204 | return 1; | 210 | return 1; |
205 | 211 | ||
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 096d1b4ef7fb..ab53663d9f5f 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h | |||
@@ -27,6 +27,8 @@ | |||
27 | int mls_compute_context_len(struct context *context); | 27 | int mls_compute_context_len(struct context *context); |
28 | void mls_sid_to_context(struct context *context, char **scontext); | 28 | void mls_sid_to_context(struct context *context, char **scontext); |
29 | int mls_context_isvalid(struct policydb *p, struct context *c); | 29 | int mls_context_isvalid(struct policydb *p, struct context *c); |
30 | int mls_range_isvalid(struct policydb *p, struct mls_range *r); | ||
31 | int mls_level_isvalid(struct policydb *p, struct mls_level *l); | ||
30 | 32 | ||
31 | int mls_context_to_sid(char oldc, | 33 | int mls_context_to_sid(char oldc, |
32 | char **scontext, | 34 | char **scontext, |
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 539828b229b2..b582aae3c62c 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c | |||
@@ -713,6 +713,27 @@ out: | |||
713 | return rc; | 713 | return rc; |
714 | } | 714 | } |
715 | 715 | ||
716 | int policydb_class_isvalid(struct policydb *p, unsigned int class) | ||
717 | { | ||
718 | if (!class || class > p->p_classes.nprim) | ||
719 | return 0; | ||
720 | return 1; | ||
721 | } | ||
722 | |||
723 | int policydb_role_isvalid(struct policydb *p, unsigned int role) | ||
724 | { | ||
725 | if (!role || role > p->p_roles.nprim) | ||
726 | return 0; | ||
727 | return 1; | ||
728 | } | ||
729 | |||
730 | int policydb_type_isvalid(struct policydb *p, unsigned int type) | ||
731 | { | ||
732 | if (!type || type > p->p_types.nprim) | ||
733 | return 0; | ||
734 | return 1; | ||
735 | } | ||
736 | |||
716 | /* | 737 | /* |
717 | * Return 1 if the fields in the security context | 738 | * Return 1 if the fields in the security context |
718 | * structure `c' are valid. Return 0 otherwise. | 739 | * structure `c' are valid. Return 0 otherwise. |
@@ -1260,6 +1281,7 @@ static int mls_read_level(struct mls_level *lp, void *fp) | |||
1260 | "categories\n"); | 1281 | "categories\n"); |
1261 | goto bad; | 1282 | goto bad; |
1262 | } | 1283 | } |
1284 | |||
1263 | return 0; | 1285 | return 0; |
1264 | 1286 | ||
1265 | bad: | 1287 | bad: |
@@ -1563,7 +1585,7 @@ int policydb_read(struct policydb *p, void *fp) | |||
1563 | p->symtab[i].nprim = nprim; | 1585 | p->symtab[i].nprim = nprim; |
1564 | } | 1586 | } |
1565 | 1587 | ||
1566 | rc = avtab_read(&p->te_avtab, fp, p->policyvers); | 1588 | rc = avtab_read(&p->te_avtab, fp, p); |
1567 | if (rc) | 1589 | if (rc) |
1568 | goto bad; | 1590 | goto bad; |
1569 | 1591 | ||
@@ -1595,6 +1617,12 @@ int policydb_read(struct policydb *p, void *fp) | |||
1595 | tr->role = le32_to_cpu(buf[0]); | 1617 | tr->role = le32_to_cpu(buf[0]); |
1596 | tr->type = le32_to_cpu(buf[1]); | 1618 | tr->type = le32_to_cpu(buf[1]); |
1597 | tr->new_role = le32_to_cpu(buf[2]); | 1619 | tr->new_role = le32_to_cpu(buf[2]); |
1620 | if (!policydb_role_isvalid(p, tr->role) || | ||
1621 | !policydb_type_isvalid(p, tr->type) || | ||
1622 | !policydb_role_isvalid(p, tr->new_role)) { | ||
1623 | rc = -EINVAL; | ||
1624 | goto bad; | ||
1625 | } | ||
1598 | ltr = tr; | 1626 | ltr = tr; |
1599 | } | 1627 | } |
1600 | 1628 | ||
@@ -1619,6 +1647,11 @@ int policydb_read(struct policydb *p, void *fp) | |||
1619 | goto bad; | 1647 | goto bad; |
1620 | ra->role = le32_to_cpu(buf[0]); | 1648 | ra->role = le32_to_cpu(buf[0]); |
1621 | ra->new_role = le32_to_cpu(buf[1]); | 1649 | ra->new_role = le32_to_cpu(buf[1]); |
1650 | if (!policydb_role_isvalid(p, ra->role) || | ||
1651 | !policydb_role_isvalid(p, ra->new_role)) { | ||
1652 | rc = -EINVAL; | ||
1653 | goto bad; | ||
1654 | } | ||
1622 | lra = ra; | 1655 | lra = ra; |
1623 | } | 1656 | } |
1624 | 1657 | ||
@@ -1872,9 +1905,19 @@ int policydb_read(struct policydb *p, void *fp) | |||
1872 | rt->target_class = le32_to_cpu(buf[0]); | 1905 | rt->target_class = le32_to_cpu(buf[0]); |
1873 | } else | 1906 | } else |
1874 | rt->target_class = SECCLASS_PROCESS; | 1907 | rt->target_class = SECCLASS_PROCESS; |
1908 | if (!policydb_type_isvalid(p, rt->source_type) || | ||
1909 | !policydb_type_isvalid(p, rt->target_type) || | ||
1910 | !policydb_class_isvalid(p, rt->target_class)) { | ||
1911 | rc = -EINVAL; | ||
1912 | goto bad; | ||
1913 | } | ||
1875 | rc = mls_read_range_helper(&rt->target_range, fp); | 1914 | rc = mls_read_range_helper(&rt->target_range, fp); |
1876 | if (rc) | 1915 | if (rc) |
1877 | goto bad; | 1916 | goto bad; |
1917 | if (!mls_range_isvalid(p, &rt->target_range)) { | ||
1918 | printk(KERN_WARNING "security: rangetrans: invalid range\n"); | ||
1919 | goto bad; | ||
1920 | } | ||
1878 | lrt = rt; | 1921 | lrt = rt; |
1879 | } | 1922 | } |
1880 | } | 1923 | } |
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 844d310f4f1b..ed6fc687c66f 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h | |||
@@ -251,6 +251,9 @@ struct policydb { | |||
251 | extern void policydb_destroy(struct policydb *p); | 251 | extern void policydb_destroy(struct policydb *p); |
252 | extern int policydb_load_isids(struct policydb *p, struct sidtab *s); | 252 | extern int policydb_load_isids(struct policydb *p, struct sidtab *s); |
253 | extern int policydb_context_isvalid(struct policydb *p, struct context *c); | 253 | extern int policydb_context_isvalid(struct policydb *p, struct context *c); |
254 | extern int policydb_class_isvalid(struct policydb *p, unsigned int class); | ||
255 | extern int policydb_type_isvalid(struct policydb *p, unsigned int type); | ||
256 | extern int policydb_role_isvalid(struct policydb *p, unsigned int role); | ||
254 | extern int policydb_read(struct policydb *p, void *fp); | 257 | extern int policydb_read(struct policydb *p, void *fp); |
255 | 258 | ||
256 | #define PERM_SYMTAB_SIZE 32 | 259 | #define PERM_SYMTAB_SIZE 32 |