diff options
Diffstat (limited to 'security/selinux/ss/avtab.c')
| -rw-r--r-- | security/selinux/ss/avtab.c | 421 |
1 files changed, 168 insertions, 253 deletions
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index c0417cf17fee..d12ca337e649 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -1,74 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Implementation of the access vector table type. * - * Author : Stephen Smalley, <sds@tycho.nsa.gov> + * Author : Stephen Smalley, <stephen.smalley.work@gmail.com> */ -/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> - * - * Added conditional policy language extensions - * - * Copyright (C) 2003 Tresys Technology, LLC - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2. +/* Updated: Frank Mayer <mayerf@tresys.com> and + * Karl MacMillan <kmacmillan@tresys.com> + * Added conditional policy language extensions + * Copyright (C) 2003 Tresys Technology, LLC * * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp> - * Tuned number of hash slots for avtab to reduce memory usage + * Tuned number of hash slots for avtab to reduce memory usage */ +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/errno.h> #include "avtab.h" #include "policydb.h" +#include "hash.h" -static struct kmem_cache *avtab_node_cachep; -static struct kmem_cache *avtab_xperms_cachep; +static struct kmem_cache *avtab_node_cachep __ro_after_init; +static struct kmem_cache *avtab_xperms_cachep __ro_after_init; -/* Based on MurmurHash3, written by Austin Appleby and placed in the - * public domain. - */ -static inline int avtab_hash(struct avtab_key *keyp, u32 mask) +static inline u32 avtab_hash(const struct avtab_key *keyp, u32 mask) { - static const u32 c1 = 0xcc9e2d51; - static const u32 c2 = 0x1b873593; - static const u32 r1 = 15; - static const u32 r2 = 13; - static const u32 m = 5; - static const u32 n = 0xe6546b64; - - u32 hash = 0; - -#define mix(input) { \ - u32 v = input; \ - v *= c1; \ - v = (v << r1) | (v >> (32 - r1)); \ - v *= c2; \ - hash ^= v; \ - hash = (hash << r2) | (hash >> (32 - r2)); \ - hash = hash * m + n; \ -} - - mix(keyp->target_class); - mix(keyp->target_type); - mix(keyp->source_type); - -#undef mix - - hash ^= hash >> 16; - hash *= 0x85ebca6b; - hash ^= hash >> 13; - hash *= 0xc2b2ae35; - hash ^= hash >> 16; - - return hash & mask; + return av_hash((u32)keyp->target_class, (u32)keyp->target_type, + (u32)keyp->source_type, mask); } -static struct avtab_node* -avtab_insert_node(struct avtab *h, int hvalue, - struct avtab_node *prev, struct avtab_node *cur, - struct avtab_key *key, struct avtab_datum *datum) +static struct avtab_node *avtab_insert_node(struct avtab *h, + struct avtab_node **dst, + const struct avtab_key *key, + const struct avtab_datum *datum) { struct avtab_node *newnode; struct avtab_extended_perms *xperms; @@ -89,56 +55,58 @@ avtab_insert_node(struct avtab *h, int hvalue, newnode->datum.u.data = datum->u.data; } - if (prev) { - newnode->next = prev->next; - prev->next = newnode; - } else { - newnode->next = flex_array_get_ptr(h->htable, hvalue); - if (flex_array_put_ptr(h->htable, hvalue, newnode, - GFP_KERNEL|__GFP_ZERO)) { - kmem_cache_free(avtab_node_cachep, newnode); - return NULL; - } - } + newnode->next = *dst; + *dst = newnode; h->nel++; return newnode; } -static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum) +static int avtab_node_cmp(const struct avtab_key *key1, + const struct avtab_key *key2) { - int hvalue; + u16 specified = key1->specified & ~(AVTAB_ENABLED | AVTAB_ENABLED_OLD); + + if (key1->source_type == key2->source_type && + key1->target_type == key2->target_type && + key1->target_class == key2->target_class && + (specified & key2->specified)) + return 0; + if (key1->source_type < key2->source_type) + return -1; + if (key1->source_type == key2->source_type && + key1->target_type < key2->target_type) + return -1; + if (key1->source_type == key2->source_type && + key1->target_type == key2->target_type && + key1->target_class < key2->target_class) + return -1; + return 1; +} + +static int avtab_insert(struct avtab *h, const struct avtab_key *key, + const struct avtab_datum *datum) +{ + u32 hvalue; struct avtab_node *prev, *cur, *newnode; - u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); + int cmp; - if (!h || !h->htable) + if (!h || !h->nslot || h->nel == U32_MAX) return -EINVAL; hvalue = avtab_hash(key, h->mask); - for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue); - cur; + for (prev = NULL, cur = h->htable[hvalue]; cur; prev = cur, cur = cur->next) { - if (key->source_type == cur->key.source_type && - key->target_type == cur->key.target_type && - key->target_class == cur->key.target_class && - (specified & cur->key.specified)) { - /* extended perms may not be unique */ - if (specified & AVTAB_XPERMS) - break; + cmp = avtab_node_cmp(key, &cur->key); + /* extended perms may not be unique */ + if (cmp == 0 && !(key->specified & AVTAB_XPERMS)) return -EEXIST; - } - if (key->source_type < cur->key.source_type) - break; - if (key->source_type == cur->key.source_type && - key->target_type < cur->key.target_type) - break; - if (key->source_type == cur->key.source_type && - key->target_type == cur->key.target_type && - key->target_class < cur->key.target_class) + if (cmp <= 0) break; } - newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); + newnode = avtab_insert_node(h, prev ? &prev->next : &h->htable[hvalue], + key, datum); if (!newnode) return -ENOMEM; @@ -149,128 +117,67 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat * key/specified mask into the table, as needed by the conditional avtab. * It also returns a pointer to the node inserted. */ -struct avtab_node * -avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum) +struct avtab_node *avtab_insert_nonunique(struct avtab *h, + const struct avtab_key *key, + const struct avtab_datum *datum) { - int hvalue; + u32 hvalue; struct avtab_node *prev, *cur; - u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); + int cmp; - if (!h || !h->htable) + if (!h || !h->nslot || h->nel == U32_MAX) return NULL; hvalue = avtab_hash(key, h->mask); - for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue); - cur; + for (prev = NULL, cur = h->htable[hvalue]; cur; prev = cur, cur = cur->next) { - if (key->source_type == cur->key.source_type && - key->target_type == cur->key.target_type && - key->target_class == cur->key.target_class && - (specified & cur->key.specified)) - break; - if (key->source_type < cur->key.source_type) - break; - if (key->source_type == cur->key.source_type && - key->target_type < cur->key.target_type) - break; - if (key->source_type == cur->key.source_type && - key->target_type == cur->key.target_type && - key->target_class < cur->key.target_class) - break; - } - return avtab_insert_node(h, hvalue, prev, cur, key, datum); -} - -struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key) -{ - int hvalue; - struct avtab_node *cur; - u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); - - if (!h || !h->htable) - return NULL; - - hvalue = avtab_hash(key, h->mask); - for (cur = flex_array_get_ptr(h->htable, hvalue); cur; - cur = cur->next) { - if (key->source_type == cur->key.source_type && - key->target_type == cur->key.target_type && - key->target_class == cur->key.target_class && - (specified & cur->key.specified)) - return &cur->datum; - - if (key->source_type < cur->key.source_type) - break; - if (key->source_type == cur->key.source_type && - key->target_type < cur->key.target_type) - break; - if (key->source_type == cur->key.source_type && - key->target_type == cur->key.target_type && - key->target_class < cur->key.target_class) + cmp = avtab_node_cmp(key, &cur->key); + if (cmp <= 0) break; } - - return NULL; + return avtab_insert_node(h, prev ? &prev->next : &h->htable[hvalue], + key, datum); } /* This search function returns a node pointer, and can be used in * conjunction with avtab_search_next_node() */ -struct avtab_node* -avtab_search_node(struct avtab *h, struct avtab_key *key) +struct avtab_node *avtab_search_node(struct avtab *h, + const struct avtab_key *key) { - int hvalue; + u32 hvalue; struct avtab_node *cur; - u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); + int cmp; - if (!h || !h->htable) + if (!h || !h->nslot) return NULL; hvalue = avtab_hash(key, h->mask); - for (cur = flex_array_get_ptr(h->htable, hvalue); cur; - cur = cur->next) { - if (key->source_type == cur->key.source_type && - key->target_type == cur->key.target_type && - key->target_class == cur->key.target_class && - (specified & cur->key.specified)) + for (cur = h->htable[hvalue]; cur; cur = cur->next) { + cmp = avtab_node_cmp(key, &cur->key); + if (cmp == 0) return cur; - - if (key->source_type < cur->key.source_type) - break; - if (key->source_type == cur->key.source_type && - key->target_type < cur->key.target_type) - break; - if (key->source_type == cur->key.source_type && - key->target_type == cur->key.target_type && - key->target_class < cur->key.target_class) + if (cmp < 0) break; } return NULL; } -struct avtab_node* -avtab_search_node_next(struct avtab_node *node, int specified) +struct avtab_node *avtab_search_node_next(struct avtab_node *node, + u16 specified) { + struct avtab_key tmp_key; struct avtab_node *cur; + int cmp; if (!node) return NULL; - - specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); + tmp_key = node->key; + tmp_key.specified = specified; for (cur = node->next; cur; cur = cur->next) { - if (node->key.source_type == cur->key.source_type && - node->key.target_type == cur->key.target_type && - node->key.target_class == cur->key.target_class && - (specified & cur->key.specified)) + cmp = avtab_node_cmp(&tmp_key, &cur->key); + if (cmp == 0) return cur; - - if (node->key.source_type < cur->key.source_type) - break; - if (node->key.source_type == cur->key.source_type && - node->key.target_type < cur->key.target_type) - break; - if (node->key.source_type == cur->key.source_type && - node->key.target_type == cur->key.target_type && - node->key.target_class < cur->key.target_class) + if (cmp < 0) break; } return NULL; @@ -278,14 +185,14 @@ avtab_search_node_next(struct avtab_node *node, int specified) void avtab_destroy(struct avtab *h) { - int i; + u32 i; struct avtab_node *cur, *temp; - if (!h || !h->htable) + if (!h) return; for (i = 0; i < h->nslot; i++) { - cur = flex_array_get_ptr(h->htable, i); + cur = h->htable[i]; while (cur) { temp = cur; cur = cur->next; @@ -295,57 +202,63 @@ void avtab_destroy(struct avtab *h) kmem_cache_free(avtab_node_cachep, temp); } } - flex_array_free(h->htable); + kvfree(h->htable); h->htable = NULL; + h->nel = 0; h->nslot = 0; h->mask = 0; } -int avtab_init(struct avtab *h) +void avtab_init(struct avtab *h) { h->htable = NULL; h->nel = 0; + h->nslot = 0; + h->mask = 0; +} + +static int avtab_alloc_common(struct avtab *h, u32 nslot) +{ + if (!nslot) + return 0; + + h->htable = kvcalloc(nslot, sizeof(void *), GFP_KERNEL); + if (!h->htable) + return -ENOMEM; + + h->nslot = nslot; + h->mask = nslot - 1; return 0; } int avtab_alloc(struct avtab *h, u32 nrules) { - u32 mask = 0; - u32 shift = 0; - u32 work = nrules; + int rc; u32 nslot = 0; - if (nrules == 0) - goto avtab_alloc_out; + if (nrules != 0) { + nslot = nrules > 3 ? rounddown_pow_of_two(nrules / 2) : 2; + if (nslot > MAX_AVTAB_HASH_BUCKETS) + nslot = MAX_AVTAB_HASH_BUCKETS; - while (work) { - work = work >> 1; - shift++; + rc = avtab_alloc_common(h, nslot); + if (rc) + return rc; } - if (shift > 2) - shift = shift - 2; - nslot = 1 << shift; - if (nslot > MAX_AVTAB_HASH_BUCKETS) - nslot = MAX_AVTAB_HASH_BUCKETS; - mask = nslot - 1; - - h->htable = flex_array_alloc(sizeof(struct avtab_node *), nslot, - GFP_KERNEL | __GFP_ZERO); - if (!h->htable) - return -ENOMEM; - avtab_alloc_out: - h->nel = 0; - h->nslot = nslot; - h->mask = mask; - pr_debug("SELinux: %d avtab hash slots, %d rules.\n", - h->nslot, nrules); + pr_debug("SELinux: %d avtab hash slots, %d rules.\n", nslot, nrules); return 0; } -void avtab_hash_eval(struct avtab *h, char *tag) +int avtab_alloc_dup(struct avtab *new, const struct avtab *orig) +{ + return avtab_alloc_common(new, orig->nslot); +} + +#ifdef CONFIG_SECURITY_SELINUX_DEBUG +void avtab_hash_eval(struct avtab *h, const char *tag) { - int i, chain_len, slots_used, max_chain_len; + u32 i, chain_len, slots_used, max_chain_len; unsigned long long chain2_len_sum; struct avtab_node *cur; @@ -353,7 +266,7 @@ void avtab_hash_eval(struct avtab *h, char *tag) max_chain_len = 0; chain2_len_sum = 0; for (i = 0; i < h->nslot; i++) { - cur = flex_array_get_ptr(h->htable, i); + cur = h->htable[i]; if (cur) { slots_used++; chain_len = 0; @@ -364,17 +277,20 @@ void avtab_hash_eval(struct avtab *h, char *tag) if (chain_len > max_chain_len) max_chain_len = chain_len; - chain2_len_sum += chain_len * chain_len; + chain2_len_sum += + (unsigned long long)chain_len * chain_len; } } pr_debug("SELinux: %s: %d entries and %d/%d buckets used, " - "longest chain length %d sum of chain length^2 %llu\n", - tag, h->nel, slots_used, h->nslot, max_chain_len, - chain2_len_sum); + "longest chain length %d, sum of chain length^2 %llu\n", + tag, h->nel, slots_used, h->nslot, max_chain_len, + chain2_len_sum); } +#endif /* CONFIG_SECURITY_SELINUX_DEBUG */ -static uint16_t spec_order[] = { +/* clang-format off */ +static const uint16_t spec_order[] = { AVTAB_ALLOWED, AVTAB_AUDITDENY, AVTAB_AUDITALLOW, @@ -385,21 +301,22 @@ static uint16_t spec_order[] = { AVTAB_XPERMS_AUDITALLOW, AVTAB_XPERMS_DONTAUDIT }; +/* clang-format on */ -int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, - int (*insertf)(struct avtab *a, struct avtab_key *k, - struct avtab_datum *d, void *p), - void *p) +int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *pol, + int (*insertf)(struct avtab *a, const struct avtab_key *k, + const struct avtab_datum *d, void *p), + void *p, bool conditional) { __le16 buf16[4]; u16 enabled; - u32 items, items2, val, vers = pol->policyvers; + u32 items, items2, val, i; struct avtab_key key; struct avtab_datum datum; struct avtab_extended_perms xperms; __le32 buf32[ARRAY_SIZE(xperms.perms.p)]; - int i, rc; - unsigned set; + int rc; + unsigned int set, vers = pol->policyvers; memset(&key, 0, sizeof(struct avtab_key)); memset(&datum, 0, sizeof(struct avtab_datum)); @@ -414,9 +331,8 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, if (items2 > ARRAY_SIZE(buf32)) { pr_err("SELinux: avtab: entry overflow\n"); return -EINVAL; - } - rc = next_entry(buf32, fp, sizeof(u32)*items2); + rc = next_entry(buf32, fp, sizeof(u32) * items2); if (rc) { pr_err("SELinux: avtab: truncated entry\n"); return rc; @@ -449,8 +365,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, pr_err("SELinux: avtab: null entry\n"); return -EINVAL; } - if ((val & AVTAB_AV) && - (val & AVTAB_TYPE)) { + if ((val & AVTAB_AV) && (val & AVTAB_TYPE)) { pr_err("SELinux: avtab: entry has both access vectors and types\n"); return -EINVAL; } @@ -477,7 +392,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, return 0; } - rc = next_entry(buf16, fp, sizeof(u16)*4); + rc = next_entry(buf16, fp, sizeof(u16) * 4); if (rc) { pr_err("SELinux: avtab: truncated entry\n"); return rc; @@ -496,21 +411,25 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, return -EINVAL; } - set = 0; - for (i = 0; i < ARRAY_SIZE(spec_order); i++) { - if (key.specified & spec_order[i]) - set++; - } + set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV)); if (!set || set > 1) { pr_err("SELinux: avtab: more than one specifier\n"); return -EINVAL; } if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) && - (key.specified & AVTAB_XPERMS)) { + (key.specified & AVTAB_XPERMS)) { pr_err("SELinux: avtab: policy version %u does not " - "support extended permissions rules and one " - "was specified\n", vers); + "support extended permissions rules and one " + "was specified\n", + vers); + return -EINVAL; + } else if ((vers < POLICYDB_VERSION_COND_XPERMS) && + (key.specified & AVTAB_XPERMS) && conditional) { + pr_err("SELinux: avtab: policy version %u does not " + "support extended permissions rules in conditional " + "policies and one was specified\n", + vers); return -EINVAL; } else if (key.specified & AVTAB_XPERMS) { memset(&xperms, 0, sizeof(struct avtab_extended_perms)); @@ -524,7 +443,8 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, pr_err("SELinux: avtab: truncated entry\n"); return rc; } - rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(xperms.perms.p)); + rc = next_entry(buf32, fp, + sizeof(u32) * ARRAY_SIZE(xperms.perms.p)); if (rc) { pr_err("SELinux: avtab: truncated entry\n"); return rc; @@ -548,19 +468,18 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, return insertf(a, &key, &datum, p); } -static int avtab_insertf(struct avtab *a, struct avtab_key *k, - struct avtab_datum *d, void *p) +static int avtab_insertf(struct avtab *a, const struct avtab_key *k, + const struct avtab_datum *d, void *p) { return avtab_insert(a, k, d); } -int avtab_read(struct avtab *a, void *fp, struct policydb *pol) +int avtab_read(struct avtab *a, struct policy_file *fp, struct policydb *pol) { int rc; __le32 buf[1]; u32 nel, i; - rc = next_entry(buf, fp, sizeof(u32)); if (rc < 0) { pr_err("SELinux: avtab: truncated table\n"); @@ -578,7 +497,7 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) goto bad; for (i = 0; i < nel; i++) { - rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL); + rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL, false); if (rc) { if (rc == -ENOMEM) pr_err("SELinux: avtab: out of memory\n"); @@ -598,7 +517,7 @@ bad: goto out; } -int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) +int avtab_write_item(struct policydb *p, const struct avtab_node *cur, struct policy_file *fp) { __le16 buf16[4]; __le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)]; @@ -614,7 +533,8 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) return rc; if (cur->key.specified & AVTAB_XPERMS) { - rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1, fp); + rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1, + fp); if (rc) return rc; rc = put_entry(&cur->datum.u.xperms->driver, sizeof(u8), 1, fp); @@ -623,7 +543,7 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) for (i = 0; i < ARRAY_SIZE(cur->datum.u.xperms->perms.p); i++) buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]); rc = put_entry(buf32, sizeof(u32), - ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp); + ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp); } else { buf32[0] = cpu_to_le32(cur->datum.u.data); rc = put_entry(buf32, sizeof(u32), 1, fp); @@ -633,9 +553,9 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) return 0; } -int avtab_write(struct policydb *p, struct avtab *a, void *fp) +int avtab_write(struct policydb *p, struct avtab *a, struct policy_file *fp) { - unsigned int i; + u32 i; int rc = 0; struct avtab_node *cur; __le32 buf[1]; @@ -646,8 +566,7 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp) return rc; for (i = 0; i < a->nslot; i++) { - for (cur = flex_array_get_ptr(a->htable, i); cur; - cur = cur->next) { + for (cur = a->htable[i]; cur; cur = cur->next) { rc = avtab_write_item(p, cur, fp); if (rc) return rc; @@ -659,10 +578,6 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp) void __init avtab_cache_init(void) { - avtab_node_cachep = kmem_cache_create("avtab_node", - sizeof(struct avtab_node), - 0, SLAB_PANIC, NULL); - avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms", - sizeof(struct avtab_extended_perms), - 0, SLAB_PANIC, NULL); + avtab_node_cachep = KMEM_CACHE(avtab_node, SLAB_PANIC); + avtab_xperms_cachep = KMEM_CACHE(avtab_extended_perms, SLAB_PANIC); } |
