diff options
author | Stephen Smalley <sds@tycho.nsa.gov> | 2010-01-07 15:55:16 -0500 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2010-01-24 16:29:05 -0500 |
commit | 2f3e82d694d3d7a2db019db1bb63385fbc1066f3 (patch) | |
tree | 9d99a883eb2ab097a3ff1ee4e1c9bf2fa851d832 /security/selinux | |
parent | 2457552d1e6f3183cd93f81c49a8da5fe8bb0e42 (diff) |
selinux: convert range transition list to a hashtab
Per https://bugzilla.redhat.com/show_bug.cgi?id=548145
there are sufficient range transition rules in modern (Fedora) policy to
make mls_compute_sid a significant factor on the shmem file setup path
due to the length of the range_tr list. Replace the simple range_tr
list with a hashtab inside the security server to help mitigate this
problem.
Signed-off-by: Stephen D. Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/ss/mls.c | 18 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 103 | ||||
-rw-r--r-- | security/selinux/ss/policydb.h | 6 |
3 files changed, 86 insertions, 41 deletions
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index e6654b543aed..443ae7370144 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c | |||
@@ -513,7 +513,8 @@ int mls_compute_sid(struct context *scontext, | |||
513 | u32 specified, | 513 | u32 specified, |
514 | struct context *newcontext) | 514 | struct context *newcontext) |
515 | { | 515 | { |
516 | struct range_trans *rtr; | 516 | struct range_trans rtr; |
517 | struct mls_range *r; | ||
517 | 518 | ||
518 | if (!selinux_mls_enabled) | 519 | if (!selinux_mls_enabled) |
519 | return 0; | 520 | return 0; |
@@ -521,15 +522,12 @@ int mls_compute_sid(struct context *scontext, | |||
521 | switch (specified) { | 522 | switch (specified) { |
522 | case AVTAB_TRANSITION: | 523 | case AVTAB_TRANSITION: |
523 | /* Look for a range transition rule. */ | 524 | /* Look for a range transition rule. */ |
524 | for (rtr = policydb.range_tr; rtr; rtr = rtr->next) { | 525 | rtr.source_type = scontext->type; |
525 | if (rtr->source_type == scontext->type && | 526 | rtr.target_type = tcontext->type; |
526 | rtr->target_type == tcontext->type && | 527 | rtr.target_class = tclass; |
527 | rtr->target_class == tclass) { | 528 | r = hashtab_search(policydb.range_tr, &rtr); |
528 | /* Set the range from the rule */ | 529 | if (r) |
529 | return mls_range_set(newcontext, | 530 | return mls_range_set(newcontext, r); |
530 | &rtr->target_range); | ||
531 | } | ||
532 | } | ||
533 | /* Fallthrough */ | 531 | /* Fallthrough */ |
534 | case AVTAB_CHANGE: | 532 | case AVTAB_CHANGE: |
535 | if (tclass == policydb.process_class) | 533 | if (tclass == policydb.process_class) |
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index f03667213ea8..5b92c0219207 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c | |||
@@ -177,6 +177,21 @@ out_free_role: | |||
177 | goto out; | 177 | goto out; |
178 | } | 178 | } |
179 | 179 | ||
180 | static u32 rangetr_hash(struct hashtab *h, const void *k) | ||
181 | { | ||
182 | const struct range_trans *key = k; | ||
183 | return (key->source_type + (key->target_type << 3) + | ||
184 | (key->target_class << 5)) & (h->size - 1); | ||
185 | } | ||
186 | |||
187 | static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2) | ||
188 | { | ||
189 | const struct range_trans *key1 = k1, *key2 = k2; | ||
190 | return (key1->source_type != key2->source_type || | ||
191 | key1->target_type != key2->target_type || | ||
192 | key1->target_class != key2->target_class); | ||
193 | } | ||
194 | |||
180 | /* | 195 | /* |
181 | * Initialize a policy database structure. | 196 | * Initialize a policy database structure. |
182 | */ | 197 | */ |
@@ -204,6 +219,10 @@ static int policydb_init(struct policydb *p) | |||
204 | if (rc) | 219 | if (rc) |
205 | goto out_free_symtab; | 220 | goto out_free_symtab; |
206 | 221 | ||
222 | p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); | ||
223 | if (!p->range_tr) | ||
224 | goto out_free_symtab; | ||
225 | |||
207 | ebitmap_init(&p->policycaps); | 226 | ebitmap_init(&p->policycaps); |
208 | ebitmap_init(&p->permissive_map); | 227 | ebitmap_init(&p->permissive_map); |
209 | 228 | ||
@@ -408,6 +427,20 @@ static void symtab_hash_eval(struct symtab *s) | |||
408 | info.slots_used, h->size, info.max_chain_len); | 427 | info.slots_used, h->size, info.max_chain_len); |
409 | } | 428 | } |
410 | } | 429 | } |
430 | |||
431 | static void rangetr_hash_eval(struct hashtab *h) | ||
432 | { | ||
433 | struct hashtab_info info; | ||
434 | |||
435 | hashtab_stat(h, &info); | ||
436 | printk(KERN_DEBUG "SELinux: rangetr: %d entries and %d/%d buckets used, " | ||
437 | "longest chain length %d\n", h->nel, | ||
438 | info.slots_used, h->size, info.max_chain_len); | ||
439 | } | ||
440 | #else | ||
441 | static inline void rangetr_hash_eval(struct hashtab *h) | ||
442 | { | ||
443 | } | ||
411 | #endif | 444 | #endif |
412 | 445 | ||
413 | /* | 446 | /* |
@@ -612,6 +645,17 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = | |||
612 | cat_destroy, | 645 | cat_destroy, |
613 | }; | 646 | }; |
614 | 647 | ||
648 | static int range_tr_destroy(void *key, void *datum, void *p) | ||
649 | { | ||
650 | struct mls_range *rt = datum; | ||
651 | kfree(key); | ||
652 | ebitmap_destroy(&rt->level[0].cat); | ||
653 | ebitmap_destroy(&rt->level[1].cat); | ||
654 | kfree(datum); | ||
655 | cond_resched(); | ||
656 | return 0; | ||
657 | } | ||
658 | |||
615 | static void ocontext_destroy(struct ocontext *c, int i) | 659 | static void ocontext_destroy(struct ocontext *c, int i) |
616 | { | 660 | { |
617 | context_destroy(&c->context[0]); | 661 | context_destroy(&c->context[0]); |
@@ -632,7 +676,6 @@ void policydb_destroy(struct policydb *p) | |||
632 | int i; | 676 | int i; |
633 | struct role_allow *ra, *lra = NULL; | 677 | struct role_allow *ra, *lra = NULL; |
634 | struct role_trans *tr, *ltr = NULL; | 678 | struct role_trans *tr, *ltr = NULL; |
635 | struct range_trans *rt, *lrt = NULL; | ||
636 | 679 | ||
637 | for (i = 0; i < SYM_NUM; i++) { | 680 | for (i = 0; i < SYM_NUM; i++) { |
638 | cond_resched(); | 681 | cond_resched(); |
@@ -693,20 +736,8 @@ void policydb_destroy(struct policydb *p) | |||
693 | } | 736 | } |
694 | kfree(lra); | 737 | kfree(lra); |
695 | 738 | ||
696 | for (rt = p->range_tr; rt; rt = rt->next) { | 739 | hashtab_map(p->range_tr, range_tr_destroy, NULL); |
697 | cond_resched(); | 740 | hashtab_destroy(p->range_tr); |
698 | if (lrt) { | ||
699 | ebitmap_destroy(&lrt->target_range.level[0].cat); | ||
700 | ebitmap_destroy(&lrt->target_range.level[1].cat); | ||
701 | kfree(lrt); | ||
702 | } | ||
703 | lrt = rt; | ||
704 | } | ||
705 | if (lrt) { | ||
706 | ebitmap_destroy(&lrt->target_range.level[0].cat); | ||
707 | ebitmap_destroy(&lrt->target_range.level[1].cat); | ||
708 | kfree(lrt); | ||
709 | } | ||
710 | 741 | ||
711 | if (p->type_attr_map) { | 742 | if (p->type_attr_map) { |
712 | for (i = 0; i < p->p_types.nprim; i++) | 743 | for (i = 0; i < p->p_types.nprim; i++) |
@@ -1689,7 +1720,8 @@ int policydb_read(struct policydb *p, void *fp) | |||
1689 | u32 len, len2, config, nprim, nel, nel2; | 1720 | u32 len, len2, config, nprim, nel, nel2; |
1690 | char *policydb_str; | 1721 | char *policydb_str; |
1691 | struct policydb_compat_info *info; | 1722 | struct policydb_compat_info *info; |
1692 | struct range_trans *rt, *lrt; | 1723 | struct range_trans *rt; |
1724 | struct mls_range *r; | ||
1693 | 1725 | ||
1694 | config = 0; | 1726 | config = 0; |
1695 | 1727 | ||
@@ -2122,44 +2154,61 @@ int policydb_read(struct policydb *p, void *fp) | |||
2122 | if (rc < 0) | 2154 | if (rc < 0) |
2123 | goto bad; | 2155 | goto bad; |
2124 | nel = le32_to_cpu(buf[0]); | 2156 | nel = le32_to_cpu(buf[0]); |
2125 | lrt = NULL; | ||
2126 | for (i = 0; i < nel; i++) { | 2157 | for (i = 0; i < nel; i++) { |
2127 | rt = kzalloc(sizeof(*rt), GFP_KERNEL); | 2158 | rt = kzalloc(sizeof(*rt), GFP_KERNEL); |
2128 | if (!rt) { | 2159 | if (!rt) { |
2129 | rc = -ENOMEM; | 2160 | rc = -ENOMEM; |
2130 | goto bad; | 2161 | goto bad; |
2131 | } | 2162 | } |
2132 | if (lrt) | ||
2133 | lrt->next = rt; | ||
2134 | else | ||
2135 | p->range_tr = rt; | ||
2136 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); | 2163 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); |
2137 | if (rc < 0) | 2164 | if (rc < 0) { |
2165 | kfree(rt); | ||
2138 | goto bad; | 2166 | goto bad; |
2167 | } | ||
2139 | rt->source_type = le32_to_cpu(buf[0]); | 2168 | rt->source_type = le32_to_cpu(buf[0]); |
2140 | rt->target_type = le32_to_cpu(buf[1]); | 2169 | rt->target_type = le32_to_cpu(buf[1]); |
2141 | if (new_rangetr) { | 2170 | if (new_rangetr) { |
2142 | rc = next_entry(buf, fp, sizeof(u32)); | 2171 | rc = next_entry(buf, fp, sizeof(u32)); |
2143 | if (rc < 0) | 2172 | if (rc < 0) { |
2173 | kfree(rt); | ||
2144 | goto bad; | 2174 | goto bad; |
2175 | } | ||
2145 | rt->target_class = le32_to_cpu(buf[0]); | 2176 | rt->target_class = le32_to_cpu(buf[0]); |
2146 | } else | 2177 | } else |
2147 | rt->target_class = p->process_class; | 2178 | rt->target_class = p->process_class; |
2148 | if (!policydb_type_isvalid(p, rt->source_type) || | 2179 | if (!policydb_type_isvalid(p, rt->source_type) || |
2149 | !policydb_type_isvalid(p, rt->target_type) || | 2180 | !policydb_type_isvalid(p, rt->target_type) || |
2150 | !policydb_class_isvalid(p, rt->target_class)) { | 2181 | !policydb_class_isvalid(p, rt->target_class)) { |
2182 | kfree(rt); | ||
2151 | rc = -EINVAL; | 2183 | rc = -EINVAL; |
2152 | goto bad; | 2184 | goto bad; |
2153 | } | 2185 | } |
2154 | rc = mls_read_range_helper(&rt->target_range, fp); | 2186 | r = kzalloc(sizeof(*r), GFP_KERNEL); |
2155 | if (rc) | 2187 | if (!r) { |
2188 | kfree(rt); | ||
2189 | rc = -ENOMEM; | ||
2156 | goto bad; | 2190 | goto bad; |
2157 | if (!mls_range_isvalid(p, &rt->target_range)) { | 2191 | } |
2192 | rc = mls_read_range_helper(r, fp); | ||
2193 | if (rc) { | ||
2194 | kfree(rt); | ||
2195 | kfree(r); | ||
2196 | goto bad; | ||
2197 | } | ||
2198 | if (!mls_range_isvalid(p, r)) { | ||
2158 | printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); | 2199 | printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); |
2200 | kfree(rt); | ||
2201 | kfree(r); | ||
2202 | goto bad; | ||
2203 | } | ||
2204 | rc = hashtab_insert(p->range_tr, rt, r); | ||
2205 | if (rc) { | ||
2206 | kfree(rt); | ||
2207 | kfree(r); | ||
2159 | goto bad; | 2208 | goto bad; |
2160 | } | 2209 | } |
2161 | lrt = rt; | ||
2162 | } | 2210 | } |
2211 | rangetr_hash_eval(p->range_tr); | ||
2163 | } | 2212 | } |
2164 | 2213 | ||
2165 | p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL); | 2214 | p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL); |
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index cdcc5700946f..193736b64de8 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h | |||
@@ -113,8 +113,6 @@ struct range_trans { | |||
113 | u32 source_type; | 113 | u32 source_type; |
114 | u32 target_type; | 114 | u32 target_type; |
115 | u32 target_class; | 115 | u32 target_class; |
116 | struct mls_range target_range; | ||
117 | struct range_trans *next; | ||
118 | }; | 116 | }; |
119 | 117 | ||
120 | /* Boolean data type */ | 118 | /* Boolean data type */ |
@@ -240,8 +238,8 @@ struct policydb { | |||
240 | fixed labeling behavior. */ | 238 | fixed labeling behavior. */ |
241 | struct genfs *genfs; | 239 | struct genfs *genfs; |
242 | 240 | ||
243 | /* range transitions */ | 241 | /* range transitions table (range_trans_key -> mls_range) */ |
244 | struct range_trans *range_tr; | 242 | struct hashtab *range_tr; |
245 | 243 | ||
246 | /* type -> attribute reverse mapping */ | 244 | /* type -> attribute reverse mapping */ |
247 | struct ebitmap *type_attr_map; | 245 | struct ebitmap *type_attr_map; |