162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Implementation of the access vector table type. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Author : Stephen Smalley, <stephen.smalley.work@gmail.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Added conditional policy language extensions 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright (C) 2003 Tresys Technology, LLC 1262306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 1362306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1462306a36Sopenharmony_ci * the Free Software Foundation, version 2. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp> 1762306a36Sopenharmony_ci * Tuned number of hash slots for avtab to reduce memory usage 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/errno.h> 2362306a36Sopenharmony_ci#include "avtab.h" 2462306a36Sopenharmony_ci#include "policydb.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic struct kmem_cache *avtab_node_cachep __ro_after_init; 2762306a36Sopenharmony_cistatic struct kmem_cache *avtab_xperms_cachep __ro_after_init; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Based on MurmurHash3, written by Austin Appleby and placed in the 3062306a36Sopenharmony_ci * public domain. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistatic inline u32 avtab_hash(const struct avtab_key *keyp, u32 mask) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci static const u32 c1 = 0xcc9e2d51; 3562306a36Sopenharmony_ci static const u32 c2 = 0x1b873593; 3662306a36Sopenharmony_ci static const u32 r1 = 15; 3762306a36Sopenharmony_ci static const u32 r2 = 13; 3862306a36Sopenharmony_ci static const u32 m = 5; 3962306a36Sopenharmony_ci static const u32 n = 0xe6546b64; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci u32 hash = 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define mix(input) do { \ 4462306a36Sopenharmony_ci u32 v = input; \ 4562306a36Sopenharmony_ci v *= c1; \ 4662306a36Sopenharmony_ci v = (v << r1) | (v >> (32 - r1)); \ 4762306a36Sopenharmony_ci v *= c2; \ 4862306a36Sopenharmony_ci hash ^= v; \ 4962306a36Sopenharmony_ci hash = (hash << r2) | (hash >> (32 - r2)); \ 5062306a36Sopenharmony_ci hash = hash * m + n; \ 5162306a36Sopenharmony_ci } while (0) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci mix(keyp->target_class); 5462306a36Sopenharmony_ci mix(keyp->target_type); 5562306a36Sopenharmony_ci mix(keyp->source_type); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#undef mix 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci hash ^= hash >> 16; 6062306a36Sopenharmony_ci hash *= 0x85ebca6b; 6162306a36Sopenharmony_ci hash ^= hash >> 13; 6262306a36Sopenharmony_ci hash *= 0xc2b2ae35; 6362306a36Sopenharmony_ci hash ^= hash >> 16; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return hash & mask; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct avtab_node* 6962306a36Sopenharmony_ciavtab_insert_node(struct avtab *h, u32 hvalue, 7062306a36Sopenharmony_ci struct avtab_node *prev, 7162306a36Sopenharmony_ci const struct avtab_key *key, const struct avtab_datum *datum) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct avtab_node *newnode; 7462306a36Sopenharmony_ci struct avtab_extended_perms *xperms; 7562306a36Sopenharmony_ci newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); 7662306a36Sopenharmony_ci if (newnode == NULL) 7762306a36Sopenharmony_ci return NULL; 7862306a36Sopenharmony_ci newnode->key = *key; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (key->specified & AVTAB_XPERMS) { 8162306a36Sopenharmony_ci xperms = kmem_cache_zalloc(avtab_xperms_cachep, GFP_KERNEL); 8262306a36Sopenharmony_ci if (xperms == NULL) { 8362306a36Sopenharmony_ci kmem_cache_free(avtab_node_cachep, newnode); 8462306a36Sopenharmony_ci return NULL; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci *xperms = *(datum->u.xperms); 8762306a36Sopenharmony_ci newnode->datum.u.xperms = xperms; 8862306a36Sopenharmony_ci } else { 8962306a36Sopenharmony_ci newnode->datum.u.data = datum->u.data; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (prev) { 9362306a36Sopenharmony_ci newnode->next = prev->next; 9462306a36Sopenharmony_ci prev->next = newnode; 9562306a36Sopenharmony_ci } else { 9662306a36Sopenharmony_ci struct avtab_node **n = &h->htable[hvalue]; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci newnode->next = *n; 9962306a36Sopenharmony_ci *n = newnode; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci h->nel++; 10362306a36Sopenharmony_ci return newnode; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int avtab_insert(struct avtab *h, const struct avtab_key *key, 10762306a36Sopenharmony_ci const struct avtab_datum *datum) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci u32 hvalue; 11062306a36Sopenharmony_ci struct avtab_node *prev, *cur, *newnode; 11162306a36Sopenharmony_ci u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!h || !h->nslot || h->nel == U32_MAX) 11462306a36Sopenharmony_ci return -EINVAL; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci hvalue = avtab_hash(key, h->mask); 11762306a36Sopenharmony_ci for (prev = NULL, cur = h->htable[hvalue]; 11862306a36Sopenharmony_ci cur; 11962306a36Sopenharmony_ci prev = cur, cur = cur->next) { 12062306a36Sopenharmony_ci if (key->source_type == cur->key.source_type && 12162306a36Sopenharmony_ci key->target_type == cur->key.target_type && 12262306a36Sopenharmony_ci key->target_class == cur->key.target_class && 12362306a36Sopenharmony_ci (specified & cur->key.specified)) { 12462306a36Sopenharmony_ci /* extended perms may not be unique */ 12562306a36Sopenharmony_ci if (specified & AVTAB_XPERMS) 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci return -EEXIST; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci if (key->source_type < cur->key.source_type) 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci if (key->source_type == cur->key.source_type && 13262306a36Sopenharmony_ci key->target_type < cur->key.target_type) 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci if (key->source_type == cur->key.source_type && 13562306a36Sopenharmony_ci key->target_type == cur->key.target_type && 13662306a36Sopenharmony_ci key->target_class < cur->key.target_class) 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci newnode = avtab_insert_node(h, hvalue, prev, key, datum); 14162306a36Sopenharmony_ci if (!newnode) 14262306a36Sopenharmony_ci return -ENOMEM; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* Unlike avtab_insert(), this function allow multiple insertions of the same 14862306a36Sopenharmony_ci * key/specified mask into the table, as needed by the conditional avtab. 14962306a36Sopenharmony_ci * It also returns a pointer to the node inserted. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistruct avtab_node *avtab_insert_nonunique(struct avtab *h, 15262306a36Sopenharmony_ci const struct avtab_key *key, 15362306a36Sopenharmony_ci const struct avtab_datum *datum) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci u32 hvalue; 15662306a36Sopenharmony_ci struct avtab_node *prev, *cur; 15762306a36Sopenharmony_ci u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!h || !h->nslot || h->nel == U32_MAX) 16062306a36Sopenharmony_ci return NULL; 16162306a36Sopenharmony_ci hvalue = avtab_hash(key, h->mask); 16262306a36Sopenharmony_ci for (prev = NULL, cur = h->htable[hvalue]; 16362306a36Sopenharmony_ci cur; 16462306a36Sopenharmony_ci prev = cur, cur = cur->next) { 16562306a36Sopenharmony_ci if (key->source_type == cur->key.source_type && 16662306a36Sopenharmony_ci key->target_type == cur->key.target_type && 16762306a36Sopenharmony_ci key->target_class == cur->key.target_class && 16862306a36Sopenharmony_ci (specified & cur->key.specified)) 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci if (key->source_type < cur->key.source_type) 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci if (key->source_type == cur->key.source_type && 17362306a36Sopenharmony_ci key->target_type < cur->key.target_type) 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci if (key->source_type == cur->key.source_type && 17662306a36Sopenharmony_ci key->target_type == cur->key.target_type && 17762306a36Sopenharmony_ci key->target_class < cur->key.target_class) 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci return avtab_insert_node(h, hvalue, prev, key, datum); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* This search function returns a node pointer, and can be used in 18462306a36Sopenharmony_ci * conjunction with avtab_search_next_node() 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistruct avtab_node *avtab_search_node(struct avtab *h, 18762306a36Sopenharmony_ci const struct avtab_key *key) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci u32 hvalue; 19062306a36Sopenharmony_ci struct avtab_node *cur; 19162306a36Sopenharmony_ci u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (!h || !h->nslot) 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci hvalue = avtab_hash(key, h->mask); 19762306a36Sopenharmony_ci for (cur = h->htable[hvalue]; cur; 19862306a36Sopenharmony_ci cur = cur->next) { 19962306a36Sopenharmony_ci if (key->source_type == cur->key.source_type && 20062306a36Sopenharmony_ci key->target_type == cur->key.target_type && 20162306a36Sopenharmony_ci key->target_class == cur->key.target_class && 20262306a36Sopenharmony_ci (specified & cur->key.specified)) 20362306a36Sopenharmony_ci return cur; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (key->source_type < cur->key.source_type) 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci if (key->source_type == cur->key.source_type && 20862306a36Sopenharmony_ci key->target_type < cur->key.target_type) 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci if (key->source_type == cur->key.source_type && 21162306a36Sopenharmony_ci key->target_type == cur->key.target_type && 21262306a36Sopenharmony_ci key->target_class < cur->key.target_class) 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci return NULL; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistruct avtab_node* 21962306a36Sopenharmony_ciavtab_search_node_next(struct avtab_node *node, u16 specified) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct avtab_node *cur; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (!node) 22462306a36Sopenharmony_ci return NULL; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 22762306a36Sopenharmony_ci for (cur = node->next; cur; cur = cur->next) { 22862306a36Sopenharmony_ci if (node->key.source_type == cur->key.source_type && 22962306a36Sopenharmony_ci node->key.target_type == cur->key.target_type && 23062306a36Sopenharmony_ci node->key.target_class == cur->key.target_class && 23162306a36Sopenharmony_ci (specified & cur->key.specified)) 23262306a36Sopenharmony_ci return cur; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (node->key.source_type < cur->key.source_type) 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci if (node->key.source_type == cur->key.source_type && 23762306a36Sopenharmony_ci node->key.target_type < cur->key.target_type) 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci if (node->key.source_type == cur->key.source_type && 24062306a36Sopenharmony_ci node->key.target_type == cur->key.target_type && 24162306a36Sopenharmony_ci node->key.target_class < cur->key.target_class) 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci return NULL; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_civoid avtab_destroy(struct avtab *h) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci u32 i; 25062306a36Sopenharmony_ci struct avtab_node *cur, *temp; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (!h) 25362306a36Sopenharmony_ci return; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci for (i = 0; i < h->nslot; i++) { 25662306a36Sopenharmony_ci cur = h->htable[i]; 25762306a36Sopenharmony_ci while (cur) { 25862306a36Sopenharmony_ci temp = cur; 25962306a36Sopenharmony_ci cur = cur->next; 26062306a36Sopenharmony_ci if (temp->key.specified & AVTAB_XPERMS) 26162306a36Sopenharmony_ci kmem_cache_free(avtab_xperms_cachep, 26262306a36Sopenharmony_ci temp->datum.u.xperms); 26362306a36Sopenharmony_ci kmem_cache_free(avtab_node_cachep, temp); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci kvfree(h->htable); 26762306a36Sopenharmony_ci h->htable = NULL; 26862306a36Sopenharmony_ci h->nel = 0; 26962306a36Sopenharmony_ci h->nslot = 0; 27062306a36Sopenharmony_ci h->mask = 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_civoid avtab_init(struct avtab *h) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci h->htable = NULL; 27662306a36Sopenharmony_ci h->nel = 0; 27762306a36Sopenharmony_ci h->nslot = 0; 27862306a36Sopenharmony_ci h->mask = 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int avtab_alloc_common(struct avtab *h, u32 nslot) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci if (!nslot) 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci h->htable = kvcalloc(nslot, sizeof(void *), GFP_KERNEL); 28762306a36Sopenharmony_ci if (!h->htable) 28862306a36Sopenharmony_ci return -ENOMEM; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci h->nslot = nslot; 29162306a36Sopenharmony_ci h->mask = nslot - 1; 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ciint avtab_alloc(struct avtab *h, u32 nrules) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci int rc; 29862306a36Sopenharmony_ci u32 nslot = 0; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (nrules != 0) { 30162306a36Sopenharmony_ci u32 shift = 1; 30262306a36Sopenharmony_ci u32 work = nrules >> 3; 30362306a36Sopenharmony_ci while (work) { 30462306a36Sopenharmony_ci work >>= 1; 30562306a36Sopenharmony_ci shift++; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci nslot = 1 << shift; 30862306a36Sopenharmony_ci if (nslot > MAX_AVTAB_HASH_BUCKETS) 30962306a36Sopenharmony_ci nslot = MAX_AVTAB_HASH_BUCKETS; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci rc = avtab_alloc_common(h, nslot); 31262306a36Sopenharmony_ci if (rc) 31362306a36Sopenharmony_ci return rc; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci pr_debug("SELinux: %d avtab hash slots, %d rules.\n", nslot, nrules); 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ciint avtab_alloc_dup(struct avtab *new, const struct avtab *orig) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci return avtab_alloc_common(new, orig->nslot); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_DEBUG 32662306a36Sopenharmony_civoid avtab_hash_eval(struct avtab *h, const char *tag) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci u32 i, chain_len, slots_used, max_chain_len; 32962306a36Sopenharmony_ci unsigned long long chain2_len_sum; 33062306a36Sopenharmony_ci struct avtab_node *cur; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci slots_used = 0; 33362306a36Sopenharmony_ci max_chain_len = 0; 33462306a36Sopenharmony_ci chain2_len_sum = 0; 33562306a36Sopenharmony_ci for (i = 0; i < h->nslot; i++) { 33662306a36Sopenharmony_ci cur = h->htable[i]; 33762306a36Sopenharmony_ci if (cur) { 33862306a36Sopenharmony_ci slots_used++; 33962306a36Sopenharmony_ci chain_len = 0; 34062306a36Sopenharmony_ci while (cur) { 34162306a36Sopenharmony_ci chain_len++; 34262306a36Sopenharmony_ci cur = cur->next; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (chain_len > max_chain_len) 34662306a36Sopenharmony_ci max_chain_len = chain_len; 34762306a36Sopenharmony_ci chain2_len_sum += (unsigned long long)chain_len * chain_len; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci pr_debug("SELinux: %s: %d entries and %d/%d buckets used, " 35262306a36Sopenharmony_ci "longest chain length %d sum of chain length^2 %llu\n", 35362306a36Sopenharmony_ci tag, h->nel, slots_used, h->nslot, max_chain_len, 35462306a36Sopenharmony_ci chain2_len_sum); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci#endif /* CONFIG_SECURITY_SELINUX_DEBUG */ 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic const uint16_t spec_order[] = { 35962306a36Sopenharmony_ci AVTAB_ALLOWED, 36062306a36Sopenharmony_ci AVTAB_AUDITDENY, 36162306a36Sopenharmony_ci AVTAB_AUDITALLOW, 36262306a36Sopenharmony_ci AVTAB_TRANSITION, 36362306a36Sopenharmony_ci AVTAB_CHANGE, 36462306a36Sopenharmony_ci AVTAB_MEMBER, 36562306a36Sopenharmony_ci AVTAB_XPERMS_ALLOWED, 36662306a36Sopenharmony_ci AVTAB_XPERMS_AUDITALLOW, 36762306a36Sopenharmony_ci AVTAB_XPERMS_DONTAUDIT 36862306a36Sopenharmony_ci}; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciint avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, 37162306a36Sopenharmony_ci int (*insertf)(struct avtab *a, const struct avtab_key *k, 37262306a36Sopenharmony_ci const struct avtab_datum *d, void *p), 37362306a36Sopenharmony_ci void *p) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci __le16 buf16[4]; 37662306a36Sopenharmony_ci u16 enabled; 37762306a36Sopenharmony_ci u32 items, items2, val, i; 37862306a36Sopenharmony_ci struct avtab_key key; 37962306a36Sopenharmony_ci struct avtab_datum datum; 38062306a36Sopenharmony_ci struct avtab_extended_perms xperms; 38162306a36Sopenharmony_ci __le32 buf32[ARRAY_SIZE(xperms.perms.p)]; 38262306a36Sopenharmony_ci int rc; 38362306a36Sopenharmony_ci unsigned int set, vers = pol->policyvers; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci memset(&key, 0, sizeof(struct avtab_key)); 38662306a36Sopenharmony_ci memset(&datum, 0, sizeof(struct avtab_datum)); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (vers < POLICYDB_VERSION_AVTAB) { 38962306a36Sopenharmony_ci rc = next_entry(buf32, fp, sizeof(u32)); 39062306a36Sopenharmony_ci if (rc) { 39162306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated entry\n"); 39262306a36Sopenharmony_ci return rc; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci items2 = le32_to_cpu(buf32[0]); 39562306a36Sopenharmony_ci if (items2 > ARRAY_SIZE(buf32)) { 39662306a36Sopenharmony_ci pr_err("SELinux: avtab: entry overflow\n"); 39762306a36Sopenharmony_ci return -EINVAL; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci rc = next_entry(buf32, fp, sizeof(u32)*items2); 40162306a36Sopenharmony_ci if (rc) { 40262306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated entry\n"); 40362306a36Sopenharmony_ci return rc; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci items = 0; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci val = le32_to_cpu(buf32[items++]); 40862306a36Sopenharmony_ci key.source_type = (u16)val; 40962306a36Sopenharmony_ci if (key.source_type != val) { 41062306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated source type\n"); 41162306a36Sopenharmony_ci return -EINVAL; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci val = le32_to_cpu(buf32[items++]); 41462306a36Sopenharmony_ci key.target_type = (u16)val; 41562306a36Sopenharmony_ci if (key.target_type != val) { 41662306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated target type\n"); 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci val = le32_to_cpu(buf32[items++]); 42062306a36Sopenharmony_ci key.target_class = (u16)val; 42162306a36Sopenharmony_ci if (key.target_class != val) { 42262306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated target class\n"); 42362306a36Sopenharmony_ci return -EINVAL; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci val = le32_to_cpu(buf32[items++]); 42762306a36Sopenharmony_ci enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (!(val & (AVTAB_AV | AVTAB_TYPE))) { 43062306a36Sopenharmony_ci pr_err("SELinux: avtab: null entry\n"); 43162306a36Sopenharmony_ci return -EINVAL; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci if ((val & AVTAB_AV) && 43462306a36Sopenharmony_ci (val & AVTAB_TYPE)) { 43562306a36Sopenharmony_ci pr_err("SELinux: avtab: entry has both access vectors and types\n"); 43662306a36Sopenharmony_ci return -EINVAL; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci if (val & AVTAB_XPERMS) { 43962306a36Sopenharmony_ci pr_err("SELinux: avtab: entry has extended permissions\n"); 44062306a36Sopenharmony_ci return -EINVAL; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(spec_order); i++) { 44462306a36Sopenharmony_ci if (val & spec_order[i]) { 44562306a36Sopenharmony_ci key.specified = spec_order[i] | enabled; 44662306a36Sopenharmony_ci datum.u.data = le32_to_cpu(buf32[items++]); 44762306a36Sopenharmony_ci rc = insertf(a, &key, &datum, p); 44862306a36Sopenharmony_ci if (rc) 44962306a36Sopenharmony_ci return rc; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (items != items2) { 45462306a36Sopenharmony_ci pr_err("SELinux: avtab: entry only had %d items, expected %d\n", 45562306a36Sopenharmony_ci items2, items); 45662306a36Sopenharmony_ci return -EINVAL; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci rc = next_entry(buf16, fp, sizeof(u16)*4); 46262306a36Sopenharmony_ci if (rc) { 46362306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated entry\n"); 46462306a36Sopenharmony_ci return rc; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci items = 0; 46862306a36Sopenharmony_ci key.source_type = le16_to_cpu(buf16[items++]); 46962306a36Sopenharmony_ci key.target_type = le16_to_cpu(buf16[items++]); 47062306a36Sopenharmony_ci key.target_class = le16_to_cpu(buf16[items++]); 47162306a36Sopenharmony_ci key.specified = le16_to_cpu(buf16[items++]); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (!policydb_type_isvalid(pol, key.source_type) || 47462306a36Sopenharmony_ci !policydb_type_isvalid(pol, key.target_type) || 47562306a36Sopenharmony_ci !policydb_class_isvalid(pol, key.target_class)) { 47662306a36Sopenharmony_ci pr_err("SELinux: avtab: invalid type or class\n"); 47762306a36Sopenharmony_ci return -EINVAL; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci set = 0; 48162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(spec_order); i++) { 48262306a36Sopenharmony_ci if (key.specified & spec_order[i]) 48362306a36Sopenharmony_ci set++; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci if (!set || set > 1) { 48662306a36Sopenharmony_ci pr_err("SELinux: avtab: more than one specifier\n"); 48762306a36Sopenharmony_ci return -EINVAL; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) && 49162306a36Sopenharmony_ci (key.specified & AVTAB_XPERMS)) { 49262306a36Sopenharmony_ci pr_err("SELinux: avtab: policy version %u does not " 49362306a36Sopenharmony_ci "support extended permissions rules and one " 49462306a36Sopenharmony_ci "was specified\n", vers); 49562306a36Sopenharmony_ci return -EINVAL; 49662306a36Sopenharmony_ci } else if (key.specified & AVTAB_XPERMS) { 49762306a36Sopenharmony_ci memset(&xperms, 0, sizeof(struct avtab_extended_perms)); 49862306a36Sopenharmony_ci rc = next_entry(&xperms.specified, fp, sizeof(u8)); 49962306a36Sopenharmony_ci if (rc) { 50062306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated entry\n"); 50162306a36Sopenharmony_ci return rc; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci rc = next_entry(&xperms.driver, fp, sizeof(u8)); 50462306a36Sopenharmony_ci if (rc) { 50562306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated entry\n"); 50662306a36Sopenharmony_ci return rc; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(xperms.perms.p)); 50962306a36Sopenharmony_ci if (rc) { 51062306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated entry\n"); 51162306a36Sopenharmony_ci return rc; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xperms.perms.p); i++) 51462306a36Sopenharmony_ci xperms.perms.p[i] = le32_to_cpu(buf32[i]); 51562306a36Sopenharmony_ci datum.u.xperms = &xperms; 51662306a36Sopenharmony_ci } else { 51762306a36Sopenharmony_ci rc = next_entry(buf32, fp, sizeof(u32)); 51862306a36Sopenharmony_ci if (rc) { 51962306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated entry\n"); 52062306a36Sopenharmony_ci return rc; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci datum.u.data = le32_to_cpu(*buf32); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci if ((key.specified & AVTAB_TYPE) && 52562306a36Sopenharmony_ci !policydb_type_isvalid(pol, datum.u.data)) { 52662306a36Sopenharmony_ci pr_err("SELinux: avtab: invalid type\n"); 52762306a36Sopenharmony_ci return -EINVAL; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci return insertf(a, &key, &datum, p); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int avtab_insertf(struct avtab *a, const struct avtab_key *k, 53362306a36Sopenharmony_ci const struct avtab_datum *d, void *p) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci return avtab_insert(a, k, d); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ciint avtab_read(struct avtab *a, void *fp, struct policydb *pol) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci int rc; 54162306a36Sopenharmony_ci __le32 buf[1]; 54262306a36Sopenharmony_ci u32 nel, i; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci rc = next_entry(buf, fp, sizeof(u32)); 54662306a36Sopenharmony_ci if (rc < 0) { 54762306a36Sopenharmony_ci pr_err("SELinux: avtab: truncated table\n"); 54862306a36Sopenharmony_ci goto bad; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci nel = le32_to_cpu(buf[0]); 55162306a36Sopenharmony_ci if (!nel) { 55262306a36Sopenharmony_ci pr_err("SELinux: avtab: table is empty\n"); 55362306a36Sopenharmony_ci rc = -EINVAL; 55462306a36Sopenharmony_ci goto bad; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci rc = avtab_alloc(a, nel); 55862306a36Sopenharmony_ci if (rc) 55962306a36Sopenharmony_ci goto bad; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci for (i = 0; i < nel; i++) { 56262306a36Sopenharmony_ci rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL); 56362306a36Sopenharmony_ci if (rc) { 56462306a36Sopenharmony_ci if (rc == -ENOMEM) 56562306a36Sopenharmony_ci pr_err("SELinux: avtab: out of memory\n"); 56662306a36Sopenharmony_ci else if (rc == -EEXIST) 56762306a36Sopenharmony_ci pr_err("SELinux: avtab: duplicate entry\n"); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci goto bad; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci rc = 0; 57462306a36Sopenharmony_ciout: 57562306a36Sopenharmony_ci return rc; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cibad: 57862306a36Sopenharmony_ci avtab_destroy(a); 57962306a36Sopenharmony_ci goto out; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ciint avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci __le16 buf16[4]; 58562306a36Sopenharmony_ci __le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)]; 58662306a36Sopenharmony_ci int rc; 58762306a36Sopenharmony_ci unsigned int i; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci buf16[0] = cpu_to_le16(cur->key.source_type); 59062306a36Sopenharmony_ci buf16[1] = cpu_to_le16(cur->key.target_type); 59162306a36Sopenharmony_ci buf16[2] = cpu_to_le16(cur->key.target_class); 59262306a36Sopenharmony_ci buf16[3] = cpu_to_le16(cur->key.specified); 59362306a36Sopenharmony_ci rc = put_entry(buf16, sizeof(u16), 4, fp); 59462306a36Sopenharmony_ci if (rc) 59562306a36Sopenharmony_ci return rc; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (cur->key.specified & AVTAB_XPERMS) { 59862306a36Sopenharmony_ci rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1, fp); 59962306a36Sopenharmony_ci if (rc) 60062306a36Sopenharmony_ci return rc; 60162306a36Sopenharmony_ci rc = put_entry(&cur->datum.u.xperms->driver, sizeof(u8), 1, fp); 60262306a36Sopenharmony_ci if (rc) 60362306a36Sopenharmony_ci return rc; 60462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cur->datum.u.xperms->perms.p); i++) 60562306a36Sopenharmony_ci buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]); 60662306a36Sopenharmony_ci rc = put_entry(buf32, sizeof(u32), 60762306a36Sopenharmony_ci ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp); 60862306a36Sopenharmony_ci } else { 60962306a36Sopenharmony_ci buf32[0] = cpu_to_le32(cur->datum.u.data); 61062306a36Sopenharmony_ci rc = put_entry(buf32, sizeof(u32), 1, fp); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci if (rc) 61362306a36Sopenharmony_ci return rc; 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ciint avtab_write(struct policydb *p, struct avtab *a, void *fp) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci u32 i; 62062306a36Sopenharmony_ci int rc = 0; 62162306a36Sopenharmony_ci struct avtab_node *cur; 62262306a36Sopenharmony_ci __le32 buf[1]; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci buf[0] = cpu_to_le32(a->nel); 62562306a36Sopenharmony_ci rc = put_entry(buf, sizeof(u32), 1, fp); 62662306a36Sopenharmony_ci if (rc) 62762306a36Sopenharmony_ci return rc; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci for (i = 0; i < a->nslot; i++) { 63062306a36Sopenharmony_ci for (cur = a->htable[i]; cur; 63162306a36Sopenharmony_ci cur = cur->next) { 63262306a36Sopenharmony_ci rc = avtab_write_item(p, cur, fp); 63362306a36Sopenharmony_ci if (rc) 63462306a36Sopenharmony_ci return rc; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return rc; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_civoid __init avtab_cache_init(void) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci avtab_node_cachep = kmem_cache_create("avtab_node", 64462306a36Sopenharmony_ci sizeof(struct avtab_node), 64562306a36Sopenharmony_ci 0, SLAB_PANIC, NULL); 64662306a36Sopenharmony_ci avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms", 64762306a36Sopenharmony_ci sizeof(struct avtab_extended_perms), 64862306a36Sopenharmony_ci 0, SLAB_PANIC, NULL); 64962306a36Sopenharmony_ci} 650