11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Implementation of the access vector table type. 31da177e4SLinus Torvalds * 47efbb60bSStephen Smalley * Author : Stephen Smalley, <sds@tycho.nsa.gov> 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; 27fa1aa143SJeff Vander Stoep static struct kmem_cache *avtab_xperms_cachep; 281da177e4SLinus Torvalds 2933ebc193SJohn Brooks /* Based on MurmurHash3, written by Austin Appleby and placed in the 3033ebc193SJohn Brooks * public domain. 3133ebc193SJohn Brooks */ 3233ebc193SJohn Brooks static inline int avtab_hash(struct avtab_key *keyp, u32 mask) 333232c110SYuichi Nakamura { 3433ebc193SJohn Brooks static const u32 c1 = 0xcc9e2d51; 3533ebc193SJohn Brooks static const u32 c2 = 0x1b873593; 3633ebc193SJohn Brooks static const u32 r1 = 15; 3733ebc193SJohn Brooks static const u32 r2 = 13; 3833ebc193SJohn Brooks static const u32 m = 5; 3933ebc193SJohn Brooks static const u32 n = 0xe6546b64; 4033ebc193SJohn Brooks 4133ebc193SJohn Brooks u32 hash = 0; 4233ebc193SJohn Brooks 4333ebc193SJohn Brooks #define mix(input) { \ 4433ebc193SJohn Brooks u32 v = input; \ 4533ebc193SJohn Brooks v *= c1; \ 4633ebc193SJohn Brooks v = (v << r1) | (v >> (32 - r1)); \ 4733ebc193SJohn Brooks v *= c2; \ 4833ebc193SJohn Brooks hash ^= v; \ 4933ebc193SJohn Brooks hash = (hash << r2) | (hash >> (32 - r2)); \ 5033ebc193SJohn Brooks hash = hash * m + n; \ 5133ebc193SJohn Brooks } 5233ebc193SJohn Brooks 5333ebc193SJohn Brooks mix(keyp->target_class); 5433ebc193SJohn Brooks mix(keyp->target_type); 5533ebc193SJohn Brooks mix(keyp->source_type); 5633ebc193SJohn Brooks 5733ebc193SJohn Brooks #undef mix 5833ebc193SJohn Brooks 5933ebc193SJohn Brooks hash ^= hash >> 16; 6033ebc193SJohn Brooks hash *= 0x85ebca6b; 6133ebc193SJohn Brooks hash ^= hash >> 13; 6233ebc193SJohn Brooks hash *= 0xc2b2ae35; 6333ebc193SJohn Brooks hash ^= hash >> 16; 6433ebc193SJohn Brooks 6533ebc193SJohn Brooks return hash & mask; 663232c110SYuichi Nakamura } 673232c110SYuichi Nakamura 681da177e4SLinus Torvalds static struct avtab_node* 691da177e4SLinus Torvalds avtab_insert_node(struct avtab *h, int hvalue, 701da177e4SLinus Torvalds struct avtab_node *prev, struct avtab_node *cur, 711da177e4SLinus Torvalds struct avtab_key *key, struct avtab_datum *datum) 721da177e4SLinus Torvalds { 731da177e4SLinus Torvalds struct avtab_node *newnode; 74fa1aa143SJeff Vander Stoep struct avtab_extended_perms *xperms; 75c3762229SRobert P. J. Day newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); 761da177e4SLinus Torvalds if (newnode == NULL) 771da177e4SLinus Torvalds return NULL; 781da177e4SLinus Torvalds newnode->key = *key; 79fa1aa143SJeff Vander Stoep 80fa1aa143SJeff Vander Stoep if (key->specified & AVTAB_XPERMS) { 81fa1aa143SJeff Vander Stoep xperms = kmem_cache_zalloc(avtab_xperms_cachep, GFP_KERNEL); 82fa1aa143SJeff Vander Stoep if (xperms == NULL) { 83fa1aa143SJeff Vander Stoep kmem_cache_free(avtab_node_cachep, newnode); 84fa1aa143SJeff Vander Stoep return NULL; 85fa1aa143SJeff Vander Stoep } 86fa1aa143SJeff Vander Stoep *xperms = *(datum->u.xperms); 87fa1aa143SJeff Vander Stoep newnode->datum.u.xperms = xperms; 88fa1aa143SJeff Vander Stoep } else { 89fa1aa143SJeff Vander Stoep newnode->datum.u.data = datum->u.data; 90fa1aa143SJeff Vander Stoep } 91fa1aa143SJeff Vander Stoep 921da177e4SLinus Torvalds if (prev) { 931da177e4SLinus Torvalds newnode->next = prev->next; 941da177e4SLinus Torvalds prev->next = newnode; 951da177e4SLinus Torvalds } else { 96ba39db6eSStephen Smalley newnode->next = flex_array_get_ptr(h->htable, hvalue); 97ba39db6eSStephen Smalley if (flex_array_put_ptr(h->htable, hvalue, newnode, 98ba39db6eSStephen Smalley GFP_KERNEL|__GFP_ZERO)) { 99ba39db6eSStephen Smalley kmem_cache_free(avtab_node_cachep, newnode); 100ba39db6eSStephen Smalley return NULL; 101ba39db6eSStephen Smalley } 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds h->nel++; 1051da177e4SLinus Torvalds return newnode; 1061da177e4SLinus Torvalds } 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum) 1091da177e4SLinus Torvalds { 1101da177e4SLinus Torvalds int hvalue; 1111da177e4SLinus Torvalds struct avtab_node *prev, *cur, *newnode; 112782ebb99SStephen Smalley u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 1131da177e4SLinus Torvalds 1143232c110SYuichi Nakamura if (!h || !h->htable) 1151da177e4SLinus Torvalds return -EINVAL; 1161da177e4SLinus Torvalds 1173232c110SYuichi Nakamura hvalue = avtab_hash(key, h->mask); 118ba39db6eSStephen Smalley for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue); 1191da177e4SLinus Torvalds cur; 1201da177e4SLinus Torvalds prev = cur, cur = cur->next) { 1211da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1221da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1231da177e4SLinus Torvalds key->target_class == cur->key.target_class && 124fa1aa143SJeff Vander Stoep (specified & cur->key.specified)) { 125fa1aa143SJeff Vander Stoep /* extended perms may not be unique */ 126fa1aa143SJeff Vander Stoep if (specified & AVTAB_XPERMS) 127fa1aa143SJeff Vander Stoep break; 1281da177e4SLinus Torvalds return -EEXIST; 129fa1aa143SJeff Vander Stoep } 1301da177e4SLinus Torvalds if (key->source_type < cur->key.source_type) 1311da177e4SLinus Torvalds break; 1321da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1331da177e4SLinus Torvalds key->target_type < cur->key.target_type) 1341da177e4SLinus Torvalds break; 1351da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1361da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1371da177e4SLinus Torvalds key->target_class < cur->key.target_class) 1381da177e4SLinus Torvalds break; 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); 1421da177e4SLinus Torvalds if (!newnode) 1431da177e4SLinus Torvalds return -ENOMEM; 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds return 0; 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds /* Unlike avtab_insert(), this function allow multiple insertions of the same 1491da177e4SLinus Torvalds * key/specified mask into the table, as needed by the conditional avtab. 1501da177e4SLinus Torvalds * It also returns a pointer to the node inserted. 1511da177e4SLinus Torvalds */ 1521da177e4SLinus Torvalds struct avtab_node * 1531da177e4SLinus Torvalds avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum) 1541da177e4SLinus Torvalds { 1551da177e4SLinus Torvalds int hvalue; 1560c0e186fSVesa-Matti J Kari struct avtab_node *prev, *cur; 157782ebb99SStephen Smalley u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 1581da177e4SLinus Torvalds 1593232c110SYuichi Nakamura if (!h || !h->htable) 1601da177e4SLinus Torvalds return NULL; 1613232c110SYuichi Nakamura hvalue = avtab_hash(key, h->mask); 162ba39db6eSStephen Smalley for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue); 1631da177e4SLinus Torvalds cur; 1641da177e4SLinus Torvalds prev = cur, cur = cur->next) { 1651da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1661da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1671da177e4SLinus Torvalds key->target_class == cur->key.target_class && 168782ebb99SStephen Smalley (specified & cur->key.specified)) 1691da177e4SLinus Torvalds break; 1701da177e4SLinus Torvalds if (key->source_type < cur->key.source_type) 1711da177e4SLinus Torvalds break; 1721da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1731da177e4SLinus Torvalds key->target_type < cur->key.target_type) 1741da177e4SLinus Torvalds break; 1751da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1761da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1771da177e4SLinus Torvalds key->target_class < cur->key.target_class) 1781da177e4SLinus Torvalds break; 1791da177e4SLinus Torvalds } 1800c0e186fSVesa-Matti J Kari return avtab_insert_node(h, hvalue, prev, cur, key, datum); 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 183782ebb99SStephen Smalley struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key) 1841da177e4SLinus Torvalds { 1851da177e4SLinus Torvalds int hvalue; 1861da177e4SLinus Torvalds struct avtab_node *cur; 187782ebb99SStephen Smalley u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 1881da177e4SLinus Torvalds 1893232c110SYuichi Nakamura if (!h || !h->htable) 1901da177e4SLinus Torvalds return NULL; 1911da177e4SLinus Torvalds 1923232c110SYuichi Nakamura hvalue = avtab_hash(key, h->mask); 193ba39db6eSStephen Smalley for (cur = flex_array_get_ptr(h->htable, hvalue); cur; 194ba39db6eSStephen Smalley cur = cur->next) { 1951da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 1961da177e4SLinus Torvalds key->target_type == cur->key.target_type && 1971da177e4SLinus Torvalds key->target_class == cur->key.target_class && 198782ebb99SStephen Smalley (specified & cur->key.specified)) 1991da177e4SLinus Torvalds return &cur->datum; 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds if (key->source_type < cur->key.source_type) 2021da177e4SLinus Torvalds break; 2031da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 2041da177e4SLinus Torvalds key->target_type < cur->key.target_type) 2051da177e4SLinus Torvalds break; 2061da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 2071da177e4SLinus Torvalds key->target_type == cur->key.target_type && 2081da177e4SLinus Torvalds key->target_class < cur->key.target_class) 2091da177e4SLinus Torvalds break; 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds return NULL; 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds /* This search function returns a node pointer, and can be used in 2161da177e4SLinus Torvalds * conjunction with avtab_search_next_node() 2171da177e4SLinus Torvalds */ 2181da177e4SLinus Torvalds struct avtab_node* 219782ebb99SStephen Smalley avtab_search_node(struct avtab *h, struct avtab_key *key) 2201da177e4SLinus Torvalds { 2211da177e4SLinus Torvalds int hvalue; 2221da177e4SLinus Torvalds struct avtab_node *cur; 223782ebb99SStephen Smalley u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 2241da177e4SLinus Torvalds 2253232c110SYuichi Nakamura if (!h || !h->htable) 2261da177e4SLinus Torvalds return NULL; 2271da177e4SLinus Torvalds 2283232c110SYuichi Nakamura hvalue = avtab_hash(key, h->mask); 229ba39db6eSStephen Smalley for (cur = flex_array_get_ptr(h->htable, hvalue); cur; 230ba39db6eSStephen Smalley cur = cur->next) { 2311da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 2321da177e4SLinus Torvalds key->target_type == cur->key.target_type && 2331da177e4SLinus Torvalds key->target_class == cur->key.target_class && 234782ebb99SStephen Smalley (specified & cur->key.specified)) 2351da177e4SLinus Torvalds return cur; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds if (key->source_type < cur->key.source_type) 2381da177e4SLinus Torvalds break; 2391da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 2401da177e4SLinus Torvalds key->target_type < cur->key.target_type) 2411da177e4SLinus Torvalds break; 2421da177e4SLinus Torvalds if (key->source_type == cur->key.source_type && 2431da177e4SLinus Torvalds key->target_type == cur->key.target_type && 2441da177e4SLinus Torvalds key->target_class < cur->key.target_class) 2451da177e4SLinus Torvalds break; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds return NULL; 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds struct avtab_node* 2511da177e4SLinus Torvalds avtab_search_node_next(struct avtab_node *node, int specified) 2521da177e4SLinus Torvalds { 2531da177e4SLinus Torvalds struct avtab_node *cur; 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds if (!node) 2561da177e4SLinus Torvalds return NULL; 2571da177e4SLinus Torvalds 258782ebb99SStephen Smalley specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); 2591da177e4SLinus Torvalds for (cur = node->next; cur; cur = cur->next) { 2601da177e4SLinus Torvalds if (node->key.source_type == cur->key.source_type && 2611da177e4SLinus Torvalds node->key.target_type == cur->key.target_type && 2621da177e4SLinus Torvalds node->key.target_class == cur->key.target_class && 263782ebb99SStephen Smalley (specified & cur->key.specified)) 2641da177e4SLinus Torvalds return cur; 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds if (node->key.source_type < cur->key.source_type) 2671da177e4SLinus Torvalds break; 2681da177e4SLinus Torvalds if (node->key.source_type == cur->key.source_type && 2691da177e4SLinus Torvalds node->key.target_type < cur->key.target_type) 2701da177e4SLinus Torvalds break; 2711da177e4SLinus Torvalds if (node->key.source_type == cur->key.source_type && 2721da177e4SLinus Torvalds node->key.target_type == cur->key.target_type && 2731da177e4SLinus Torvalds node->key.target_class < cur->key.target_class) 2741da177e4SLinus Torvalds break; 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds return NULL; 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds void avtab_destroy(struct avtab *h) 2801da177e4SLinus Torvalds { 2811da177e4SLinus Torvalds int i; 2821da177e4SLinus Torvalds struct avtab_node *cur, *temp; 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds if (!h || !h->htable) 2851da177e4SLinus Torvalds return; 2861da177e4SLinus Torvalds 2873232c110SYuichi Nakamura for (i = 0; i < h->nslot; i++) { 288ba39db6eSStephen Smalley cur = flex_array_get_ptr(h->htable, i); 289dbc74c65SVesa-Matti Kari while (cur) { 2901da177e4SLinus Torvalds temp = cur; 2911da177e4SLinus Torvalds cur = cur->next; 292fa1aa143SJeff Vander Stoep if (temp->key.specified & AVTAB_XPERMS) 293fa1aa143SJeff Vander Stoep kmem_cache_free(avtab_xperms_cachep, 294fa1aa143SJeff Vander Stoep temp->datum.u.xperms); 2951da177e4SLinus Torvalds kmem_cache_free(avtab_node_cachep, temp); 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds } 298ba39db6eSStephen Smalley flex_array_free(h->htable); 2991da177e4SLinus Torvalds h->htable = NULL; 3003232c110SYuichi Nakamura h->nslot = 0; 3013232c110SYuichi Nakamura h->mask = 0; 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds int avtab_init(struct avtab *h) 3051da177e4SLinus Torvalds { 3063232c110SYuichi Nakamura h->htable = NULL; 3073232c110SYuichi Nakamura h->nel = 0; 3083232c110SYuichi Nakamura return 0; 3093232c110SYuichi Nakamura } 3101da177e4SLinus Torvalds 3113232c110SYuichi Nakamura int avtab_alloc(struct avtab *h, u32 nrules) 3123232c110SYuichi Nakamura { 31333ebc193SJohn Brooks u32 mask = 0; 3143232c110SYuichi Nakamura u32 shift = 0; 3153232c110SYuichi Nakamura u32 work = nrules; 3163232c110SYuichi Nakamura u32 nslot = 0; 3173232c110SYuichi Nakamura 3183232c110SYuichi Nakamura if (nrules == 0) 3193232c110SYuichi Nakamura goto avtab_alloc_out; 3203232c110SYuichi Nakamura 3213232c110SYuichi Nakamura while (work) { 3223232c110SYuichi Nakamura work = work >> 1; 3233232c110SYuichi Nakamura shift++; 3243232c110SYuichi Nakamura } 3253232c110SYuichi Nakamura if (shift > 2) 3263232c110SYuichi Nakamura shift = shift - 2; 3273232c110SYuichi Nakamura nslot = 1 << shift; 32800d85c83SEric Paris if (nslot > MAX_AVTAB_HASH_BUCKETS) 32900d85c83SEric Paris nslot = MAX_AVTAB_HASH_BUCKETS; 3303232c110SYuichi Nakamura mask = nslot - 1; 3313232c110SYuichi Nakamura 332ba39db6eSStephen Smalley h->htable = flex_array_alloc(sizeof(struct avtab_node *), nslot, 333ba39db6eSStephen Smalley GFP_KERNEL | __GFP_ZERO); 3341da177e4SLinus Torvalds if (!h->htable) 3351da177e4SLinus Torvalds return -ENOMEM; 3363232c110SYuichi Nakamura 3373232c110SYuichi Nakamura avtab_alloc_out: 3381da177e4SLinus Torvalds h->nel = 0; 3393232c110SYuichi Nakamura h->nslot = nslot; 3403232c110SYuichi Nakamura h->mask = mask; 341c87a7e75Speter enderborg pr_debug("SELinux: %d avtab hash slots, %d rules.\n", 342454d972cSJames Morris h->nslot, nrules); 3431da177e4SLinus Torvalds return 0; 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds void avtab_hash_eval(struct avtab *h, char *tag) 3471da177e4SLinus Torvalds { 3481da177e4SLinus Torvalds int i, chain_len, slots_used, max_chain_len; 3493232c110SYuichi Nakamura unsigned long long chain2_len_sum; 3501da177e4SLinus Torvalds struct avtab_node *cur; 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds slots_used = 0; 3531da177e4SLinus Torvalds max_chain_len = 0; 3543232c110SYuichi Nakamura chain2_len_sum = 0; 3553232c110SYuichi Nakamura for (i = 0; i < h->nslot; i++) { 356ba39db6eSStephen Smalley cur = flex_array_get_ptr(h->htable, i); 3571da177e4SLinus Torvalds if (cur) { 3581da177e4SLinus Torvalds slots_used++; 3591da177e4SLinus Torvalds chain_len = 0; 3601da177e4SLinus Torvalds while (cur) { 3611da177e4SLinus Torvalds chain_len++; 3621da177e4SLinus Torvalds cur = cur->next; 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds 3651da177e4SLinus Torvalds if (chain_len > max_chain_len) 3661da177e4SLinus Torvalds max_chain_len = chain_len; 3673232c110SYuichi Nakamura chain2_len_sum += chain_len * chain_len; 3681da177e4SLinus Torvalds } 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 371c87a7e75Speter enderborg pr_debug("SELinux: %s: %d entries and %d/%d buckets used, " 372f5269710SEric Paris "longest chain length %d sum of chain length^2 %llu\n", 3733232c110SYuichi Nakamura tag, h->nel, slots_used, h->nslot, max_chain_len, 3743232c110SYuichi Nakamura chain2_len_sum); 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds 377782ebb99SStephen Smalley static uint16_t spec_order[] = { 378782ebb99SStephen Smalley AVTAB_ALLOWED, 379782ebb99SStephen Smalley AVTAB_AUDITDENY, 380782ebb99SStephen Smalley AVTAB_AUDITALLOW, 381782ebb99SStephen Smalley AVTAB_TRANSITION, 382782ebb99SStephen Smalley AVTAB_CHANGE, 383fa1aa143SJeff Vander Stoep AVTAB_MEMBER, 384fa1aa143SJeff Vander Stoep AVTAB_XPERMS_ALLOWED, 385fa1aa143SJeff Vander Stoep AVTAB_XPERMS_AUDITALLOW, 386fa1aa143SJeff Vander Stoep AVTAB_XPERMS_DONTAUDIT 387782ebb99SStephen Smalley }; 388782ebb99SStephen Smalley 38945e5421eSStephen Smalley int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, 390782ebb99SStephen Smalley int (*insertf)(struct avtab *a, struct avtab_key *k, 391782ebb99SStephen Smalley struct avtab_datum *d, void *p), 392782ebb99SStephen Smalley void *p) 3931da177e4SLinus Torvalds { 394b5bf6c55SAlexey Dobriyan __le16 buf16[4]; 395b5bf6c55SAlexey Dobriyan u16 enabled; 39645e5421eSStephen Smalley u32 items, items2, val, vers = pol->policyvers; 397782ebb99SStephen Smalley struct avtab_key key; 398782ebb99SStephen Smalley struct avtab_datum datum; 399fa1aa143SJeff Vander Stoep struct avtab_extended_perms xperms; 400fa1aa143SJeff Vander Stoep __le32 buf32[ARRAY_SIZE(xperms.perms.p)]; 401782ebb99SStephen Smalley int i, rc; 40245e5421eSStephen Smalley unsigned set; 4031da177e4SLinus Torvalds 404782ebb99SStephen Smalley memset(&key, 0, sizeof(struct avtab_key)); 405782ebb99SStephen Smalley memset(&datum, 0, sizeof(struct avtab_datum)); 4061da177e4SLinus Torvalds 407782ebb99SStephen Smalley if (vers < POLICYDB_VERSION_AVTAB) { 408782ebb99SStephen Smalley rc = next_entry(buf32, fp, sizeof(u32)); 4099e0bd4cbSDan Carpenter if (rc) { 410c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated entry\n"); 4119e0bd4cbSDan Carpenter return rc; 4121da177e4SLinus Torvalds } 413782ebb99SStephen Smalley items2 = le32_to_cpu(buf32[0]); 414782ebb99SStephen Smalley if (items2 > ARRAY_SIZE(buf32)) { 415c87a7e75Speter enderborg pr_err("SELinux: avtab: entry overflow\n"); 4169e0bd4cbSDan Carpenter return -EINVAL; 417782ebb99SStephen Smalley 4181da177e4SLinus Torvalds } 419782ebb99SStephen Smalley rc = next_entry(buf32, fp, sizeof(u32)*items2); 4209e0bd4cbSDan Carpenter if (rc) { 421c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated entry\n"); 4229e0bd4cbSDan Carpenter return rc; 4231da177e4SLinus Torvalds } 4241da177e4SLinus Torvalds items = 0; 4251da177e4SLinus Torvalds 426782ebb99SStephen Smalley val = le32_to_cpu(buf32[items++]); 427782ebb99SStephen Smalley key.source_type = (u16)val; 428782ebb99SStephen Smalley if (key.source_type != val) { 429c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated source type\n"); 4309e0bd4cbSDan Carpenter return -EINVAL; 431782ebb99SStephen Smalley } 432782ebb99SStephen Smalley val = le32_to_cpu(buf32[items++]); 433782ebb99SStephen Smalley key.target_type = (u16)val; 434782ebb99SStephen Smalley if (key.target_type != val) { 435c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated target type\n"); 4369e0bd4cbSDan Carpenter return -EINVAL; 437782ebb99SStephen Smalley } 438782ebb99SStephen Smalley val = le32_to_cpu(buf32[items++]); 439782ebb99SStephen Smalley key.target_class = (u16)val; 440782ebb99SStephen Smalley if (key.target_class != val) { 441c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated target class\n"); 4429e0bd4cbSDan Carpenter return -EINVAL; 4431da177e4SLinus Torvalds } 4441da177e4SLinus Torvalds 445782ebb99SStephen Smalley val = le32_to_cpu(buf32[items++]); 446782ebb99SStephen Smalley enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0; 447782ebb99SStephen Smalley 448782ebb99SStephen Smalley if (!(val & (AVTAB_AV | AVTAB_TYPE))) { 449c87a7e75Speter enderborg pr_err("SELinux: avtab: null entry\n"); 4509e0bd4cbSDan Carpenter return -EINVAL; 451782ebb99SStephen Smalley } 452782ebb99SStephen Smalley if ((val & AVTAB_AV) && 453782ebb99SStephen Smalley (val & AVTAB_TYPE)) { 454c87a7e75Speter enderborg pr_err("SELinux: avtab: entry has both access vectors and types\n"); 4559e0bd4cbSDan Carpenter return -EINVAL; 456782ebb99SStephen Smalley } 457fa1aa143SJeff Vander Stoep if (val & AVTAB_XPERMS) { 458c87a7e75Speter enderborg pr_err("SELinux: avtab: entry has extended permissions\n"); 459fa1aa143SJeff Vander Stoep return -EINVAL; 460fa1aa143SJeff Vander Stoep } 461782ebb99SStephen Smalley 46232725ad8STobias Klauser for (i = 0; i < ARRAY_SIZE(spec_order); i++) { 463782ebb99SStephen Smalley if (val & spec_order[i]) { 464782ebb99SStephen Smalley key.specified = spec_order[i] | enabled; 465fa1aa143SJeff Vander Stoep datum.u.data = le32_to_cpu(buf32[items++]); 466782ebb99SStephen Smalley rc = insertf(a, &key, &datum, p); 467eb5df9a7SEric Paris if (rc) 468eb5df9a7SEric Paris return rc; 469782ebb99SStephen Smalley } 470782ebb99SStephen Smalley } 471782ebb99SStephen Smalley 472782ebb99SStephen Smalley if (items != items2) { 473c87a7e75Speter enderborg pr_err("SELinux: avtab: entry only had %d items, expected %d\n", 474c87a7e75Speter enderborg items2, items); 4759e0bd4cbSDan Carpenter return -EINVAL; 476782ebb99SStephen Smalley } 477782ebb99SStephen Smalley return 0; 478782ebb99SStephen Smalley } 479782ebb99SStephen Smalley 480782ebb99SStephen Smalley rc = next_entry(buf16, fp, sizeof(u16)*4); 4819e0bd4cbSDan Carpenter if (rc) { 482c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated entry\n"); 4839e0bd4cbSDan Carpenter return rc; 484782ebb99SStephen Smalley } 485782ebb99SStephen Smalley 486782ebb99SStephen Smalley items = 0; 487782ebb99SStephen Smalley key.source_type = le16_to_cpu(buf16[items++]); 488782ebb99SStephen Smalley key.target_type = le16_to_cpu(buf16[items++]); 489782ebb99SStephen Smalley key.target_class = le16_to_cpu(buf16[items++]); 490782ebb99SStephen Smalley key.specified = le16_to_cpu(buf16[items++]); 491782ebb99SStephen Smalley 49245e5421eSStephen Smalley if (!policydb_type_isvalid(pol, key.source_type) || 49345e5421eSStephen Smalley !policydb_type_isvalid(pol, key.target_type) || 49445e5421eSStephen Smalley !policydb_class_isvalid(pol, key.target_class)) { 495c87a7e75Speter enderborg pr_err("SELinux: avtab: invalid type or class\n"); 4969e0bd4cbSDan Carpenter return -EINVAL; 49745e5421eSStephen Smalley } 49845e5421eSStephen Smalley 49945e5421eSStephen Smalley set = 0; 50045e5421eSStephen Smalley for (i = 0; i < ARRAY_SIZE(spec_order); i++) { 50145e5421eSStephen Smalley if (key.specified & spec_order[i]) 50245e5421eSStephen Smalley set++; 50345e5421eSStephen Smalley } 50445e5421eSStephen Smalley if (!set || set > 1) { 505c87a7e75Speter enderborg pr_err("SELinux: avtab: more than one specifier\n"); 5069e0bd4cbSDan Carpenter return -EINVAL; 50745e5421eSStephen Smalley } 50845e5421eSStephen Smalley 509fa1aa143SJeff Vander Stoep if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) && 510fa1aa143SJeff Vander Stoep (key.specified & AVTAB_XPERMS)) { 511c87a7e75Speter enderborg pr_err("SELinux: avtab: policy version %u does not " 512fa1aa143SJeff Vander Stoep "support extended permissions rules and one " 513fa1aa143SJeff Vander Stoep "was specified\n", vers); 514fa1aa143SJeff Vander Stoep return -EINVAL; 515fa1aa143SJeff Vander Stoep } else if (key.specified & AVTAB_XPERMS) { 516fa1aa143SJeff Vander Stoep memset(&xperms, 0, sizeof(struct avtab_extended_perms)); 517fa1aa143SJeff Vander Stoep rc = next_entry(&xperms.specified, fp, sizeof(u8)); 518fa1aa143SJeff Vander Stoep if (rc) { 519c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated entry\n"); 520fa1aa143SJeff Vander Stoep return rc; 521fa1aa143SJeff Vander Stoep } 522fa1aa143SJeff Vander Stoep rc = next_entry(&xperms.driver, fp, sizeof(u8)); 523fa1aa143SJeff Vander Stoep if (rc) { 524c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated entry\n"); 525fa1aa143SJeff Vander Stoep return rc; 526fa1aa143SJeff Vander Stoep } 527fa1aa143SJeff Vander Stoep rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(xperms.perms.p)); 528fa1aa143SJeff Vander Stoep if (rc) { 529c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated entry\n"); 530fa1aa143SJeff Vander Stoep return rc; 531fa1aa143SJeff Vander Stoep } 532fa1aa143SJeff Vander Stoep for (i = 0; i < ARRAY_SIZE(xperms.perms.p); i++) 533fa1aa143SJeff Vander Stoep xperms.perms.p[i] = le32_to_cpu(buf32[i]); 534fa1aa143SJeff Vander Stoep datum.u.xperms = &xperms; 535fa1aa143SJeff Vander Stoep } else { 536782ebb99SStephen Smalley rc = next_entry(buf32, fp, sizeof(u32)); 5379e0bd4cbSDan Carpenter if (rc) { 538c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated entry\n"); 5399e0bd4cbSDan Carpenter return rc; 540782ebb99SStephen Smalley } 541fa1aa143SJeff Vander Stoep datum.u.data = le32_to_cpu(*buf32); 542fa1aa143SJeff Vander Stoep } 54345e5421eSStephen Smalley if ((key.specified & AVTAB_TYPE) && 544fa1aa143SJeff Vander Stoep !policydb_type_isvalid(pol, datum.u.data)) { 545c87a7e75Speter enderborg pr_err("SELinux: avtab: invalid type\n"); 5469e0bd4cbSDan Carpenter return -EINVAL; 54745e5421eSStephen Smalley } 548782ebb99SStephen Smalley return insertf(a, &key, &datum, p); 549782ebb99SStephen Smalley } 550782ebb99SStephen Smalley 551782ebb99SStephen Smalley static int avtab_insertf(struct avtab *a, struct avtab_key *k, 552782ebb99SStephen Smalley struct avtab_datum *d, void *p) 553782ebb99SStephen Smalley { 554782ebb99SStephen Smalley return avtab_insert(a, k, d); 555782ebb99SStephen Smalley } 556782ebb99SStephen Smalley 55745e5421eSStephen Smalley int avtab_read(struct avtab *a, void *fp, struct policydb *pol) 5581da177e4SLinus Torvalds { 5591da177e4SLinus Torvalds int rc; 560b5bf6c55SAlexey Dobriyan __le32 buf[1]; 5611da177e4SLinus Torvalds u32 nel, i; 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds 5641da177e4SLinus Torvalds rc = next_entry(buf, fp, sizeof(u32)); 5651da177e4SLinus Torvalds if (rc < 0) { 566c87a7e75Speter enderborg pr_err("SELinux: avtab: truncated table\n"); 5671da177e4SLinus Torvalds goto bad; 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds nel = le32_to_cpu(buf[0]); 5701da177e4SLinus Torvalds if (!nel) { 571c87a7e75Speter enderborg pr_err("SELinux: avtab: table is empty\n"); 5721da177e4SLinus Torvalds rc = -EINVAL; 5731da177e4SLinus Torvalds goto bad; 5741da177e4SLinus Torvalds } 5753232c110SYuichi Nakamura 5763232c110SYuichi Nakamura rc = avtab_alloc(a, nel); 5773232c110SYuichi Nakamura if (rc) 5783232c110SYuichi Nakamura goto bad; 5793232c110SYuichi Nakamura 5801da177e4SLinus Torvalds for (i = 0; i < nel; i++) { 58145e5421eSStephen Smalley rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL); 5821da177e4SLinus Torvalds if (rc) { 5831da177e4SLinus Torvalds if (rc == -ENOMEM) 584c87a7e75Speter enderborg pr_err("SELinux: avtab: out of memory\n"); 585782ebb99SStephen Smalley else if (rc == -EEXIST) 586c87a7e75Speter enderborg pr_err("SELinux: avtab: duplicate entry\n"); 5879e0bd4cbSDan Carpenter 5881da177e4SLinus Torvalds goto bad; 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds } 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds rc = 0; 5931da177e4SLinus Torvalds out: 5941da177e4SLinus Torvalds return rc; 5951da177e4SLinus Torvalds 5961da177e4SLinus Torvalds bad: 5971da177e4SLinus Torvalds avtab_destroy(a); 5981da177e4SLinus Torvalds goto out; 5991da177e4SLinus Torvalds } 6001da177e4SLinus Torvalds 601cee74f47SEric Paris int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) 602cee74f47SEric Paris { 603cee74f47SEric Paris __le16 buf16[4]; 604fa1aa143SJeff Vander Stoep __le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)]; 605cee74f47SEric Paris int rc; 606fa1aa143SJeff Vander Stoep unsigned int i; 607cee74f47SEric Paris 608cee74f47SEric Paris buf16[0] = cpu_to_le16(cur->key.source_type); 609cee74f47SEric Paris buf16[1] = cpu_to_le16(cur->key.target_type); 610cee74f47SEric Paris buf16[2] = cpu_to_le16(cur->key.target_class); 611cee74f47SEric Paris buf16[3] = cpu_to_le16(cur->key.specified); 612cee74f47SEric Paris rc = put_entry(buf16, sizeof(u16), 4, fp); 613cee74f47SEric Paris if (rc) 614cee74f47SEric Paris return rc; 615fa1aa143SJeff Vander Stoep 616fa1aa143SJeff Vander Stoep if (cur->key.specified & AVTAB_XPERMS) { 617fa1aa143SJeff Vander Stoep rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1, fp); 618fa1aa143SJeff Vander Stoep if (rc) 619fa1aa143SJeff Vander Stoep return rc; 620fa1aa143SJeff Vander Stoep rc = put_entry(&cur->datum.u.xperms->driver, sizeof(u8), 1, fp); 621fa1aa143SJeff Vander Stoep if (rc) 622fa1aa143SJeff Vander Stoep return rc; 623fa1aa143SJeff Vander Stoep for (i = 0; i < ARRAY_SIZE(cur->datum.u.xperms->perms.p); i++) 624fa1aa143SJeff Vander Stoep buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]); 625fa1aa143SJeff Vander Stoep rc = put_entry(buf32, sizeof(u32), 626fa1aa143SJeff Vander Stoep ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp); 627fa1aa143SJeff Vander Stoep } else { 628fa1aa143SJeff Vander Stoep buf32[0] = cpu_to_le32(cur->datum.u.data); 629cee74f47SEric Paris rc = put_entry(buf32, sizeof(u32), 1, fp); 630fa1aa143SJeff Vander Stoep } 631cee74f47SEric Paris if (rc) 632cee74f47SEric Paris return rc; 633cee74f47SEric Paris return 0; 634cee74f47SEric Paris } 635cee74f47SEric Paris 636cee74f47SEric Paris int avtab_write(struct policydb *p, struct avtab *a, void *fp) 637cee74f47SEric Paris { 638cee74f47SEric Paris unsigned int i; 639cee74f47SEric Paris int rc = 0; 640cee74f47SEric Paris struct avtab_node *cur; 641cee74f47SEric Paris __le32 buf[1]; 642cee74f47SEric Paris 643cee74f47SEric Paris buf[0] = cpu_to_le32(a->nel); 644cee74f47SEric Paris rc = put_entry(buf, sizeof(u32), 1, fp); 645cee74f47SEric Paris if (rc) 646cee74f47SEric Paris return rc; 647cee74f47SEric Paris 648cee74f47SEric Paris for (i = 0; i < a->nslot; i++) { 649ba39db6eSStephen Smalley for (cur = flex_array_get_ptr(a->htable, i); cur; 650ba39db6eSStephen Smalley cur = cur->next) { 651cee74f47SEric Paris rc = avtab_write_item(p, cur, fp); 652cee74f47SEric Paris if (rc) 653cee74f47SEric Paris return rc; 654cee74f47SEric Paris } 655cee74f47SEric Paris } 656cee74f47SEric Paris 657cee74f47SEric Paris return rc; 658cee74f47SEric Paris } 659aa8e712cSStephen Smalley 660aa8e712cSStephen Smalley void __init avtab_cache_init(void) 6611da177e4SLinus Torvalds { 6621da177e4SLinus Torvalds avtab_node_cachep = kmem_cache_create("avtab_node", 6631da177e4SLinus Torvalds sizeof(struct avtab_node), 66420c2df83SPaul Mundt 0, SLAB_PANIC, NULL); 665fa1aa143SJeff Vander Stoep avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms", 666fa1aa143SJeff Vander Stoep sizeof(struct avtab_extended_perms), 667fa1aa143SJeff Vander Stoep 0, SLAB_PANIC, NULL); 6681da177e4SLinus Torvalds } 669