18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Implementation of the kernel access vector cache (AVC). 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Stephen Smalley, <sds@tycho.nsa.gov> 68c2ecf20Sopenharmony_ci * James Morris <jmorris@redhat.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Update: KaiGai, Kohei <kaigai@ak.jp.nec.com> 98c2ecf20Sopenharmony_ci * Replaced the avc_lock spinlock by RCU. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/stddef.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/fs.h> 188c2ecf20Sopenharmony_ci#include <linux/dcache.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 218c2ecf20Sopenharmony_ci#include <linux/percpu.h> 228c2ecf20Sopenharmony_ci#include <linux/list.h> 238c2ecf20Sopenharmony_ci#include <net/sock.h> 248c2ecf20Sopenharmony_ci#include <linux/un.h> 258c2ecf20Sopenharmony_ci#include <net/af_unix.h> 268c2ecf20Sopenharmony_ci#include <linux/ip.h> 278c2ecf20Sopenharmony_ci#include <linux/audit.h> 288c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 298c2ecf20Sopenharmony_ci#include <net/ipv6.h> 308c2ecf20Sopenharmony_ci#include "avc.h" 318c2ecf20Sopenharmony_ci#include "avc_ss.h" 328c2ecf20Sopenharmony_ci#include "classmap.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 358c2ecf20Sopenharmony_ci#include <trace/events/avc.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define AVC_CACHE_SLOTS 512 388c2ecf20Sopenharmony_ci#define AVC_DEF_CACHE_THRESHOLD 512 398c2ecf20Sopenharmony_ci#define AVC_CACHE_RECLAIM 16 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS 428c2ecf20Sopenharmony_ci#define avc_cache_stats_incr(field) this_cpu_inc(avc_cache_stats.field) 438c2ecf20Sopenharmony_ci#else 448c2ecf20Sopenharmony_ci#define avc_cache_stats_incr(field) do {} while (0) 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct avc_entry { 488c2ecf20Sopenharmony_ci u32 ssid; 498c2ecf20Sopenharmony_ci u32 tsid; 508c2ecf20Sopenharmony_ci u16 tclass; 518c2ecf20Sopenharmony_ci struct av_decision avd; 528c2ecf20Sopenharmony_ci struct avc_xperms_node *xp_node; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct avc_node { 568c2ecf20Sopenharmony_ci struct avc_entry ae; 578c2ecf20Sopenharmony_ci struct hlist_node list; /* anchored in avc_cache->slots[i] */ 588c2ecf20Sopenharmony_ci struct rcu_head rhead; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct avc_xperms_decision_node { 628c2ecf20Sopenharmony_ci struct extended_perms_decision xpd; 638c2ecf20Sopenharmony_ci struct list_head xpd_list; /* list of extended_perms_decision */ 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct avc_xperms_node { 678c2ecf20Sopenharmony_ci struct extended_perms xp; 688c2ecf20Sopenharmony_ci struct list_head xpd_head; /* list head of extended_perms_decision */ 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct avc_cache { 728c2ecf20Sopenharmony_ci struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ 738c2ecf20Sopenharmony_ci spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ 748c2ecf20Sopenharmony_ci atomic_t lru_hint; /* LRU hint for reclaim scan */ 758c2ecf20Sopenharmony_ci atomic_t active_nodes; 768c2ecf20Sopenharmony_ci u32 latest_notif; /* latest revocation notification */ 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistruct avc_callback_node { 808c2ecf20Sopenharmony_ci int (*callback) (u32 event); 818c2ecf20Sopenharmony_ci u32 events; 828c2ecf20Sopenharmony_ci struct avc_callback_node *next; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS 868c2ecf20Sopenharmony_ciDEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; 878c2ecf20Sopenharmony_ci#endif 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistruct selinux_avc { 908c2ecf20Sopenharmony_ci unsigned int avc_cache_threshold; 918c2ecf20Sopenharmony_ci struct avc_cache avc_cache; 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct selinux_avc selinux_avc; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_civoid selinux_avc_init(struct selinux_avc **avc) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int i; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci selinux_avc.avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD; 1018c2ecf20Sopenharmony_ci for (i = 0; i < AVC_CACHE_SLOTS; i++) { 1028c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(&selinux_avc.avc_cache.slots[i]); 1038c2ecf20Sopenharmony_ci spin_lock_init(&selinux_avc.avc_cache.slots_lock[i]); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci atomic_set(&selinux_avc.avc_cache.active_nodes, 0); 1068c2ecf20Sopenharmony_ci atomic_set(&selinux_avc.avc_cache.lru_hint, 0); 1078c2ecf20Sopenharmony_ci *avc = &selinux_avc; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciunsigned int avc_get_cache_threshold(struct selinux_avc *avc) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return avc->avc_cache_threshold; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_civoid avc_set_cache_threshold(struct selinux_avc *avc, 1168c2ecf20Sopenharmony_ci unsigned int cache_threshold) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci avc->avc_cache_threshold = cache_threshold; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic struct avc_callback_node *avc_callbacks; 1228c2ecf20Sopenharmony_cistatic struct kmem_cache *avc_node_cachep; 1238c2ecf20Sopenharmony_cistatic struct kmem_cache *avc_xperms_data_cachep; 1248c2ecf20Sopenharmony_cistatic struct kmem_cache *avc_xperms_decision_cachep; 1258c2ecf20Sopenharmony_cistatic struct kmem_cache *avc_xperms_cachep; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/** 1338c2ecf20Sopenharmony_ci * avc_init - Initialize the AVC. 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * Initialize the access vector cache. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_civoid __init avc_init(void) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), 1408c2ecf20Sopenharmony_ci 0, SLAB_PANIC, NULL); 1418c2ecf20Sopenharmony_ci avc_xperms_cachep = kmem_cache_create("avc_xperms_node", 1428c2ecf20Sopenharmony_ci sizeof(struct avc_xperms_node), 1438c2ecf20Sopenharmony_ci 0, SLAB_PANIC, NULL); 1448c2ecf20Sopenharmony_ci avc_xperms_decision_cachep = kmem_cache_create( 1458c2ecf20Sopenharmony_ci "avc_xperms_decision_node", 1468c2ecf20Sopenharmony_ci sizeof(struct avc_xperms_decision_node), 1478c2ecf20Sopenharmony_ci 0, SLAB_PANIC, NULL); 1488c2ecf20Sopenharmony_ci avc_xperms_data_cachep = kmem_cache_create("avc_xperms_data", 1498c2ecf20Sopenharmony_ci sizeof(struct extended_perms_data), 1508c2ecf20Sopenharmony_ci 0, SLAB_PANIC, NULL); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciint avc_get_hash_stats(struct selinux_avc *avc, char *page) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci int i, chain_len, max_chain_len, slots_used; 1568c2ecf20Sopenharmony_ci struct avc_node *node; 1578c2ecf20Sopenharmony_ci struct hlist_head *head; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci rcu_read_lock(); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci slots_used = 0; 1628c2ecf20Sopenharmony_ci max_chain_len = 0; 1638c2ecf20Sopenharmony_ci for (i = 0; i < AVC_CACHE_SLOTS; i++) { 1648c2ecf20Sopenharmony_ci head = &avc->avc_cache.slots[i]; 1658c2ecf20Sopenharmony_ci if (!hlist_empty(head)) { 1668c2ecf20Sopenharmony_ci slots_used++; 1678c2ecf20Sopenharmony_ci chain_len = 0; 1688c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(node, head, list) 1698c2ecf20Sopenharmony_ci chain_len++; 1708c2ecf20Sopenharmony_ci if (chain_len > max_chain_len) 1718c2ecf20Sopenharmony_ci max_chain_len = chain_len; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci rcu_read_unlock(); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n" 1788c2ecf20Sopenharmony_ci "longest chain: %d\n", 1798c2ecf20Sopenharmony_ci atomic_read(&avc->avc_cache.active_nodes), 1808c2ecf20Sopenharmony_ci slots_used, AVC_CACHE_SLOTS, max_chain_len); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* 1848c2ecf20Sopenharmony_ci * using a linked list for extended_perms_decision lookup because the list is 1858c2ecf20Sopenharmony_ci * always small. i.e. less than 5, typically 1 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_cistatic struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver, 1888c2ecf20Sopenharmony_ci struct avc_xperms_node *xp_node) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct avc_xperms_decision_node *xpd_node; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) { 1938c2ecf20Sopenharmony_ci if (xpd_node->xpd.driver == driver) 1948c2ecf20Sopenharmony_ci return &xpd_node->xpd; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci return NULL; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic inline unsigned int 2008c2ecf20Sopenharmony_ciavc_xperms_has_perm(struct extended_perms_decision *xpd, 2018c2ecf20Sopenharmony_ci u8 perm, u8 which) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci unsigned int rc = 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if ((which == XPERMS_ALLOWED) && 2068c2ecf20Sopenharmony_ci (xpd->used & XPERMS_ALLOWED)) 2078c2ecf20Sopenharmony_ci rc = security_xperm_test(xpd->allowed->p, perm); 2088c2ecf20Sopenharmony_ci else if ((which == XPERMS_AUDITALLOW) && 2098c2ecf20Sopenharmony_ci (xpd->used & XPERMS_AUDITALLOW)) 2108c2ecf20Sopenharmony_ci rc = security_xperm_test(xpd->auditallow->p, perm); 2118c2ecf20Sopenharmony_ci else if ((which == XPERMS_DONTAUDIT) && 2128c2ecf20Sopenharmony_ci (xpd->used & XPERMS_DONTAUDIT)) 2138c2ecf20Sopenharmony_ci rc = security_xperm_test(xpd->dontaudit->p, perm); 2148c2ecf20Sopenharmony_ci return rc; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void avc_xperms_allow_perm(struct avc_xperms_node *xp_node, 2188c2ecf20Sopenharmony_ci u8 driver, u8 perm) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct extended_perms_decision *xpd; 2218c2ecf20Sopenharmony_ci security_xperm_set(xp_node->xp.drivers.p, driver); 2228c2ecf20Sopenharmony_ci xpd = avc_xperms_decision_lookup(driver, xp_node); 2238c2ecf20Sopenharmony_ci if (xpd && xpd->allowed) 2248c2ecf20Sopenharmony_ci security_xperm_set(xpd->allowed->p, perm); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void avc_xperms_decision_free(struct avc_xperms_decision_node *xpd_node) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct extended_perms_decision *xpd; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci xpd = &xpd_node->xpd; 2328c2ecf20Sopenharmony_ci if (xpd->allowed) 2338c2ecf20Sopenharmony_ci kmem_cache_free(avc_xperms_data_cachep, xpd->allowed); 2348c2ecf20Sopenharmony_ci if (xpd->auditallow) 2358c2ecf20Sopenharmony_ci kmem_cache_free(avc_xperms_data_cachep, xpd->auditallow); 2368c2ecf20Sopenharmony_ci if (xpd->dontaudit) 2378c2ecf20Sopenharmony_ci kmem_cache_free(avc_xperms_data_cachep, xpd->dontaudit); 2388c2ecf20Sopenharmony_ci kmem_cache_free(avc_xperms_decision_cachep, xpd_node); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void avc_xperms_free(struct avc_xperms_node *xp_node) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct avc_xperms_decision_node *xpd_node, *tmp; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (!xp_node) 2468c2ecf20Sopenharmony_ci return; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci list_for_each_entry_safe(xpd_node, tmp, &xp_node->xpd_head, xpd_list) { 2498c2ecf20Sopenharmony_ci list_del(&xpd_node->xpd_list); 2508c2ecf20Sopenharmony_ci avc_xperms_decision_free(xpd_node); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci kmem_cache_free(avc_xperms_cachep, xp_node); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void avc_copy_xperms_decision(struct extended_perms_decision *dest, 2568c2ecf20Sopenharmony_ci struct extended_perms_decision *src) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci dest->driver = src->driver; 2598c2ecf20Sopenharmony_ci dest->used = src->used; 2608c2ecf20Sopenharmony_ci if (dest->used & XPERMS_ALLOWED) 2618c2ecf20Sopenharmony_ci memcpy(dest->allowed->p, src->allowed->p, 2628c2ecf20Sopenharmony_ci sizeof(src->allowed->p)); 2638c2ecf20Sopenharmony_ci if (dest->used & XPERMS_AUDITALLOW) 2648c2ecf20Sopenharmony_ci memcpy(dest->auditallow->p, src->auditallow->p, 2658c2ecf20Sopenharmony_ci sizeof(src->auditallow->p)); 2668c2ecf20Sopenharmony_ci if (dest->used & XPERMS_DONTAUDIT) 2678c2ecf20Sopenharmony_ci memcpy(dest->dontaudit->p, src->dontaudit->p, 2688c2ecf20Sopenharmony_ci sizeof(src->dontaudit->p)); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* 2728c2ecf20Sopenharmony_ci * similar to avc_copy_xperms_decision, but only copy decision 2738c2ecf20Sopenharmony_ci * information relevant to this perm 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_cistatic inline void avc_quick_copy_xperms_decision(u8 perm, 2768c2ecf20Sopenharmony_ci struct extended_perms_decision *dest, 2778c2ecf20Sopenharmony_ci struct extended_perms_decision *src) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * compute index of the u32 of the 256 bits (8 u32s) that contain this 2818c2ecf20Sopenharmony_ci * command permission 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci u8 i = perm >> 5; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci dest->used = src->used; 2868c2ecf20Sopenharmony_ci if (dest->used & XPERMS_ALLOWED) 2878c2ecf20Sopenharmony_ci dest->allowed->p[i] = src->allowed->p[i]; 2888c2ecf20Sopenharmony_ci if (dest->used & XPERMS_AUDITALLOW) 2898c2ecf20Sopenharmony_ci dest->auditallow->p[i] = src->auditallow->p[i]; 2908c2ecf20Sopenharmony_ci if (dest->used & XPERMS_DONTAUDIT) 2918c2ecf20Sopenharmony_ci dest->dontaudit->p[i] = src->dontaudit->p[i]; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic struct avc_xperms_decision_node 2958c2ecf20Sopenharmony_ci *avc_xperms_decision_alloc(u8 which) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct avc_xperms_decision_node *xpd_node; 2988c2ecf20Sopenharmony_ci struct extended_perms_decision *xpd; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep, 3018c2ecf20Sopenharmony_ci GFP_NOWAIT | __GFP_NOWARN); 3028c2ecf20Sopenharmony_ci if (!xpd_node) 3038c2ecf20Sopenharmony_ci return NULL; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci xpd = &xpd_node->xpd; 3068c2ecf20Sopenharmony_ci if (which & XPERMS_ALLOWED) { 3078c2ecf20Sopenharmony_ci xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep, 3088c2ecf20Sopenharmony_ci GFP_NOWAIT | __GFP_NOWARN); 3098c2ecf20Sopenharmony_ci if (!xpd->allowed) 3108c2ecf20Sopenharmony_ci goto error; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci if (which & XPERMS_AUDITALLOW) { 3138c2ecf20Sopenharmony_ci xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep, 3148c2ecf20Sopenharmony_ci GFP_NOWAIT | __GFP_NOWARN); 3158c2ecf20Sopenharmony_ci if (!xpd->auditallow) 3168c2ecf20Sopenharmony_ci goto error; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci if (which & XPERMS_DONTAUDIT) { 3198c2ecf20Sopenharmony_ci xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep, 3208c2ecf20Sopenharmony_ci GFP_NOWAIT | __GFP_NOWARN); 3218c2ecf20Sopenharmony_ci if (!xpd->dontaudit) 3228c2ecf20Sopenharmony_ci goto error; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci return xpd_node; 3258c2ecf20Sopenharmony_cierror: 3268c2ecf20Sopenharmony_ci avc_xperms_decision_free(xpd_node); 3278c2ecf20Sopenharmony_ci return NULL; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int avc_add_xperms_decision(struct avc_node *node, 3318c2ecf20Sopenharmony_ci struct extended_perms_decision *src) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct avc_xperms_decision_node *dest_xpd; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci node->ae.xp_node->xp.len++; 3368c2ecf20Sopenharmony_ci dest_xpd = avc_xperms_decision_alloc(src->used); 3378c2ecf20Sopenharmony_ci if (!dest_xpd) 3388c2ecf20Sopenharmony_ci return -ENOMEM; 3398c2ecf20Sopenharmony_ci avc_copy_xperms_decision(&dest_xpd->xpd, src); 3408c2ecf20Sopenharmony_ci list_add(&dest_xpd->xpd_list, &node->ae.xp_node->xpd_head); 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic struct avc_xperms_node *avc_xperms_alloc(void) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct avc_xperms_node *xp_node; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci xp_node = kmem_cache_zalloc(avc_xperms_cachep, GFP_NOWAIT | __GFP_NOWARN); 3498c2ecf20Sopenharmony_ci if (!xp_node) 3508c2ecf20Sopenharmony_ci return xp_node; 3518c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&xp_node->xpd_head); 3528c2ecf20Sopenharmony_ci return xp_node; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int avc_xperms_populate(struct avc_node *node, 3568c2ecf20Sopenharmony_ci struct avc_xperms_node *src) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct avc_xperms_node *dest; 3598c2ecf20Sopenharmony_ci struct avc_xperms_decision_node *dest_xpd; 3608c2ecf20Sopenharmony_ci struct avc_xperms_decision_node *src_xpd; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (src->xp.len == 0) 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci dest = avc_xperms_alloc(); 3658c2ecf20Sopenharmony_ci if (!dest) 3668c2ecf20Sopenharmony_ci return -ENOMEM; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p)); 3698c2ecf20Sopenharmony_ci dest->xp.len = src->xp.len; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* for each source xpd allocate a destination xpd and copy */ 3728c2ecf20Sopenharmony_ci list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) { 3738c2ecf20Sopenharmony_ci dest_xpd = avc_xperms_decision_alloc(src_xpd->xpd.used); 3748c2ecf20Sopenharmony_ci if (!dest_xpd) 3758c2ecf20Sopenharmony_ci goto error; 3768c2ecf20Sopenharmony_ci avc_copy_xperms_decision(&dest_xpd->xpd, &src_xpd->xpd); 3778c2ecf20Sopenharmony_ci list_add(&dest_xpd->xpd_list, &dest->xpd_head); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci node->ae.xp_node = dest; 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_cierror: 3828c2ecf20Sopenharmony_ci avc_xperms_free(dest); 3838c2ecf20Sopenharmony_ci return -ENOMEM; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic inline u32 avc_xperms_audit_required(u32 requested, 3888c2ecf20Sopenharmony_ci struct av_decision *avd, 3898c2ecf20Sopenharmony_ci struct extended_perms_decision *xpd, 3908c2ecf20Sopenharmony_ci u8 perm, 3918c2ecf20Sopenharmony_ci int result, 3928c2ecf20Sopenharmony_ci u32 *deniedp) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci u32 denied, audited; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci denied = requested & ~avd->allowed; 3978c2ecf20Sopenharmony_ci if (unlikely(denied)) { 3988c2ecf20Sopenharmony_ci audited = denied & avd->auditdeny; 3998c2ecf20Sopenharmony_ci if (audited && xpd) { 4008c2ecf20Sopenharmony_ci if (avc_xperms_has_perm(xpd, perm, XPERMS_DONTAUDIT)) 4018c2ecf20Sopenharmony_ci audited &= ~requested; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } else if (result) { 4048c2ecf20Sopenharmony_ci audited = denied = requested; 4058c2ecf20Sopenharmony_ci } else { 4068c2ecf20Sopenharmony_ci audited = requested & avd->auditallow; 4078c2ecf20Sopenharmony_ci if (audited && xpd) { 4088c2ecf20Sopenharmony_ci if (!avc_xperms_has_perm(xpd, perm, XPERMS_AUDITALLOW)) 4098c2ecf20Sopenharmony_ci audited &= ~requested; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci *deniedp = denied; 4148c2ecf20Sopenharmony_ci return audited; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic inline int avc_xperms_audit(struct selinux_state *state, 4188c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, u16 tclass, 4198c2ecf20Sopenharmony_ci u32 requested, struct av_decision *avd, 4208c2ecf20Sopenharmony_ci struct extended_perms_decision *xpd, 4218c2ecf20Sopenharmony_ci u8 perm, int result, 4228c2ecf20Sopenharmony_ci struct common_audit_data *ad) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci u32 audited, denied; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci audited = avc_xperms_audit_required( 4278c2ecf20Sopenharmony_ci requested, avd, xpd, perm, result, &denied); 4288c2ecf20Sopenharmony_ci if (likely(!audited)) 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci return slow_avc_audit(state, ssid, tsid, tclass, requested, 4318c2ecf20Sopenharmony_ci audited, denied, result, ad); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void avc_node_free(struct rcu_head *rhead) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct avc_node *node = container_of(rhead, struct avc_node, rhead); 4378c2ecf20Sopenharmony_ci avc_xperms_free(node->ae.xp_node); 4388c2ecf20Sopenharmony_ci kmem_cache_free(avc_node_cachep, node); 4398c2ecf20Sopenharmony_ci avc_cache_stats_incr(frees); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void avc_node_delete(struct selinux_avc *avc, struct avc_node *node) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci hlist_del_rcu(&node->list); 4458c2ecf20Sopenharmony_ci call_rcu(&node->rhead, avc_node_free); 4468c2ecf20Sopenharmony_ci atomic_dec(&avc->avc_cache.active_nodes); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic void avc_node_kill(struct selinux_avc *avc, struct avc_node *node) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci avc_xperms_free(node->ae.xp_node); 4528c2ecf20Sopenharmony_ci kmem_cache_free(avc_node_cachep, node); 4538c2ecf20Sopenharmony_ci avc_cache_stats_incr(frees); 4548c2ecf20Sopenharmony_ci atomic_dec(&avc->avc_cache.active_nodes); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void avc_node_replace(struct selinux_avc *avc, 4588c2ecf20Sopenharmony_ci struct avc_node *new, struct avc_node *old) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci hlist_replace_rcu(&old->list, &new->list); 4618c2ecf20Sopenharmony_ci call_rcu(&old->rhead, avc_node_free); 4628c2ecf20Sopenharmony_ci atomic_dec(&avc->avc_cache.active_nodes); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic inline int avc_reclaim_node(struct selinux_avc *avc) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct avc_node *node; 4688c2ecf20Sopenharmony_ci int hvalue, try, ecx; 4698c2ecf20Sopenharmony_ci unsigned long flags; 4708c2ecf20Sopenharmony_ci struct hlist_head *head; 4718c2ecf20Sopenharmony_ci spinlock_t *lock; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) { 4748c2ecf20Sopenharmony_ci hvalue = atomic_inc_return(&avc->avc_cache.lru_hint) & 4758c2ecf20Sopenharmony_ci (AVC_CACHE_SLOTS - 1); 4768c2ecf20Sopenharmony_ci head = &avc->avc_cache.slots[hvalue]; 4778c2ecf20Sopenharmony_ci lock = &avc->avc_cache.slots_lock[hvalue]; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (!spin_trylock_irqsave(lock, flags)) 4808c2ecf20Sopenharmony_ci continue; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci rcu_read_lock(); 4838c2ecf20Sopenharmony_ci hlist_for_each_entry(node, head, list) { 4848c2ecf20Sopenharmony_ci avc_node_delete(avc, node); 4858c2ecf20Sopenharmony_ci avc_cache_stats_incr(reclaims); 4868c2ecf20Sopenharmony_ci ecx++; 4878c2ecf20Sopenharmony_ci if (ecx >= AVC_CACHE_RECLAIM) { 4888c2ecf20Sopenharmony_ci rcu_read_unlock(); 4898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 4908c2ecf20Sopenharmony_ci goto out; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci rcu_read_unlock(); 4948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ciout: 4978c2ecf20Sopenharmony_ci return ecx; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic struct avc_node *avc_alloc_node(struct selinux_avc *avc) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct avc_node *node; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT | __GFP_NOWARN); 5058c2ecf20Sopenharmony_ci if (!node) 5068c2ecf20Sopenharmony_ci goto out; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&node->list); 5098c2ecf20Sopenharmony_ci avc_cache_stats_incr(allocations); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (atomic_inc_return(&avc->avc_cache.active_nodes) > 5128c2ecf20Sopenharmony_ci avc->avc_cache_threshold) 5138c2ecf20Sopenharmony_ci avc_reclaim_node(avc); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ciout: 5168c2ecf20Sopenharmony_ci return node; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci node->ae.ssid = ssid; 5228c2ecf20Sopenharmony_ci node->ae.tsid = tsid; 5238c2ecf20Sopenharmony_ci node->ae.tclass = tclass; 5248c2ecf20Sopenharmony_ci memcpy(&node->ae.avd, avd, sizeof(node->ae.avd)); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic inline struct avc_node *avc_search_node(struct selinux_avc *avc, 5288c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, u16 tclass) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct avc_node *node, *ret = NULL; 5318c2ecf20Sopenharmony_ci int hvalue; 5328c2ecf20Sopenharmony_ci struct hlist_head *head; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci hvalue = avc_hash(ssid, tsid, tclass); 5358c2ecf20Sopenharmony_ci head = &avc->avc_cache.slots[hvalue]; 5368c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(node, head, list) { 5378c2ecf20Sopenharmony_ci if (ssid == node->ae.ssid && 5388c2ecf20Sopenharmony_ci tclass == node->ae.tclass && 5398c2ecf20Sopenharmony_ci tsid == node->ae.tsid) { 5408c2ecf20Sopenharmony_ci ret = node; 5418c2ecf20Sopenharmony_ci break; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return ret; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci/** 5498c2ecf20Sopenharmony_ci * avc_lookup - Look up an AVC entry. 5508c2ecf20Sopenharmony_ci * @ssid: source security identifier 5518c2ecf20Sopenharmony_ci * @tsid: target security identifier 5528c2ecf20Sopenharmony_ci * @tclass: target security class 5538c2ecf20Sopenharmony_ci * 5548c2ecf20Sopenharmony_ci * Look up an AVC entry that is valid for the 5558c2ecf20Sopenharmony_ci * (@ssid, @tsid), interpreting the permissions 5568c2ecf20Sopenharmony_ci * based on @tclass. If a valid AVC entry exists, 5578c2ecf20Sopenharmony_ci * then this function returns the avc_node. 5588c2ecf20Sopenharmony_ci * Otherwise, this function returns NULL. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_cistatic struct avc_node *avc_lookup(struct selinux_avc *avc, 5618c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, u16 tclass) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct avc_node *node; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci avc_cache_stats_incr(lookups); 5668c2ecf20Sopenharmony_ci node = avc_search_node(avc, ssid, tsid, tclass); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (node) 5698c2ecf20Sopenharmony_ci return node; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci avc_cache_stats_incr(misses); 5728c2ecf20Sopenharmony_ci return NULL; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int avc_latest_notif_update(struct selinux_avc *avc, 5768c2ecf20Sopenharmony_ci int seqno, int is_insert) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci int ret = 0; 5798c2ecf20Sopenharmony_ci static DEFINE_SPINLOCK(notif_lock); 5808c2ecf20Sopenharmony_ci unsigned long flag; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci spin_lock_irqsave(¬if_lock, flag); 5838c2ecf20Sopenharmony_ci if (is_insert) { 5848c2ecf20Sopenharmony_ci if (seqno < avc->avc_cache.latest_notif) { 5858c2ecf20Sopenharmony_ci pr_warn("SELinux: avc: seqno %d < latest_notif %d\n", 5868c2ecf20Sopenharmony_ci seqno, avc->avc_cache.latest_notif); 5878c2ecf20Sopenharmony_ci ret = -EAGAIN; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci } else { 5908c2ecf20Sopenharmony_ci if (seqno > avc->avc_cache.latest_notif) 5918c2ecf20Sopenharmony_ci avc->avc_cache.latest_notif = seqno; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(¬if_lock, flag); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci return ret; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci/** 5998c2ecf20Sopenharmony_ci * avc_insert - Insert an AVC entry. 6008c2ecf20Sopenharmony_ci * @ssid: source security identifier 6018c2ecf20Sopenharmony_ci * @tsid: target security identifier 6028c2ecf20Sopenharmony_ci * @tclass: target security class 6038c2ecf20Sopenharmony_ci * @avd: resulting av decision 6048c2ecf20Sopenharmony_ci * @xp_node: resulting extended permissions 6058c2ecf20Sopenharmony_ci * 6068c2ecf20Sopenharmony_ci * Insert an AVC entry for the SID pair 6078c2ecf20Sopenharmony_ci * (@ssid, @tsid) and class @tclass. 6088c2ecf20Sopenharmony_ci * The access vectors and the sequence number are 6098c2ecf20Sopenharmony_ci * normally provided by the security server in 6108c2ecf20Sopenharmony_ci * response to a security_compute_av() call. If the 6118c2ecf20Sopenharmony_ci * sequence number @avd->seqno is not less than the latest 6128c2ecf20Sopenharmony_ci * revocation notification, then the function copies 6138c2ecf20Sopenharmony_ci * the access vectors into a cache entry, returns 6148c2ecf20Sopenharmony_ci * avc_node inserted. Otherwise, this function returns NULL. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_cistatic struct avc_node *avc_insert(struct selinux_avc *avc, 6178c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, u16 tclass, 6188c2ecf20Sopenharmony_ci struct av_decision *avd, 6198c2ecf20Sopenharmony_ci struct avc_xperms_node *xp_node) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct avc_node *pos, *node = NULL; 6228c2ecf20Sopenharmony_ci int hvalue; 6238c2ecf20Sopenharmony_ci unsigned long flag; 6248c2ecf20Sopenharmony_ci spinlock_t *lock; 6258c2ecf20Sopenharmony_ci struct hlist_head *head; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (avc_latest_notif_update(avc, avd->seqno, 1)) 6288c2ecf20Sopenharmony_ci return NULL; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci node = avc_alloc_node(avc); 6318c2ecf20Sopenharmony_ci if (!node) 6328c2ecf20Sopenharmony_ci return NULL; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci avc_node_populate(node, ssid, tsid, tclass, avd); 6358c2ecf20Sopenharmony_ci if (avc_xperms_populate(node, xp_node)) { 6368c2ecf20Sopenharmony_ci avc_node_kill(avc, node); 6378c2ecf20Sopenharmony_ci return NULL; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci hvalue = avc_hash(ssid, tsid, tclass); 6418c2ecf20Sopenharmony_ci head = &avc->avc_cache.slots[hvalue]; 6428c2ecf20Sopenharmony_ci lock = &avc->avc_cache.slots_lock[hvalue]; 6438c2ecf20Sopenharmony_ci spin_lock_irqsave(lock, flag); 6448c2ecf20Sopenharmony_ci hlist_for_each_entry(pos, head, list) { 6458c2ecf20Sopenharmony_ci if (pos->ae.ssid == ssid && 6468c2ecf20Sopenharmony_ci pos->ae.tsid == tsid && 6478c2ecf20Sopenharmony_ci pos->ae.tclass == tclass) { 6488c2ecf20Sopenharmony_ci avc_node_replace(avc, node, pos); 6498c2ecf20Sopenharmony_ci goto found; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci hlist_add_head_rcu(&node->list, head); 6538c2ecf20Sopenharmony_cifound: 6548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lock, flag); 6558c2ecf20Sopenharmony_ci return node; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci/** 6598c2ecf20Sopenharmony_ci * avc_audit_pre_callback - SELinux specific information 6608c2ecf20Sopenharmony_ci * will be called by generic audit code 6618c2ecf20Sopenharmony_ci * @ab: the audit buffer 6628c2ecf20Sopenharmony_ci * @a: audit_data 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_cistatic void avc_audit_pre_callback(struct audit_buffer *ab, void *a) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct common_audit_data *ad = a; 6678c2ecf20Sopenharmony_ci struct selinux_audit_data *sad = ad->selinux_audit_data; 6688c2ecf20Sopenharmony_ci u32 av = sad->audited; 6698c2ecf20Sopenharmony_ci const char **perms; 6708c2ecf20Sopenharmony_ci int i, perm; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci audit_log_format(ab, "avc: %s ", sad->denied ? "denied" : "granted"); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (av == 0) { 6758c2ecf20Sopenharmony_ci audit_log_format(ab, " null"); 6768c2ecf20Sopenharmony_ci return; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci perms = secclass_map[sad->tclass-1].perms; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci audit_log_format(ab, " {"); 6828c2ecf20Sopenharmony_ci i = 0; 6838c2ecf20Sopenharmony_ci perm = 1; 6848c2ecf20Sopenharmony_ci while (i < (sizeof(av) * 8)) { 6858c2ecf20Sopenharmony_ci if ((perm & av) && perms[i]) { 6868c2ecf20Sopenharmony_ci audit_log_format(ab, " %s", perms[i]); 6878c2ecf20Sopenharmony_ci av &= ~perm; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci i++; 6908c2ecf20Sopenharmony_ci perm <<= 1; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (av) 6948c2ecf20Sopenharmony_ci audit_log_format(ab, " 0x%x", av); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci audit_log_format(ab, " } for "); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci/** 7008c2ecf20Sopenharmony_ci * avc_audit_post_callback - SELinux specific information 7018c2ecf20Sopenharmony_ci * will be called by generic audit code 7028c2ecf20Sopenharmony_ci * @ab: the audit buffer 7038c2ecf20Sopenharmony_ci * @a: audit_data 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_cistatic void avc_audit_post_callback(struct audit_buffer *ab, void *a) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct common_audit_data *ad = a; 7088c2ecf20Sopenharmony_ci struct selinux_audit_data *sad = ad->selinux_audit_data; 7098c2ecf20Sopenharmony_ci char *scontext = NULL; 7108c2ecf20Sopenharmony_ci char *tcontext = NULL; 7118c2ecf20Sopenharmony_ci const char *tclass = NULL; 7128c2ecf20Sopenharmony_ci u32 scontext_len; 7138c2ecf20Sopenharmony_ci u32 tcontext_len; 7148c2ecf20Sopenharmony_ci int rc; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci rc = security_sid_to_context(sad->state, sad->ssid, &scontext, 7178c2ecf20Sopenharmony_ci &scontext_len); 7188c2ecf20Sopenharmony_ci if (rc) 7198c2ecf20Sopenharmony_ci audit_log_format(ab, " ssid=%d", sad->ssid); 7208c2ecf20Sopenharmony_ci else 7218c2ecf20Sopenharmony_ci audit_log_format(ab, " scontext=%s", scontext); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci rc = security_sid_to_context(sad->state, sad->tsid, &tcontext, 7248c2ecf20Sopenharmony_ci &tcontext_len); 7258c2ecf20Sopenharmony_ci if (rc) 7268c2ecf20Sopenharmony_ci audit_log_format(ab, " tsid=%d", sad->tsid); 7278c2ecf20Sopenharmony_ci else 7288c2ecf20Sopenharmony_ci audit_log_format(ab, " tcontext=%s", tcontext); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci tclass = secclass_map[sad->tclass-1].name; 7318c2ecf20Sopenharmony_ci audit_log_format(ab, " tclass=%s", tclass); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (sad->denied) 7348c2ecf20Sopenharmony_ci audit_log_format(ab, " permissive=%u", sad->result ? 0 : 1); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci trace_selinux_audited(sad, scontext, tcontext, tclass); 7378c2ecf20Sopenharmony_ci kfree(tcontext); 7388c2ecf20Sopenharmony_ci kfree(scontext); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* in case of invalid context report also the actual context string */ 7418c2ecf20Sopenharmony_ci rc = security_sid_to_context_inval(sad->state, sad->ssid, &scontext, 7428c2ecf20Sopenharmony_ci &scontext_len); 7438c2ecf20Sopenharmony_ci if (!rc && scontext) { 7448c2ecf20Sopenharmony_ci if (scontext_len && scontext[scontext_len - 1] == '\0') 7458c2ecf20Sopenharmony_ci scontext_len--; 7468c2ecf20Sopenharmony_ci audit_log_format(ab, " srawcon="); 7478c2ecf20Sopenharmony_ci audit_log_n_untrustedstring(ab, scontext, scontext_len); 7488c2ecf20Sopenharmony_ci kfree(scontext); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci rc = security_sid_to_context_inval(sad->state, sad->tsid, &scontext, 7528c2ecf20Sopenharmony_ci &scontext_len); 7538c2ecf20Sopenharmony_ci if (!rc && scontext) { 7548c2ecf20Sopenharmony_ci if (scontext_len && scontext[scontext_len - 1] == '\0') 7558c2ecf20Sopenharmony_ci scontext_len--; 7568c2ecf20Sopenharmony_ci audit_log_format(ab, " trawcon="); 7578c2ecf20Sopenharmony_ci audit_log_n_untrustedstring(ab, scontext, scontext_len); 7588c2ecf20Sopenharmony_ci kfree(scontext); 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci/* This is the slow part of avc audit with big stack footprint */ 7638c2ecf20Sopenharmony_cinoinline int slow_avc_audit(struct selinux_state *state, 7648c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, u16 tclass, 7658c2ecf20Sopenharmony_ci u32 requested, u32 audited, u32 denied, int result, 7668c2ecf20Sopenharmony_ci struct common_audit_data *a) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct common_audit_data stack_data; 7698c2ecf20Sopenharmony_ci struct selinux_audit_data sad; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (WARN_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map))) 7728c2ecf20Sopenharmony_ci return -EINVAL; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (!a) { 7758c2ecf20Sopenharmony_ci a = &stack_data; 7768c2ecf20Sopenharmony_ci a->type = LSM_AUDIT_DATA_NONE; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci sad.tclass = tclass; 7808c2ecf20Sopenharmony_ci sad.requested = requested; 7818c2ecf20Sopenharmony_ci sad.ssid = ssid; 7828c2ecf20Sopenharmony_ci sad.tsid = tsid; 7838c2ecf20Sopenharmony_ci sad.audited = audited; 7848c2ecf20Sopenharmony_ci sad.denied = denied; 7858c2ecf20Sopenharmony_ci sad.result = result; 7868c2ecf20Sopenharmony_ci sad.state = state; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci a->selinux_audit_data = &sad; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback); 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci/** 7958c2ecf20Sopenharmony_ci * avc_add_callback - Register a callback for security events. 7968c2ecf20Sopenharmony_ci * @callback: callback function 7978c2ecf20Sopenharmony_ci * @events: security events 7988c2ecf20Sopenharmony_ci * 7998c2ecf20Sopenharmony_ci * Register a callback function for events in the set @events. 8008c2ecf20Sopenharmony_ci * Returns %0 on success or -%ENOMEM if insufficient memory 8018c2ecf20Sopenharmony_ci * exists to add the callback. 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ciint __init avc_add_callback(int (*callback)(u32 event), u32 events) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct avc_callback_node *c; 8068c2ecf20Sopenharmony_ci int rc = 0; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci c = kmalloc(sizeof(*c), GFP_KERNEL); 8098c2ecf20Sopenharmony_ci if (!c) { 8108c2ecf20Sopenharmony_ci rc = -ENOMEM; 8118c2ecf20Sopenharmony_ci goto out; 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci c->callback = callback; 8158c2ecf20Sopenharmony_ci c->events = events; 8168c2ecf20Sopenharmony_ci c->next = avc_callbacks; 8178c2ecf20Sopenharmony_ci avc_callbacks = c; 8188c2ecf20Sopenharmony_ciout: 8198c2ecf20Sopenharmony_ci return rc; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci/** 8238c2ecf20Sopenharmony_ci * avc_update_node Update an AVC entry 8248c2ecf20Sopenharmony_ci * @event : Updating event 8258c2ecf20Sopenharmony_ci * @perms : Permission mask bits 8268c2ecf20Sopenharmony_ci * @ssid,@tsid,@tclass : identifier of an AVC entry 8278c2ecf20Sopenharmony_ci * @seqno : sequence number when decision was made 8288c2ecf20Sopenharmony_ci * @xpd: extended_perms_decision to be added to the node 8298c2ecf20Sopenharmony_ci * @flags: the AVC_* flags, e.g. AVC_NONBLOCKING, AVC_EXTENDED_PERMS, or 0. 8308c2ecf20Sopenharmony_ci * 8318c2ecf20Sopenharmony_ci * if a valid AVC entry doesn't exist,this function returns -ENOENT. 8328c2ecf20Sopenharmony_ci * if kmalloc() called internal returns NULL, this function returns -ENOMEM. 8338c2ecf20Sopenharmony_ci * otherwise, this function updates the AVC entry. The original AVC-entry object 8348c2ecf20Sopenharmony_ci * will release later by RCU. 8358c2ecf20Sopenharmony_ci */ 8368c2ecf20Sopenharmony_cistatic int avc_update_node(struct selinux_avc *avc, 8378c2ecf20Sopenharmony_ci u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid, 8388c2ecf20Sopenharmony_ci u32 tsid, u16 tclass, u32 seqno, 8398c2ecf20Sopenharmony_ci struct extended_perms_decision *xpd, 8408c2ecf20Sopenharmony_ci u32 flags) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci int hvalue, rc = 0; 8438c2ecf20Sopenharmony_ci unsigned long flag; 8448c2ecf20Sopenharmony_ci struct avc_node *pos, *node, *orig = NULL; 8458c2ecf20Sopenharmony_ci struct hlist_head *head; 8468c2ecf20Sopenharmony_ci spinlock_t *lock; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* 8498c2ecf20Sopenharmony_ci * If we are in a non-blocking code path, e.g. VFS RCU walk, 8508c2ecf20Sopenharmony_ci * then we must not add permissions to a cache entry 8518c2ecf20Sopenharmony_ci * because we will not audit the denial. Otherwise, 8528c2ecf20Sopenharmony_ci * during the subsequent blocking retry (e.g. VFS ref walk), we 8538c2ecf20Sopenharmony_ci * will find the permissions already granted in the cache entry 8548c2ecf20Sopenharmony_ci * and won't audit anything at all, leading to silent denials in 8558c2ecf20Sopenharmony_ci * permissive mode that only appear when in enforcing mode. 8568c2ecf20Sopenharmony_ci * 8578c2ecf20Sopenharmony_ci * See the corresponding handling of MAY_NOT_BLOCK in avc_audit() 8588c2ecf20Sopenharmony_ci * and selinux_inode_permission(). 8598c2ecf20Sopenharmony_ci */ 8608c2ecf20Sopenharmony_ci if (flags & AVC_NONBLOCKING) 8618c2ecf20Sopenharmony_ci return 0; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci node = avc_alloc_node(avc); 8648c2ecf20Sopenharmony_ci if (!node) { 8658c2ecf20Sopenharmony_ci rc = -ENOMEM; 8668c2ecf20Sopenharmony_ci goto out; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* Lock the target slot */ 8708c2ecf20Sopenharmony_ci hvalue = avc_hash(ssid, tsid, tclass); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci head = &avc->avc_cache.slots[hvalue]; 8738c2ecf20Sopenharmony_ci lock = &avc->avc_cache.slots_lock[hvalue]; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci spin_lock_irqsave(lock, flag); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci hlist_for_each_entry(pos, head, list) { 8788c2ecf20Sopenharmony_ci if (ssid == pos->ae.ssid && 8798c2ecf20Sopenharmony_ci tsid == pos->ae.tsid && 8808c2ecf20Sopenharmony_ci tclass == pos->ae.tclass && 8818c2ecf20Sopenharmony_ci seqno == pos->ae.avd.seqno){ 8828c2ecf20Sopenharmony_ci orig = pos; 8838c2ecf20Sopenharmony_ci break; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (!orig) { 8888c2ecf20Sopenharmony_ci rc = -ENOENT; 8898c2ecf20Sopenharmony_ci avc_node_kill(avc, node); 8908c2ecf20Sopenharmony_ci goto out_unlock; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* 8948c2ecf20Sopenharmony_ci * Copy and replace original node. 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (orig->ae.xp_node) { 9008c2ecf20Sopenharmony_ci rc = avc_xperms_populate(node, orig->ae.xp_node); 9018c2ecf20Sopenharmony_ci if (rc) { 9028c2ecf20Sopenharmony_ci avc_node_kill(avc, node); 9038c2ecf20Sopenharmony_ci goto out_unlock; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci switch (event) { 9088c2ecf20Sopenharmony_ci case AVC_CALLBACK_GRANT: 9098c2ecf20Sopenharmony_ci node->ae.avd.allowed |= perms; 9108c2ecf20Sopenharmony_ci if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS)) 9118c2ecf20Sopenharmony_ci avc_xperms_allow_perm(node->ae.xp_node, driver, xperm); 9128c2ecf20Sopenharmony_ci break; 9138c2ecf20Sopenharmony_ci case AVC_CALLBACK_TRY_REVOKE: 9148c2ecf20Sopenharmony_ci case AVC_CALLBACK_REVOKE: 9158c2ecf20Sopenharmony_ci node->ae.avd.allowed &= ~perms; 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci case AVC_CALLBACK_AUDITALLOW_ENABLE: 9188c2ecf20Sopenharmony_ci node->ae.avd.auditallow |= perms; 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci case AVC_CALLBACK_AUDITALLOW_DISABLE: 9218c2ecf20Sopenharmony_ci node->ae.avd.auditallow &= ~perms; 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci case AVC_CALLBACK_AUDITDENY_ENABLE: 9248c2ecf20Sopenharmony_ci node->ae.avd.auditdeny |= perms; 9258c2ecf20Sopenharmony_ci break; 9268c2ecf20Sopenharmony_ci case AVC_CALLBACK_AUDITDENY_DISABLE: 9278c2ecf20Sopenharmony_ci node->ae.avd.auditdeny &= ~perms; 9288c2ecf20Sopenharmony_ci break; 9298c2ecf20Sopenharmony_ci case AVC_CALLBACK_ADD_XPERMS: 9308c2ecf20Sopenharmony_ci avc_add_xperms_decision(node, xpd); 9318c2ecf20Sopenharmony_ci break; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci avc_node_replace(avc, node, orig); 9348c2ecf20Sopenharmony_ciout_unlock: 9358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lock, flag); 9368c2ecf20Sopenharmony_ciout: 9378c2ecf20Sopenharmony_ci return rc; 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci/** 9418c2ecf20Sopenharmony_ci * avc_flush - Flush the cache 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_cistatic void avc_flush(struct selinux_avc *avc) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci struct hlist_head *head; 9468c2ecf20Sopenharmony_ci struct avc_node *node; 9478c2ecf20Sopenharmony_ci spinlock_t *lock; 9488c2ecf20Sopenharmony_ci unsigned long flag; 9498c2ecf20Sopenharmony_ci int i; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci for (i = 0; i < AVC_CACHE_SLOTS; i++) { 9528c2ecf20Sopenharmony_ci head = &avc->avc_cache.slots[i]; 9538c2ecf20Sopenharmony_ci lock = &avc->avc_cache.slots_lock[i]; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci spin_lock_irqsave(lock, flag); 9568c2ecf20Sopenharmony_ci /* 9578c2ecf20Sopenharmony_ci * With preemptable RCU, the outer spinlock does not 9588c2ecf20Sopenharmony_ci * prevent RCU grace periods from ending. 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_ci rcu_read_lock(); 9618c2ecf20Sopenharmony_ci hlist_for_each_entry(node, head, list) 9628c2ecf20Sopenharmony_ci avc_node_delete(avc, node); 9638c2ecf20Sopenharmony_ci rcu_read_unlock(); 9648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lock, flag); 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci/** 9698c2ecf20Sopenharmony_ci * avc_ss_reset - Flush the cache and revalidate migrated permissions. 9708c2ecf20Sopenharmony_ci * @seqno: policy sequence number 9718c2ecf20Sopenharmony_ci */ 9728c2ecf20Sopenharmony_ciint avc_ss_reset(struct selinux_avc *avc, u32 seqno) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci struct avc_callback_node *c; 9758c2ecf20Sopenharmony_ci int rc = 0, tmprc; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci avc_flush(avc); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci for (c = avc_callbacks; c; c = c->next) { 9808c2ecf20Sopenharmony_ci if (c->events & AVC_CALLBACK_RESET) { 9818c2ecf20Sopenharmony_ci tmprc = c->callback(AVC_CALLBACK_RESET); 9828c2ecf20Sopenharmony_ci /* save the first error encountered for the return 9838c2ecf20Sopenharmony_ci value and continue processing the callbacks */ 9848c2ecf20Sopenharmony_ci if (!rc) 9858c2ecf20Sopenharmony_ci rc = tmprc; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci avc_latest_notif_update(avc, seqno, 0); 9908c2ecf20Sopenharmony_ci return rc; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci/* 9948c2ecf20Sopenharmony_ci * Slow-path helper function for avc_has_perm_noaudit, 9958c2ecf20Sopenharmony_ci * when the avc_node lookup fails. We get called with 9968c2ecf20Sopenharmony_ci * the RCU read lock held, and need to return with it 9978c2ecf20Sopenharmony_ci * still held, but drop if for the security compute. 9988c2ecf20Sopenharmony_ci * 9998c2ecf20Sopenharmony_ci * Don't inline this, since it's the slow-path and just 10008c2ecf20Sopenharmony_ci * results in a bigger stack frame. 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_cistatic noinline 10038c2ecf20Sopenharmony_cistruct avc_node *avc_compute_av(struct selinux_state *state, 10048c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, 10058c2ecf20Sopenharmony_ci u16 tclass, struct av_decision *avd, 10068c2ecf20Sopenharmony_ci struct avc_xperms_node *xp_node) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci rcu_read_unlock(); 10098c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&xp_node->xpd_head); 10108c2ecf20Sopenharmony_ci security_compute_av(state, ssid, tsid, tclass, avd, &xp_node->xp); 10118c2ecf20Sopenharmony_ci rcu_read_lock(); 10128c2ecf20Sopenharmony_ci return avc_insert(state->avc, ssid, tsid, tclass, avd, xp_node); 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cistatic noinline int avc_denied(struct selinux_state *state, 10168c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, 10178c2ecf20Sopenharmony_ci u16 tclass, u32 requested, 10188c2ecf20Sopenharmony_ci u8 driver, u8 xperm, unsigned int flags, 10198c2ecf20Sopenharmony_ci struct av_decision *avd) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci if (flags & AVC_STRICT) 10228c2ecf20Sopenharmony_ci return -EACCES; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (enforcing_enabled(state) && 10258c2ecf20Sopenharmony_ci !(avd->flags & AVD_FLAGS_PERMISSIVE)) 10268c2ecf20Sopenharmony_ci return -EACCES; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci avc_update_node(state->avc, AVC_CALLBACK_GRANT, requested, driver, 10298c2ecf20Sopenharmony_ci xperm, ssid, tsid, tclass, avd->seqno, NULL, flags); 10308c2ecf20Sopenharmony_ci return 0; 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci/* 10348c2ecf20Sopenharmony_ci * The avc extended permissions logic adds an additional 256 bits of 10358c2ecf20Sopenharmony_ci * permissions to an avc node when extended permissions for that node are 10368c2ecf20Sopenharmony_ci * specified in the avtab. If the additional 256 permissions is not adequate, 10378c2ecf20Sopenharmony_ci * as-is the case with ioctls, then multiple may be chained together and the 10388c2ecf20Sopenharmony_ci * driver field is used to specify which set contains the permission. 10398c2ecf20Sopenharmony_ci */ 10408c2ecf20Sopenharmony_ciint avc_has_extended_perms(struct selinux_state *state, 10418c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, u16 tclass, u32 requested, 10428c2ecf20Sopenharmony_ci u8 driver, u8 xperm, struct common_audit_data *ad) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci struct avc_node *node; 10458c2ecf20Sopenharmony_ci struct av_decision avd; 10468c2ecf20Sopenharmony_ci u32 denied; 10478c2ecf20Sopenharmony_ci struct extended_perms_decision local_xpd; 10488c2ecf20Sopenharmony_ci struct extended_perms_decision *xpd = NULL; 10498c2ecf20Sopenharmony_ci struct extended_perms_data allowed; 10508c2ecf20Sopenharmony_ci struct extended_perms_data auditallow; 10518c2ecf20Sopenharmony_ci struct extended_perms_data dontaudit; 10528c2ecf20Sopenharmony_ci struct avc_xperms_node local_xp_node; 10538c2ecf20Sopenharmony_ci struct avc_xperms_node *xp_node; 10548c2ecf20Sopenharmony_ci int rc = 0, rc2; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci xp_node = &local_xp_node; 10578c2ecf20Sopenharmony_ci if (WARN_ON(!requested)) 10588c2ecf20Sopenharmony_ci return -EACCES; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci rcu_read_lock(); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci node = avc_lookup(state->avc, ssid, tsid, tclass); 10638c2ecf20Sopenharmony_ci if (unlikely(!node)) { 10648c2ecf20Sopenharmony_ci node = avc_compute_av(state, ssid, tsid, tclass, &avd, xp_node); 10658c2ecf20Sopenharmony_ci } else { 10668c2ecf20Sopenharmony_ci memcpy(&avd, &node->ae.avd, sizeof(avd)); 10678c2ecf20Sopenharmony_ci xp_node = node->ae.xp_node; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci /* if extended permissions are not defined, only consider av_decision */ 10708c2ecf20Sopenharmony_ci if (!xp_node || !xp_node->xp.len) 10718c2ecf20Sopenharmony_ci goto decision; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci local_xpd.allowed = &allowed; 10748c2ecf20Sopenharmony_ci local_xpd.auditallow = &auditallow; 10758c2ecf20Sopenharmony_ci local_xpd.dontaudit = &dontaudit; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci xpd = avc_xperms_decision_lookup(driver, xp_node); 10788c2ecf20Sopenharmony_ci if (unlikely(!xpd)) { 10798c2ecf20Sopenharmony_ci /* 10808c2ecf20Sopenharmony_ci * Compute the extended_perms_decision only if the driver 10818c2ecf20Sopenharmony_ci * is flagged 10828c2ecf20Sopenharmony_ci */ 10838c2ecf20Sopenharmony_ci if (!security_xperm_test(xp_node->xp.drivers.p, driver)) { 10848c2ecf20Sopenharmony_ci avd.allowed &= ~requested; 10858c2ecf20Sopenharmony_ci goto decision; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci rcu_read_unlock(); 10888c2ecf20Sopenharmony_ci security_compute_xperms_decision(state, ssid, tsid, tclass, 10898c2ecf20Sopenharmony_ci driver, &local_xpd); 10908c2ecf20Sopenharmony_ci rcu_read_lock(); 10918c2ecf20Sopenharmony_ci avc_update_node(state->avc, AVC_CALLBACK_ADD_XPERMS, requested, 10928c2ecf20Sopenharmony_ci driver, xperm, ssid, tsid, tclass, avd.seqno, 10938c2ecf20Sopenharmony_ci &local_xpd, 0); 10948c2ecf20Sopenharmony_ci } else { 10958c2ecf20Sopenharmony_ci avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci xpd = &local_xpd; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (!avc_xperms_has_perm(xpd, xperm, XPERMS_ALLOWED)) 11008c2ecf20Sopenharmony_ci avd.allowed &= ~requested; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cidecision: 11038c2ecf20Sopenharmony_ci denied = requested & ~(avd.allowed); 11048c2ecf20Sopenharmony_ci if (unlikely(denied)) 11058c2ecf20Sopenharmony_ci rc = avc_denied(state, ssid, tsid, tclass, requested, 11068c2ecf20Sopenharmony_ci driver, xperm, AVC_EXTENDED_PERMS, &avd); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci rcu_read_unlock(); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci rc2 = avc_xperms_audit(state, ssid, tsid, tclass, requested, 11118c2ecf20Sopenharmony_ci &avd, xpd, xperm, rc, ad); 11128c2ecf20Sopenharmony_ci if (rc2) 11138c2ecf20Sopenharmony_ci return rc2; 11148c2ecf20Sopenharmony_ci return rc; 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci/** 11188c2ecf20Sopenharmony_ci * avc_has_perm_noaudit - Check permissions but perform no auditing. 11198c2ecf20Sopenharmony_ci * @ssid: source security identifier 11208c2ecf20Sopenharmony_ci * @tsid: target security identifier 11218c2ecf20Sopenharmony_ci * @tclass: target security class 11228c2ecf20Sopenharmony_ci * @requested: requested permissions, interpreted based on @tclass 11238c2ecf20Sopenharmony_ci * @flags: AVC_STRICT, AVC_NONBLOCKING, or 0 11248c2ecf20Sopenharmony_ci * @avd: access vector decisions 11258c2ecf20Sopenharmony_ci * 11268c2ecf20Sopenharmony_ci * Check the AVC to determine whether the @requested permissions are granted 11278c2ecf20Sopenharmony_ci * for the SID pair (@ssid, @tsid), interpreting the permissions 11288c2ecf20Sopenharmony_ci * based on @tclass, and call the security server on a cache miss to obtain 11298c2ecf20Sopenharmony_ci * a new decision and add it to the cache. Return a copy of the decisions 11308c2ecf20Sopenharmony_ci * in @avd. Return %0 if all @requested permissions are granted, 11318c2ecf20Sopenharmony_ci * -%EACCES if any permissions are denied, or another -errno upon 11328c2ecf20Sopenharmony_ci * other errors. This function is typically called by avc_has_perm(), 11338c2ecf20Sopenharmony_ci * but may also be called directly to separate permission checking from 11348c2ecf20Sopenharmony_ci * auditing, e.g. in cases where a lock must be held for the check but 11358c2ecf20Sopenharmony_ci * should be released for the auditing. 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_ciinline int avc_has_perm_noaudit(struct selinux_state *state, 11388c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, 11398c2ecf20Sopenharmony_ci u16 tclass, u32 requested, 11408c2ecf20Sopenharmony_ci unsigned int flags, 11418c2ecf20Sopenharmony_ci struct av_decision *avd) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci struct avc_node *node; 11448c2ecf20Sopenharmony_ci struct avc_xperms_node xp_node; 11458c2ecf20Sopenharmony_ci int rc = 0; 11468c2ecf20Sopenharmony_ci u32 denied; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (WARN_ON(!requested)) 11498c2ecf20Sopenharmony_ci return -EACCES; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci rcu_read_lock(); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci node = avc_lookup(state->avc, ssid, tsid, tclass); 11548c2ecf20Sopenharmony_ci if (unlikely(!node)) 11558c2ecf20Sopenharmony_ci node = avc_compute_av(state, ssid, tsid, tclass, avd, &xp_node); 11568c2ecf20Sopenharmony_ci else 11578c2ecf20Sopenharmony_ci memcpy(avd, &node->ae.avd, sizeof(*avd)); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci denied = requested & ~(avd->allowed); 11608c2ecf20Sopenharmony_ci if (unlikely(denied)) 11618c2ecf20Sopenharmony_ci rc = avc_denied(state, ssid, tsid, tclass, requested, 0, 0, 11628c2ecf20Sopenharmony_ci flags, avd); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci rcu_read_unlock(); 11658c2ecf20Sopenharmony_ci return rc; 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci/** 11698c2ecf20Sopenharmony_ci * avc_has_perm - Check permissions and perform any appropriate auditing. 11708c2ecf20Sopenharmony_ci * @ssid: source security identifier 11718c2ecf20Sopenharmony_ci * @tsid: target security identifier 11728c2ecf20Sopenharmony_ci * @tclass: target security class 11738c2ecf20Sopenharmony_ci * @requested: requested permissions, interpreted based on @tclass 11748c2ecf20Sopenharmony_ci * @auditdata: auxiliary audit data 11758c2ecf20Sopenharmony_ci * 11768c2ecf20Sopenharmony_ci * Check the AVC to determine whether the @requested permissions are granted 11778c2ecf20Sopenharmony_ci * for the SID pair (@ssid, @tsid), interpreting the permissions 11788c2ecf20Sopenharmony_ci * based on @tclass, and call the security server on a cache miss to obtain 11798c2ecf20Sopenharmony_ci * a new decision and add it to the cache. Audit the granting or denial of 11808c2ecf20Sopenharmony_ci * permissions in accordance with the policy. Return %0 if all @requested 11818c2ecf20Sopenharmony_ci * permissions are granted, -%EACCES if any permissions are denied, or 11828c2ecf20Sopenharmony_ci * another -errno upon other errors. 11838c2ecf20Sopenharmony_ci */ 11848c2ecf20Sopenharmony_ciint avc_has_perm(struct selinux_state *state, u32 ssid, u32 tsid, u16 tclass, 11858c2ecf20Sopenharmony_ci u32 requested, struct common_audit_data *auditdata) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci struct av_decision avd; 11888c2ecf20Sopenharmony_ci int rc, rc2; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, 0, 11918c2ecf20Sopenharmony_ci &avd); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc, 11948c2ecf20Sopenharmony_ci auditdata, 0); 11958c2ecf20Sopenharmony_ci if (rc2) 11968c2ecf20Sopenharmony_ci return rc2; 11978c2ecf20Sopenharmony_ci return rc; 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ciint avc_has_perm_flags(struct selinux_state *state, 12018c2ecf20Sopenharmony_ci u32 ssid, u32 tsid, u16 tclass, u32 requested, 12028c2ecf20Sopenharmony_ci struct common_audit_data *auditdata, 12038c2ecf20Sopenharmony_ci int flags) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct av_decision avd; 12068c2ecf20Sopenharmony_ci int rc, rc2; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, 12098c2ecf20Sopenharmony_ci (flags & MAY_NOT_BLOCK) ? AVC_NONBLOCKING : 0, 12108c2ecf20Sopenharmony_ci &avd); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc, 12138c2ecf20Sopenharmony_ci auditdata, flags); 12148c2ecf20Sopenharmony_ci if (rc2) 12158c2ecf20Sopenharmony_ci return rc2; 12168c2ecf20Sopenharmony_ci return rc; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ciu32 avc_policy_seqno(struct selinux_state *state) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci return state->avc->avc_cache.latest_notif; 12228c2ecf20Sopenharmony_ci} 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_civoid avc_disable(void) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci /* 12278c2ecf20Sopenharmony_ci * If you are looking at this because you have realized that we are 12288c2ecf20Sopenharmony_ci * not destroying the avc_node_cachep it might be easy to fix, but 12298c2ecf20Sopenharmony_ci * I don't know the memory barrier semantics well enough to know. It's 12308c2ecf20Sopenharmony_ci * possible that some other task dereferenced security_ops when 12318c2ecf20Sopenharmony_ci * it still pointed to selinux operations. If that is the case it's 12328c2ecf20Sopenharmony_ci * possible that it is about to use the avc and is about to need the 12338c2ecf20Sopenharmony_ci * avc_node_cachep. I know I could wrap the security.c security_ops call 12348c2ecf20Sopenharmony_ci * in an rcu_lock, but seriously, it's not worth it. Instead I just flush 12358c2ecf20Sopenharmony_ci * the cache and get that memory back. 12368c2ecf20Sopenharmony_ci */ 12378c2ecf20Sopenharmony_ci if (avc_node_cachep) { 12388c2ecf20Sopenharmony_ci avc_flush(selinux_state.avc); 12398c2ecf20Sopenharmony_ci /* kmem_cache_destroy(avc_node_cachep); */ 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci} 1242