11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Implementation of the access vector table type. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Author : Stephen Smalley, <sds@epoch.ncsc.mil> 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds /* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Added conditional policy language extensions 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * Copyright (C) 2003 Tresys Technology, LLC 121da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 131da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 141da177e4SLinus Torvalds * the Free Software Foundation, version 2. 153232c110SYuichi Nakamura * 163232c110SYuichi Nakamura * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp> 173232c110SYuichi Nakamura * Tuned number of hash slots for avtab to reduce memory usage 181da177e4SLinus Torvalds */ 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds #include <linux/kernel.h> 211da177e4SLinus Torvalds #include <linux/slab.h> 221da177e4SLinus Torvalds #include <linux/errno.h> 231da177e4SLinus Torvalds #include "avtab.h" 241da177e4SLinus Torvalds #include "policydb.h" 251da177e4SLinus Torvalds 26e18b890bSChristoph Lameter static struct kmem_cache *avtab_node_cachep; 271da177e4SLinus Torvalds 2833ebc193SJohn Brooks /* Based on MurmurHash3, written by Austin Appleby and placed in the 2933ebc193SJohn Brooks * public domain. 3033ebc193SJohn Brooks */ 3133ebc193SJohn Brooks static inline int avtab_hash(struct avtab_key *keyp, u32 mask) 323232c110SYuichi Nakamura { 3333ebc193SJohn Brooks static const u32 c1 = 0xcc9e2d51; 3433ebc193SJohn Brooks static const u32 c2 = 0x1b873593; 3533ebc193SJohn Brooks static const u32 r1 = 15; 3633ebc193SJohn Brooks static const u32 r2 = 13; 3733ebc193SJohn Brooks static const u32 m = 5; 3833ebc193SJohn Brooks static const u32 n = 0xe6546b64; 3933ebc193SJohn Brooks 4033ebc193SJohn Brooks u32 hash = 0; 4133ebc193SJohn Brooks 4233ebc193SJohn Brooks #define mix(input) { \ 4333ebc193SJohn Brooks u32 v = input; \ 4433ebc193SJohn Brooks v *= c1; \ 4533ebc193SJohn Brooks v = (v << r1) | (v >> (32 - r1)); \ 4633ebc193SJohn Brooks v *= c2; \ 4733ebc193SJohn Brooks hash ^= v; \ 4833ebc193SJohn Brooks hash = (hash << r2) | (hash >> (32 - r2)); \ 4933ebc193SJohn Brooks hash = hash * m + n; \ 5033ebc193SJohn Brooks } 5133ebc193SJohn Brooks 5233ebc193SJohn Brooks mix(keyp->target_class); 5333ebc193SJohn Brooks mix(keyp->target_type); 5433ebc193SJohn Brooks mix(keyp->source_type); 5533ebc193SJohn Brooks 5633ebc193SJohn Brooks #undef mix 5733ebc193SJohn Brooks 5833ebc193SJohn Brooks hash ^= hash >> 16; 5933ebc193SJohn Brooks hash *= 0x85ebca6b; 6033ebc193SJohn Brooks hash ^= hash >> 13; 6133ebc193SJohn Brooks hash *= 0xc2b2ae35; 6233ebc193SJohn Brooks hash ^= hash >> 16; 6333ebc193SJohn Brooks 6433ebc193SJohn Brooks return hash & mask; 653232c110SYuichi Nakamura } 663232c110SYuichi Nakamura 671da177e4SLinus Torvalds static struct avtab_node* 681da177e4SLinus Torvalds avtab_insert_node(struct avtab *h, int hvalue, 691da177e4SLinus Torvalds struct avtab_node *prev, struct avtab_node *cur, 701da177e4SLinus Torvalds struct avtab_key *key, struct avtab_datum *datum) 711da177e4SLinus Torvalds { 721da177e4SLinus Torvalds struct avtab_node *newnode; 73c3762229SRobert P. J. Day newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); 741da177e4SLinus Torvalds if (newnode == NULL) 751da177e4SLinus Torvalds return NULL; 761da177e4SLinus Torvalds newnode->key = *key; 771da177e4SLinus Torvalds newnode->datum = *datum; 781da177e4SLinus Torvalds if (prev) { 791da177e4SLinus Torvalds newnode->next = prev->next; 801da177e4SLinus Torvalds prev->next = newnode; 811da177e4SLinus Torvalds } else { 82ba39db6eSStephen Smalley newnode->next = flex_array_get_ptr(h->htable, hvalue); 83ba39db6eSStephen Smalley if (flex_array_put_ptr(h->htable, hvalue, newnode, 84ba39db6eSStephen Smalley GFP_KERNEL|__GFP_ZERO)) { 85ba39db6eSStephen Smalley kmem_cache_free(avtab_node_cachep, newnode); 86ba39db6eSStephen Smalley return NULL; 87ba39db6eSStephen Smalley } 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds h->nel++; 911da177e4SLinus Torvalds return newnode; 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum) 951da177e4SLinus Torvalds { 961da177e4SLinus Torvalds int hvalue; 971da177e4SLinus Torvalds struct avtab_node *prev, *cur, *newnode; 98782ebb99SStephen Smalley u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 991da177e4SLinus Torvalds 1003232c110SYuichi Nakamura if (!h || !h->htable) 1011da177e4SLinus Torvalds return -EINVAL; 1021da177e4SLinus Torvalds 1033232c110SYuichi Nakamura hvalue = avtab_hash(key, h->mask); 104ba39db6eSStephen Smalley for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue); 1051da177e4SLinus Torvalds cur; 1061da177e4SLinus Torvalds prev = cur, cur = cur->next) { 1071da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1081da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1091da177e4SLinus Torvalds key->target_class == cur->key.target_class && 110782ebb99SStephen Smalley (specified & cur->key.specified)) 1111da177e4SLinus Torvalds return -EEXIST; 1121da177e4SLinus Torvalds if (key->source_type < cur->key.source_type) 1131da177e4SLinus Torvalds break; 1141da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1151da177e4SLinus Torvalds key->target_type < cur->key.target_type) 1161da177e4SLinus Torvalds break; 1171da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1181da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1191da177e4SLinus Torvalds key->target_class < cur->key.target_class) 1201da177e4SLinus Torvalds break; 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); 1241da177e4SLinus Torvalds if (!newnode) 1251da177e4SLinus Torvalds return -ENOMEM; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds return 0; 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds /* Unlike avtab_insert(), this function allow multiple insertions of the same 1311da177e4SLinus Torvalds * key/specified mask into the table, as needed by the conditional avtab. 1321da177e4SLinus Torvalds * It also returns a pointer to the node inserted. 1331da177e4SLinus Torvalds */ 1341da177e4SLinus Torvalds struct avtab_node * 1351da177e4SLinus Torvalds avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum) 1361da177e4SLinus Torvalds { 1371da177e4SLinus Torvalds int hvalue; 1380c0e186fSVesa-Matti J Kari struct avtab_node *prev, *cur; 139782ebb99SStephen Smalley u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 1401da177e4SLinus Torvalds 1413232c110SYuichi Nakamura if (!h || !h->htable) 1421da177e4SLinus Torvalds return NULL; 1433232c110SYuichi Nakamura hvalue = avtab_hash(key, h->mask); 144ba39db6eSStephen Smalley for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue); 1451da177e4SLinus Torvalds cur; 1461da177e4SLinus Torvalds prev = cur, cur = cur->next) { 1471da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1481da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1491da177e4SLinus Torvalds key->target_class == cur->key.target_class && 150782ebb99SStephen Smalley (specified & cur->key.specified)) 1511da177e4SLinus Torvalds break; 1521da177e4SLinus Torvalds if (key->source_type < cur->key.source_type) 1531da177e4SLinus Torvalds break; 1541da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1551da177e4SLinus Torvalds key->target_type < cur->key.target_type) 1561da177e4SLinus Torvalds break; 1571da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1581da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1591da177e4SLinus Torvalds key->target_class < cur->key.target_class) 1601da177e4SLinus Torvalds break; 1611da177e4SLinus Torvalds } 1620c0e186fSVesa-Matti J Kari return avtab_insert_node(h, hvalue, prev, cur, key, datum); 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 165782ebb99SStephen Smalley struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key) 1661da177e4SLinus Torvalds { 1671da177e4SLinus Torvalds int hvalue; 1681da177e4SLinus Torvalds struct avtab_node *cur; 169782ebb99SStephen Smalley u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 1701da177e4SLinus Torvalds 1713232c110SYuichi Nakamura if (!h || !h->htable) 1721da177e4SLinus Torvalds return NULL; 1731da177e4SLinus Torvalds 1743232c110SYuichi Nakamura hvalue = avtab_hash(key, h->mask); 175ba39db6eSStephen Smalley for (cur = flex_array_get_ptr(h->htable, hvalue); cur; 176ba39db6eSStephen Smalley cur = cur->next) { 1771da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1781da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1791da177e4SLinus Torvalds key->target_class == cur->key.target_class && 180782ebb99SStephen Smalley (specified & cur->key.specified)) 1811da177e4SLinus Torvalds return &cur->datum; 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds if (key->source_type < cur->key.source_type) 1841da177e4SLinus Torvalds break; 1851da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1861da177e4SLinus Torvalds key->target_type < cur->key.target_type) 1871da177e4SLinus Torvalds break; 1881da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1891da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1901da177e4SLinus Torvalds key->target_class < cur->key.target_class) 1911da177e4SLinus Torvalds break; 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds return NULL; 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds /* This search function returns a node pointer, and can be used in 1981da177e4SLinus Torvalds * conjunction with avtab_search_next_node() 1991da177e4SLinus Torvalds */ 2001da177e4SLinus Torvalds struct avtab_node* 201782ebb99SStephen Smalley avtab_search_node(struct avtab *h, struct avtab_key *key) 2021da177e4SLinus Torvalds { 2031da177e4SLinus Torvalds int hvalue; 2041da177e4SLinus Torvalds struct avtab_node *cur; 205782ebb99SStephen Smalley u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 2061da177e4SLinus Torvalds 2073232c110SYuichi Nakamura if (!h || !h->htable) 2081da177e4SLinus Torvalds return NULL; 2091da177e4SLinus Torvalds 2103232c110SYuichi Nakamura hvalue = avtab_hash(key, h->mask); 211ba39db6eSStephen Smalley for (cur = flex_array_get_ptr(h->htable, hvalue); cur; 212ba39db6eSStephen Smalley cur = cur->next) { 2131da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 2141da177e4SLinus Torvalds key->target_type == cur->key.target_type && 2151da177e4SLinus Torvalds key->target_class == cur->key.target_class && 216782ebb99SStephen Smalley (specified & cur->key.specified)) 2171da177e4SLinus Torvalds return cur; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds if (key->source_type < cur->key.source_type) 2201da177e4SLinus Torvalds break; 2211da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 2221da177e4SLinus Torvalds key->target_type < cur->key.target_type) 2231da177e4SLinus Torvalds break; 2241da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 2251da177e4SLinus Torvalds key->target_type == cur->key.target_type && 2261da177e4SLinus Torvalds key->target_class < cur->key.target_class) 2271da177e4SLinus Torvalds break; 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds return NULL; 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds struct avtab_node* 2331da177e4SLinus Torvalds avtab_search_node_next(struct avtab_node *node, int specified) 2341da177e4SLinus Torvalds { 2351da177e4SLinus Torvalds struct avtab_node *cur; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds if (!node) 2381da177e4SLinus Torvalds return NULL; 2391da177e4SLinus Torvalds 240782ebb99SStephen Smalley specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 2411da177e4SLinus Torvalds for (cur = node->next; cur; cur = cur->next) { 2421da177e4SLinus Torvalds if (node->key.source_type == cur->key.source_type && 2431da177e4SLinus Torvalds node->key.target_type == cur->key.target_type && 2441da177e4SLinus Torvalds node->key.target_class == cur->key.target_class && 245782ebb99SStephen Smalley (specified & cur->key.specified)) 2461da177e4SLinus Torvalds return cur; 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds if (node->key.source_type < cur->key.source_type) 2491da177e4SLinus Torvalds break; 2501da177e4SLinus Torvalds if (node->key.source_type == cur->key.source_type && 2511da177e4SLinus Torvalds node->key.target_type < cur->key.target_type) 2521da177e4SLinus Torvalds break; 2531da177e4SLinus Torvalds if (node->key.source_type == cur->key.source_type && 2541da177e4SLinus Torvalds node->key.target_type == cur->key.target_type && 2551da177e4SLinus Torvalds node->key.target_class < cur->key.target_class) 2561da177e4SLinus Torvalds break; 2571da177e4SLinus Torvalds } 2581da177e4SLinus Torvalds return NULL; 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds void avtab_destroy(struct avtab *h) 2621da177e4SLinus Torvalds { 2631da177e4SLinus Torvalds int i; 2641da177e4SLinus Torvalds struct avtab_node *cur, *temp; 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds if (!h || !h->htable) 2671da177e4SLinus Torvalds return; 2681da177e4SLinus Torvalds 2693232c110SYuichi Nakamura for (i = 0; i < h->nslot; i++) { 270ba39db6eSStephen Smalley cur = flex_array_get_ptr(h->htable, i); 271dbc74c65SVesa-Matti Kari while (cur) { 2721da177e4SLinus Torvalds temp = cur; 2731da177e4SLinus Torvalds cur = cur->next; 2741da177e4SLinus Torvalds kmem_cache_free(avtab_node_cachep, temp); 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds } 277ba39db6eSStephen Smalley flex_array_free(h->htable); 2781da177e4SLinus Torvalds h->htable = NULL; 2793232c110SYuichi Nakamura h->nslot = 0; 2803232c110SYuichi Nakamura h->mask = 0; 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds int avtab_init(struct avtab *h) 2841da177e4SLinus Torvalds { 2853232c110SYuichi Nakamura h->htable = NULL; 2863232c110SYuichi Nakamura h->nel = 0; 2873232c110SYuichi Nakamura return 0; 2883232c110SYuichi Nakamura } 2891da177e4SLinus Torvalds 2903232c110SYuichi Nakamura int avtab_alloc(struct avtab *h, u32 nrules) 2913232c110SYuichi Nakamura { 29233ebc193SJohn Brooks u32 mask = 0; 2933232c110SYuichi Nakamura u32 shift = 0; 2943232c110SYuichi Nakamura u32 work = nrules; 2953232c110SYuichi Nakamura u32 nslot = 0; 2963232c110SYuichi Nakamura 2973232c110SYuichi Nakamura if (nrules == 0) 2983232c110SYuichi Nakamura goto avtab_alloc_out; 2993232c110SYuichi Nakamura 3003232c110SYuichi Nakamura while (work) { 3013232c110SYuichi Nakamura work = work >> 1; 3023232c110SYuichi Nakamura shift++; 3033232c110SYuichi Nakamura } 3043232c110SYuichi Nakamura if (shift > 2) 3053232c110SYuichi Nakamura shift = shift - 2; 3063232c110SYuichi Nakamura nslot = 1 << shift; 30700d85c83SEric Paris if (nslot > MAX_AVTAB_HASH_BUCKETS) 30800d85c83SEric Paris nslot = MAX_AVTAB_HASH_BUCKETS; 3093232c110SYuichi Nakamura mask = nslot - 1; 3103232c110SYuichi Nakamura 311ba39db6eSStephen Smalley h->htable = flex_array_alloc(sizeof(struct avtab_node *), nslot, 312ba39db6eSStephen Smalley GFP_KERNEL | __GFP_ZERO); 3131da177e4SLinus Torvalds if (!h->htable) 3141da177e4SLinus Torvalds return -ENOMEM; 3153232c110SYuichi Nakamura 3163232c110SYuichi Nakamura avtab_alloc_out: 3171da177e4SLinus Torvalds h->nel = 0; 3183232c110SYuichi Nakamura h->nslot = nslot; 3193232c110SYuichi Nakamura h->mask = mask; 320454d972cSJames Morris printk(KERN_DEBUG "SELinux: %d avtab hash slots, %d rules.\n", 321454d972cSJames Morris h->nslot, nrules); 3221da177e4SLinus Torvalds return 0; 3231da177e4SLinus Torvalds } 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds void avtab_hash_eval(struct avtab *h, char *tag) 3261da177e4SLinus Torvalds { 3271da177e4SLinus Torvalds int i, chain_len, slots_used, max_chain_len; 3283232c110SYuichi Nakamura unsigned long long chain2_len_sum; 3291da177e4SLinus Torvalds struct avtab_node *cur; 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds slots_used = 0; 3321da177e4SLinus Torvalds max_chain_len = 0; 3333232c110SYuichi Nakamura chain2_len_sum = 0; 3343232c110SYuichi Nakamura for (i = 0; i < h->nslot; i++) { 335ba39db6eSStephen Smalley cur = flex_array_get_ptr(h->htable, i); 3361da177e4SLinus Torvalds if (cur) { 3371da177e4SLinus Torvalds slots_used++; 3381da177e4SLinus Torvalds chain_len = 0; 3391da177e4SLinus Torvalds while (cur) { 3401da177e4SLinus Torvalds chain_len++; 3411da177e4SLinus Torvalds cur = cur->next; 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds if (chain_len > max_chain_len) 3451da177e4SLinus Torvalds max_chain_len = chain_len; 3463232c110SYuichi Nakamura chain2_len_sum += chain_len * chain_len; 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds } 3491da177e4SLinus Torvalds 350744ba35eSEric Paris printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " 351f5269710SEric Paris "longest chain length %d sum of chain length^2 %llu\n", 3523232c110SYuichi Nakamura tag, h->nel, slots_used, h->nslot, max_chain_len, 3533232c110SYuichi Nakamura chain2_len_sum); 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 356782ebb99SStephen Smalley static uint16_t spec_order[] = { 357782ebb99SStephen Smalley AVTAB_ALLOWED, 358782ebb99SStephen Smalley AVTAB_AUDITDENY, 359782ebb99SStephen Smalley AVTAB_AUDITALLOW, 360782ebb99SStephen Smalley AVTAB_TRANSITION, 361782ebb99SStephen Smalley AVTAB_CHANGE, 362782ebb99SStephen Smalley AVTAB_MEMBER 363782ebb99SStephen Smalley }; 364782ebb99SStephen Smalley 36545e5421eSStephen Smalley int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, 366782ebb99SStephen Smalley int (*insertf)(struct avtab *a, struct avtab_key *k, 367782ebb99SStephen Smalley struct avtab_datum *d, void *p), 368782ebb99SStephen Smalley void *p) 3691da177e4SLinus Torvalds { 370b5bf6c55SAlexey Dobriyan __le16 buf16[4]; 371b5bf6c55SAlexey Dobriyan u16 enabled; 372b5bf6c55SAlexey Dobriyan __le32 buf32[7]; 37345e5421eSStephen Smalley u32 items, items2, val, vers = pol->policyvers; 374782ebb99SStephen Smalley struct avtab_key key; 375782ebb99SStephen Smalley struct avtab_datum datum; 376782ebb99SStephen Smalley int i, rc; 37745e5421eSStephen Smalley unsigned set; 3781da177e4SLinus Torvalds 379782ebb99SStephen Smalley memset(&key, 0, sizeof(struct avtab_key)); 380782ebb99SStephen Smalley memset(&datum, 0, sizeof(struct avtab_datum)); 3811da177e4SLinus Torvalds 382782ebb99SStephen Smalley if (vers < POLICYDB_VERSION_AVTAB) { 383782ebb99SStephen Smalley rc = next_entry(buf32, fp, sizeof(u32)); 3849e0bd4cbSDan Carpenter if (rc) { 385454d972cSJames Morris printk(KERN_ERR "SELinux: avtab: truncated entry\n"); 3869e0bd4cbSDan Carpenter return rc; 3871da177e4SLinus Torvalds } 388782ebb99SStephen Smalley items2 = le32_to_cpu(buf32[0]); 389782ebb99SStephen Smalley if (items2 > ARRAY_SIZE(buf32)) { 390454d972cSJames Morris printk(KERN_ERR "SELinux: avtab: entry overflow\n"); 3919e0bd4cbSDan Carpenter return -EINVAL; 392782ebb99SStephen Smalley 3931da177e4SLinus Torvalds } 394782ebb99SStephen Smalley rc = next_entry(buf32, fp, sizeof(u32)*items2); 3959e0bd4cbSDan Carpenter if (rc) { 396454d972cSJames Morris printk(KERN_ERR "SELinux: avtab: truncated entry\n"); 3979e0bd4cbSDan Carpenter return rc; 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds items = 0; 4001da177e4SLinus Torvalds 401782ebb99SStephen Smalley val = le32_to_cpu(buf32[items++]); 402782ebb99SStephen Smalley key.source_type = (u16)val; 403782ebb99SStephen Smalley if (key.source_type != val) { 404744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: truncated source type\n"); 4059e0bd4cbSDan Carpenter return -EINVAL; 406782ebb99SStephen Smalley } 407782ebb99SStephen Smalley val = le32_to_cpu(buf32[items++]); 408782ebb99SStephen Smalley key.target_type = (u16)val; 409782ebb99SStephen Smalley if (key.target_type != val) { 410744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: truncated target type\n"); 4119e0bd4cbSDan Carpenter return -EINVAL; 412782ebb99SStephen Smalley } 413782ebb99SStephen Smalley val = le32_to_cpu(buf32[items++]); 414782ebb99SStephen Smalley key.target_class = (u16)val; 415782ebb99SStephen Smalley if (key.target_class != val) { 416744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: truncated target class\n"); 4179e0bd4cbSDan Carpenter return -EINVAL; 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds 420782ebb99SStephen Smalley val = le32_to_cpu(buf32[items++]); 421782ebb99SStephen Smalley enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0; 422782ebb99SStephen Smalley 423782ebb99SStephen Smalley if (!(val & (AVTAB_AV | AVTAB_TYPE))) { 424744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: null entry\n"); 4259e0bd4cbSDan Carpenter return -EINVAL; 426782ebb99SStephen Smalley } 427782ebb99SStephen Smalley if ((val & AVTAB_AV) && 428782ebb99SStephen Smalley (val & AVTAB_TYPE)) { 429744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); 4309e0bd4cbSDan Carpenter return -EINVAL; 431782ebb99SStephen Smalley } 432782ebb99SStephen Smalley 43332725ad8STobias Klauser for (i = 0; i < ARRAY_SIZE(spec_order); i++) { 434782ebb99SStephen Smalley if (val & spec_order[i]) { 435782ebb99SStephen Smalley key.specified = spec_order[i] | enabled; 436782ebb99SStephen Smalley datum.data = le32_to_cpu(buf32[items++]); 437782ebb99SStephen Smalley rc = insertf(a, &key, &datum, p); 438eb5df9a7SEric Paris if (rc) 439eb5df9a7SEric Paris return rc; 440782ebb99SStephen Smalley } 441782ebb99SStephen Smalley } 442782ebb99SStephen Smalley 443782ebb99SStephen Smalley if (items != items2) { 444744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items); 4459e0bd4cbSDan Carpenter return -EINVAL; 446782ebb99SStephen Smalley } 447782ebb99SStephen Smalley return 0; 448782ebb99SStephen Smalley } 449782ebb99SStephen Smalley 450782ebb99SStephen Smalley rc = next_entry(buf16, fp, sizeof(u16)*4); 4519e0bd4cbSDan Carpenter if (rc) { 452744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: truncated entry\n"); 4539e0bd4cbSDan Carpenter return rc; 454782ebb99SStephen Smalley } 455782ebb99SStephen Smalley 456782ebb99SStephen Smalley items = 0; 457782ebb99SStephen Smalley key.source_type = le16_to_cpu(buf16[items++]); 458782ebb99SStephen Smalley key.target_type = le16_to_cpu(buf16[items++]); 459782ebb99SStephen Smalley key.target_class = le16_to_cpu(buf16[items++]); 460782ebb99SStephen Smalley key.specified = le16_to_cpu(buf16[items++]); 461782ebb99SStephen Smalley 46245e5421eSStephen Smalley if (!policydb_type_isvalid(pol, key.source_type) || 46345e5421eSStephen Smalley !policydb_type_isvalid(pol, key.target_type) || 46445e5421eSStephen Smalley !policydb_class_isvalid(pol, key.target_class)) { 465744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: invalid type or class\n"); 4669e0bd4cbSDan Carpenter return -EINVAL; 46745e5421eSStephen Smalley } 46845e5421eSStephen Smalley 46945e5421eSStephen Smalley set = 0; 47045e5421eSStephen Smalley for (i = 0; i < ARRAY_SIZE(spec_order); i++) { 47145e5421eSStephen Smalley if (key.specified & spec_order[i]) 47245e5421eSStephen Smalley set++; 47345e5421eSStephen Smalley } 47445e5421eSStephen Smalley if (!set || set > 1) { 475744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: more than one specifier\n"); 4769e0bd4cbSDan Carpenter return -EINVAL; 47745e5421eSStephen Smalley } 47845e5421eSStephen Smalley 479782ebb99SStephen Smalley rc = next_entry(buf32, fp, sizeof(u32)); 4809e0bd4cbSDan Carpenter if (rc) { 481744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: truncated entry\n"); 4829e0bd4cbSDan Carpenter return rc; 483782ebb99SStephen Smalley } 484782ebb99SStephen Smalley datum.data = le32_to_cpu(*buf32); 48545e5421eSStephen Smalley if ((key.specified & AVTAB_TYPE) && 48645e5421eSStephen Smalley !policydb_type_isvalid(pol, datum.data)) { 487744ba35eSEric Paris printk(KERN_ERR "SELinux: avtab: invalid type\n"); 4889e0bd4cbSDan Carpenter return -EINVAL; 48945e5421eSStephen Smalley } 490782ebb99SStephen Smalley return insertf(a, &key, &datum, p); 491782ebb99SStephen Smalley } 492782ebb99SStephen Smalley 493782ebb99SStephen Smalley static int avtab_insertf(struct avtab *a, struct avtab_key *k, 494782ebb99SStephen Smalley struct avtab_datum *d, void *p) 495782ebb99SStephen Smalley { 496782ebb99SStephen Smalley return avtab_insert(a, k, d); 497782ebb99SStephen Smalley } 498782ebb99SStephen Smalley 49945e5421eSStephen Smalley int avtab_read(struct avtab *a, void *fp, struct policydb *pol) 5001da177e4SLinus Torvalds { 5011da177e4SLinus Torvalds int rc; 502b5bf6c55SAlexey Dobriyan __le32 buf[1]; 5031da177e4SLinus Torvalds u32 nel, i; 5041da177e4SLinus Torvalds 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds rc = next_entry(buf, fp, sizeof(u32)); 5071da177e4SLinus Torvalds if (rc < 0) { 508454d972cSJames Morris printk(KERN_ERR "SELinux: avtab: truncated table\n"); 5091da177e4SLinus Torvalds goto bad; 5101da177e4SLinus Torvalds } 5111da177e4SLinus Torvalds nel = le32_to_cpu(buf[0]); 5121da177e4SLinus Torvalds if (!nel) { 513454d972cSJames Morris printk(KERN_ERR "SELinux: avtab: table is empty\n"); 5141da177e4SLinus Torvalds rc = -EINVAL; 5151da177e4SLinus Torvalds goto bad; 5161da177e4SLinus Torvalds } 5173232c110SYuichi Nakamura 5183232c110SYuichi Nakamura rc = avtab_alloc(a, nel); 5193232c110SYuichi Nakamura if (rc) 5203232c110SYuichi Nakamura goto bad; 5213232c110SYuichi Nakamura 5221da177e4SLinus Torvalds for (i = 0; i < nel; i++) { 52345e5421eSStephen Smalley rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL); 5241da177e4SLinus Torvalds if (rc) { 5251da177e4SLinus Torvalds if (rc == -ENOMEM) 526454d972cSJames Morris printk(KERN_ERR "SELinux: avtab: out of memory\n"); 527782ebb99SStephen Smalley else if (rc == -EEXIST) 528454d972cSJames Morris printk(KERN_ERR "SELinux: avtab: duplicate entry\n"); 5299e0bd4cbSDan Carpenter 5301da177e4SLinus Torvalds goto bad; 5311da177e4SLinus Torvalds } 5321da177e4SLinus Torvalds } 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds rc = 0; 5351da177e4SLinus Torvalds out: 5361da177e4SLinus Torvalds return rc; 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds bad: 5391da177e4SLinus Torvalds avtab_destroy(a); 5401da177e4SLinus Torvalds goto out; 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 543cee74f47SEric Paris int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) 544cee74f47SEric Paris { 545cee74f47SEric Paris __le16 buf16[4]; 546cee74f47SEric Paris __le32 buf32[1]; 547cee74f47SEric Paris int rc; 548cee74f47SEric Paris 549cee74f47SEric Paris buf16[0] = cpu_to_le16(cur->key.source_type); 550cee74f47SEric Paris buf16[1] = cpu_to_le16(cur->key.target_type); 551cee74f47SEric Paris buf16[2] = cpu_to_le16(cur->key.target_class); 552cee74f47SEric Paris buf16[3] = cpu_to_le16(cur->key.specified); 553cee74f47SEric Paris rc = put_entry(buf16, sizeof(u16), 4, fp); 554cee74f47SEric Paris if (rc) 555cee74f47SEric Paris return rc; 556cee74f47SEric Paris buf32[0] = cpu_to_le32(cur->datum.data); 557cee74f47SEric Paris rc = put_entry(buf32, sizeof(u32), 1, fp); 558cee74f47SEric Paris if (rc) 559cee74f47SEric Paris return rc; 560cee74f47SEric Paris return 0; 561cee74f47SEric Paris } 562cee74f47SEric Paris 563cee74f47SEric Paris int avtab_write(struct policydb *p, struct avtab *a, void *fp) 564cee74f47SEric Paris { 565cee74f47SEric Paris unsigned int i; 566cee74f47SEric Paris int rc = 0; 567cee74f47SEric Paris struct avtab_node *cur; 568cee74f47SEric Paris __le32 buf[1]; 569cee74f47SEric Paris 570cee74f47SEric Paris buf[0] = cpu_to_le32(a->nel); 571cee74f47SEric Paris rc = put_entry(buf, sizeof(u32), 1, fp); 572cee74f47SEric Paris if (rc) 573cee74f47SEric Paris return rc; 574cee74f47SEric Paris 575cee74f47SEric Paris for (i = 0; i < a->nslot; i++) { 576ba39db6eSStephen Smalley for (cur = flex_array_get_ptr(a->htable, i); cur; 577ba39db6eSStephen Smalley cur = cur->next) { 578cee74f47SEric Paris rc = avtab_write_item(p, cur, fp); 579cee74f47SEric Paris if (rc) 580cee74f47SEric Paris return rc; 581cee74f47SEric Paris } 582cee74f47SEric Paris } 583cee74f47SEric Paris 584cee74f47SEric Paris return rc; 585cee74f47SEric Paris } 5861da177e4SLinus Torvalds void avtab_cache_init(void) 5871da177e4SLinus Torvalds { 5881da177e4SLinus Torvalds avtab_node_cachep = kmem_cache_create("avtab_node", 5891da177e4SLinus Torvalds sizeof(struct avtab_node), 59020c2df83SPaul Mundt 0, SLAB_PANIC, NULL); 5911da177e4SLinus Torvalds } 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds void avtab_cache_destroy(void) 5941da177e4SLinus Torvalds { 5951da177e4SLinus Torvalds kmem_cache_destroy(avtab_node_cachep); 5961da177e4SLinus Torvalds } 597