162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Implementation of the kernel access vector cache (AVC). 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Stephen Smalley, <stephen.smalley.work@gmail.com> 662306a36Sopenharmony_ci * James Morris <jmorris@redhat.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Update: KaiGai, Kohei <kaigai@ak.jp.nec.com> 962306a36Sopenharmony_ci * Replaced the avc_lock spinlock by RCU. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/stddef.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/fs.h> 1862306a36Sopenharmony_ci#include <linux/dcache.h> 1962306a36Sopenharmony_ci#include <linux/init.h> 2062306a36Sopenharmony_ci#include <linux/skbuff.h> 2162306a36Sopenharmony_ci#include <linux/percpu.h> 2262306a36Sopenharmony_ci#include <linux/list.h> 2362306a36Sopenharmony_ci#include <net/sock.h> 2462306a36Sopenharmony_ci#include <linux/un.h> 2562306a36Sopenharmony_ci#include <net/af_unix.h> 2662306a36Sopenharmony_ci#include <linux/ip.h> 2762306a36Sopenharmony_ci#include <linux/audit.h> 2862306a36Sopenharmony_ci#include <linux/ipv6.h> 2962306a36Sopenharmony_ci#include <net/ipv6.h> 3062306a36Sopenharmony_ci#include "avc.h" 3162306a36Sopenharmony_ci#include "avc_ss.h" 3262306a36Sopenharmony_ci#include "classmap.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 3562306a36Sopenharmony_ci#include <trace/events/avc.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define AVC_CACHE_SLOTS 512 3862306a36Sopenharmony_ci#define AVC_DEF_CACHE_THRESHOLD 512 3962306a36Sopenharmony_ci#define AVC_CACHE_RECLAIM 16 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS 4262306a36Sopenharmony_ci#define avc_cache_stats_incr(field) this_cpu_inc(avc_cache_stats.field) 4362306a36Sopenharmony_ci#else 4462306a36Sopenharmony_ci#define avc_cache_stats_incr(field) do {} while (0) 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct avc_entry { 4862306a36Sopenharmony_ci u32 ssid; 4962306a36Sopenharmony_ci u32 tsid; 5062306a36Sopenharmony_ci u16 tclass; 5162306a36Sopenharmony_ci struct av_decision avd; 5262306a36Sopenharmony_ci struct avc_xperms_node *xp_node; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct avc_node { 5662306a36Sopenharmony_ci struct avc_entry ae; 5762306a36Sopenharmony_ci struct hlist_node list; /* anchored in avc_cache->slots[i] */ 5862306a36Sopenharmony_ci struct rcu_head rhead; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct avc_xperms_decision_node { 6262306a36Sopenharmony_ci struct extended_perms_decision xpd; 6362306a36Sopenharmony_ci struct list_head xpd_list; /* list of extended_perms_decision */ 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct avc_xperms_node { 6762306a36Sopenharmony_ci struct extended_perms xp; 6862306a36Sopenharmony_ci struct list_head xpd_head; /* list head of extended_perms_decision */ 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct avc_cache { 7262306a36Sopenharmony_ci struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ 7362306a36Sopenharmony_ci spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ 7462306a36Sopenharmony_ci atomic_t lru_hint; /* LRU hint for reclaim scan */ 7562306a36Sopenharmony_ci atomic_t active_nodes; 7662306a36Sopenharmony_ci u32 latest_notif; /* latest revocation notification */ 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct avc_callback_node { 8062306a36Sopenharmony_ci int (*callback) (u32 event); 8162306a36Sopenharmony_ci u32 events; 8262306a36Sopenharmony_ci struct avc_callback_node *next; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS 8662306a36Sopenharmony_ciDEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; 8762306a36Sopenharmony_ci#endif 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct selinux_avc { 9062306a36Sopenharmony_ci unsigned int avc_cache_threshold; 9162306a36Sopenharmony_ci struct avc_cache avc_cache; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct selinux_avc selinux_avc; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_civoid selinux_avc_init(void) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci int i; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci selinux_avc.avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD; 10162306a36Sopenharmony_ci for (i = 0; i < AVC_CACHE_SLOTS; i++) { 10262306a36Sopenharmony_ci INIT_HLIST_HEAD(&selinux_avc.avc_cache.slots[i]); 10362306a36Sopenharmony_ci spin_lock_init(&selinux_avc.avc_cache.slots_lock[i]); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci atomic_set(&selinux_avc.avc_cache.active_nodes, 0); 10662306a36Sopenharmony_ci atomic_set(&selinux_avc.avc_cache.lru_hint, 0); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciunsigned int avc_get_cache_threshold(void) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci return selinux_avc.avc_cache_threshold; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_civoid avc_set_cache_threshold(unsigned int cache_threshold) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci selinux_avc.avc_cache_threshold = cache_threshold; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic struct avc_callback_node *avc_callbacks __ro_after_init; 12062306a36Sopenharmony_cistatic struct kmem_cache *avc_node_cachep __ro_after_init; 12162306a36Sopenharmony_cistatic struct kmem_cache *avc_xperms_data_cachep __ro_after_init; 12262306a36Sopenharmony_cistatic struct kmem_cache *avc_xperms_decision_cachep __ro_after_init; 12362306a36Sopenharmony_cistatic struct kmem_cache *avc_xperms_cachep __ro_after_init; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic inline u32 avc_hash(u32 ssid, u32 tsid, u16 tclass) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/** 13162306a36Sopenharmony_ci * avc_init - Initialize the AVC. 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Initialize the access vector cache. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_civoid __init avc_init(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), 13862306a36Sopenharmony_ci 0, SLAB_PANIC, NULL); 13962306a36Sopenharmony_ci avc_xperms_cachep = kmem_cache_create("avc_xperms_node", 14062306a36Sopenharmony_ci sizeof(struct avc_xperms_node), 14162306a36Sopenharmony_ci 0, SLAB_PANIC, NULL); 14262306a36Sopenharmony_ci avc_xperms_decision_cachep = kmem_cache_create( 14362306a36Sopenharmony_ci "avc_xperms_decision_node", 14462306a36Sopenharmony_ci sizeof(struct avc_xperms_decision_node), 14562306a36Sopenharmony_ci 0, SLAB_PANIC, NULL); 14662306a36Sopenharmony_ci avc_xperms_data_cachep = kmem_cache_create("avc_xperms_data", 14762306a36Sopenharmony_ci sizeof(struct extended_perms_data), 14862306a36Sopenharmony_ci 0, SLAB_PANIC, NULL); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciint avc_get_hash_stats(char *page) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int i, chain_len, max_chain_len, slots_used; 15462306a36Sopenharmony_ci struct avc_node *node; 15562306a36Sopenharmony_ci struct hlist_head *head; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci rcu_read_lock(); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci slots_used = 0; 16062306a36Sopenharmony_ci max_chain_len = 0; 16162306a36Sopenharmony_ci for (i = 0; i < AVC_CACHE_SLOTS; i++) { 16262306a36Sopenharmony_ci head = &selinux_avc.avc_cache.slots[i]; 16362306a36Sopenharmony_ci if (!hlist_empty(head)) { 16462306a36Sopenharmony_ci slots_used++; 16562306a36Sopenharmony_ci chain_len = 0; 16662306a36Sopenharmony_ci hlist_for_each_entry_rcu(node, head, list) 16762306a36Sopenharmony_ci chain_len++; 16862306a36Sopenharmony_ci if (chain_len > max_chain_len) 16962306a36Sopenharmony_ci max_chain_len = chain_len; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci rcu_read_unlock(); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n" 17662306a36Sopenharmony_ci "longest chain: %d\n", 17762306a36Sopenharmony_ci atomic_read(&selinux_avc.avc_cache.active_nodes), 17862306a36Sopenharmony_ci slots_used, AVC_CACHE_SLOTS, max_chain_len); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* 18262306a36Sopenharmony_ci * using a linked list for extended_perms_decision lookup because the list is 18362306a36Sopenharmony_ci * always small. i.e. less than 5, typically 1 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistatic struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver, 18662306a36Sopenharmony_ci struct avc_xperms_node *xp_node) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct avc_xperms_decision_node *xpd_node; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) { 19162306a36Sopenharmony_ci if (xpd_node->xpd.driver == driver) 19262306a36Sopenharmony_ci return &xpd_node->xpd; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic inline unsigned int 19862306a36Sopenharmony_ciavc_xperms_has_perm(struct extended_perms_decision *xpd, 19962306a36Sopenharmony_ci u8 perm, u8 which) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci unsigned int rc = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if ((which == XPERMS_ALLOWED) && 20462306a36Sopenharmony_ci (xpd->used & XPERMS_ALLOWED)) 20562306a36Sopenharmony_ci rc = security_xperm_test(xpd->allowed->p, perm); 20662306a36Sopenharmony_ci else if ((which == XPERMS_AUDITALLOW) && 20762306a36Sopenharmony_ci (xpd->used & XPERMS_AUDITALLOW)) 20862306a36Sopenharmony_ci rc = security_xperm_test(xpd->auditallow->p, perm); 20962306a36Sopenharmony_ci else if ((which == XPERMS_DONTAUDIT) && 21062306a36Sopenharmony_ci (xpd->used & XPERMS_DONTAUDIT)) 21162306a36Sopenharmony_ci rc = security_xperm_test(xpd->dontaudit->p, perm); 21262306a36Sopenharmony_ci return rc; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void avc_xperms_allow_perm(struct avc_xperms_node *xp_node, 21662306a36Sopenharmony_ci u8 driver, u8 perm) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct extended_perms_decision *xpd; 21962306a36Sopenharmony_ci security_xperm_set(xp_node->xp.drivers.p, driver); 22062306a36Sopenharmony_ci xpd = avc_xperms_decision_lookup(driver, xp_node); 22162306a36Sopenharmony_ci if (xpd && xpd->allowed) 22262306a36Sopenharmony_ci security_xperm_set(xpd->allowed->p, perm); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void avc_xperms_decision_free(struct avc_xperms_decision_node *xpd_node) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct extended_perms_decision *xpd; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci xpd = &xpd_node->xpd; 23062306a36Sopenharmony_ci if (xpd->allowed) 23162306a36Sopenharmony_ci kmem_cache_free(avc_xperms_data_cachep, xpd->allowed); 23262306a36Sopenharmony_ci if (xpd->auditallow) 23362306a36Sopenharmony_ci kmem_cache_free(avc_xperms_data_cachep, xpd->auditallow); 23462306a36Sopenharmony_ci if (xpd->dontaudit) 23562306a36Sopenharmony_ci kmem_cache_free(avc_xperms_data_cachep, xpd->dontaudit); 23662306a36Sopenharmony_ci kmem_cache_free(avc_xperms_decision_cachep, xpd_node); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void avc_xperms_free(struct avc_xperms_node *xp_node) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct avc_xperms_decision_node *xpd_node, *tmp; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (!xp_node) 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci list_for_each_entry_safe(xpd_node, tmp, &xp_node->xpd_head, xpd_list) { 24762306a36Sopenharmony_ci list_del(&xpd_node->xpd_list); 24862306a36Sopenharmony_ci avc_xperms_decision_free(xpd_node); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci kmem_cache_free(avc_xperms_cachep, xp_node); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void avc_copy_xperms_decision(struct extended_perms_decision *dest, 25462306a36Sopenharmony_ci struct extended_perms_decision *src) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci dest->driver = src->driver; 25762306a36Sopenharmony_ci dest->used = src->used; 25862306a36Sopenharmony_ci if (dest->used & XPERMS_ALLOWED) 25962306a36Sopenharmony_ci memcpy(dest->allowed->p, src->allowed->p, 26062306a36Sopenharmony_ci sizeof(src->allowed->p)); 26162306a36Sopenharmony_ci if (dest->used & XPERMS_AUDITALLOW) 26262306a36Sopenharmony_ci memcpy(dest->auditallow->p, src->auditallow->p, 26362306a36Sopenharmony_ci sizeof(src->auditallow->p)); 26462306a36Sopenharmony_ci if (dest->used & XPERMS_DONTAUDIT) 26562306a36Sopenharmony_ci memcpy(dest->dontaudit->p, src->dontaudit->p, 26662306a36Sopenharmony_ci sizeof(src->dontaudit->p)); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* 27062306a36Sopenharmony_ci * similar to avc_copy_xperms_decision, but only copy decision 27162306a36Sopenharmony_ci * information relevant to this perm 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_cistatic inline void avc_quick_copy_xperms_decision(u8 perm, 27462306a36Sopenharmony_ci struct extended_perms_decision *dest, 27562306a36Sopenharmony_ci struct extended_perms_decision *src) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci /* 27862306a36Sopenharmony_ci * compute index of the u32 of the 256 bits (8 u32s) that contain this 27962306a36Sopenharmony_ci * command permission 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_ci u8 i = perm >> 5; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci dest->used = src->used; 28462306a36Sopenharmony_ci if (dest->used & XPERMS_ALLOWED) 28562306a36Sopenharmony_ci dest->allowed->p[i] = src->allowed->p[i]; 28662306a36Sopenharmony_ci if (dest->used & XPERMS_AUDITALLOW) 28762306a36Sopenharmony_ci dest->auditallow->p[i] = src->auditallow->p[i]; 28862306a36Sopenharmony_ci if (dest->used & XPERMS_DONTAUDIT) 28962306a36Sopenharmony_ci dest->dontaudit->p[i] = src->dontaudit->p[i]; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic struct avc_xperms_decision_node 29362306a36Sopenharmony_ci *avc_xperms_decision_alloc(u8 which) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct avc_xperms_decision_node *xpd_node; 29662306a36Sopenharmony_ci struct extended_perms_decision *xpd; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep, 29962306a36Sopenharmony_ci GFP_NOWAIT | __GFP_NOWARN); 30062306a36Sopenharmony_ci if (!xpd_node) 30162306a36Sopenharmony_ci return NULL; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci xpd = &xpd_node->xpd; 30462306a36Sopenharmony_ci if (which & XPERMS_ALLOWED) { 30562306a36Sopenharmony_ci xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep, 30662306a36Sopenharmony_ci GFP_NOWAIT | __GFP_NOWARN); 30762306a36Sopenharmony_ci if (!xpd->allowed) 30862306a36Sopenharmony_ci goto error; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci if (which & XPERMS_AUDITALLOW) { 31162306a36Sopenharmony_ci xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep, 31262306a36Sopenharmony_ci GFP_NOWAIT | __GFP_NOWARN); 31362306a36Sopenharmony_ci if (!xpd->auditallow) 31462306a36Sopenharmony_ci goto error; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci if (which & XPERMS_DONTAUDIT) { 31762306a36Sopenharmony_ci xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep, 31862306a36Sopenharmony_ci GFP_NOWAIT | __GFP_NOWARN); 31962306a36Sopenharmony_ci if (!xpd->dontaudit) 32062306a36Sopenharmony_ci goto error; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci return xpd_node; 32362306a36Sopenharmony_cierror: 32462306a36Sopenharmony_ci avc_xperms_decision_free(xpd_node); 32562306a36Sopenharmony_ci return NULL; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int avc_add_xperms_decision(struct avc_node *node, 32962306a36Sopenharmony_ci struct extended_perms_decision *src) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct avc_xperms_decision_node *dest_xpd; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci node->ae.xp_node->xp.len++; 33462306a36Sopenharmony_ci dest_xpd = avc_xperms_decision_alloc(src->used); 33562306a36Sopenharmony_ci if (!dest_xpd) 33662306a36Sopenharmony_ci return -ENOMEM; 33762306a36Sopenharmony_ci avc_copy_xperms_decision(&dest_xpd->xpd, src); 33862306a36Sopenharmony_ci list_add(&dest_xpd->xpd_list, &node->ae.xp_node->xpd_head); 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic struct avc_xperms_node *avc_xperms_alloc(void) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct avc_xperms_node *xp_node; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci xp_node = kmem_cache_zalloc(avc_xperms_cachep, GFP_NOWAIT | __GFP_NOWARN); 34762306a36Sopenharmony_ci if (!xp_node) 34862306a36Sopenharmony_ci return xp_node; 34962306a36Sopenharmony_ci INIT_LIST_HEAD(&xp_node->xpd_head); 35062306a36Sopenharmony_ci return xp_node; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic int avc_xperms_populate(struct avc_node *node, 35462306a36Sopenharmony_ci struct avc_xperms_node *src) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct avc_xperms_node *dest; 35762306a36Sopenharmony_ci struct avc_xperms_decision_node *dest_xpd; 35862306a36Sopenharmony_ci struct avc_xperms_decision_node *src_xpd; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (src->xp.len == 0) 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci dest = avc_xperms_alloc(); 36362306a36Sopenharmony_ci if (!dest) 36462306a36Sopenharmony_ci return -ENOMEM; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p)); 36762306a36Sopenharmony_ci dest->xp.len = src->xp.len; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* for each source xpd allocate a destination xpd and copy */ 37062306a36Sopenharmony_ci list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) { 37162306a36Sopenharmony_ci dest_xpd = avc_xperms_decision_alloc(src_xpd->xpd.used); 37262306a36Sopenharmony_ci if (!dest_xpd) 37362306a36Sopenharmony_ci goto error; 37462306a36Sopenharmony_ci avc_copy_xperms_decision(&dest_xpd->xpd, &src_xpd->xpd); 37562306a36Sopenharmony_ci list_add(&dest_xpd->xpd_list, &dest->xpd_head); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci node->ae.xp_node = dest; 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_cierror: 38062306a36Sopenharmony_ci avc_xperms_free(dest); 38162306a36Sopenharmony_ci return -ENOMEM; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic inline u32 avc_xperms_audit_required(u32 requested, 38662306a36Sopenharmony_ci struct av_decision *avd, 38762306a36Sopenharmony_ci struct extended_perms_decision *xpd, 38862306a36Sopenharmony_ci u8 perm, 38962306a36Sopenharmony_ci int result, 39062306a36Sopenharmony_ci u32 *deniedp) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci u32 denied, audited; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci denied = requested & ~avd->allowed; 39562306a36Sopenharmony_ci if (unlikely(denied)) { 39662306a36Sopenharmony_ci audited = denied & avd->auditdeny; 39762306a36Sopenharmony_ci if (audited && xpd) { 39862306a36Sopenharmony_ci if (avc_xperms_has_perm(xpd, perm, XPERMS_DONTAUDIT)) 39962306a36Sopenharmony_ci audited &= ~requested; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } else if (result) { 40262306a36Sopenharmony_ci audited = denied = requested; 40362306a36Sopenharmony_ci } else { 40462306a36Sopenharmony_ci audited = requested & avd->auditallow; 40562306a36Sopenharmony_ci if (audited && xpd) { 40662306a36Sopenharmony_ci if (!avc_xperms_has_perm(xpd, perm, XPERMS_AUDITALLOW)) 40762306a36Sopenharmony_ci audited &= ~requested; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci *deniedp = denied; 41262306a36Sopenharmony_ci return audited; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass, 41662306a36Sopenharmony_ci u32 requested, struct av_decision *avd, 41762306a36Sopenharmony_ci struct extended_perms_decision *xpd, 41862306a36Sopenharmony_ci u8 perm, int result, 41962306a36Sopenharmony_ci struct common_audit_data *ad) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci u32 audited, denied; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci audited = avc_xperms_audit_required( 42462306a36Sopenharmony_ci requested, avd, xpd, perm, result, &denied); 42562306a36Sopenharmony_ci if (likely(!audited)) 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci return slow_avc_audit(ssid, tsid, tclass, requested, 42862306a36Sopenharmony_ci audited, denied, result, ad); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic void avc_node_free(struct rcu_head *rhead) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct avc_node *node = container_of(rhead, struct avc_node, rhead); 43462306a36Sopenharmony_ci avc_xperms_free(node->ae.xp_node); 43562306a36Sopenharmony_ci kmem_cache_free(avc_node_cachep, node); 43662306a36Sopenharmony_ci avc_cache_stats_incr(frees); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic void avc_node_delete(struct avc_node *node) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci hlist_del_rcu(&node->list); 44262306a36Sopenharmony_ci call_rcu(&node->rhead, avc_node_free); 44362306a36Sopenharmony_ci atomic_dec(&selinux_avc.avc_cache.active_nodes); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void avc_node_kill(struct avc_node *node) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci avc_xperms_free(node->ae.xp_node); 44962306a36Sopenharmony_ci kmem_cache_free(avc_node_cachep, node); 45062306a36Sopenharmony_ci avc_cache_stats_incr(frees); 45162306a36Sopenharmony_ci atomic_dec(&selinux_avc.avc_cache.active_nodes); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic void avc_node_replace(struct avc_node *new, struct avc_node *old) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci hlist_replace_rcu(&old->list, &new->list); 45762306a36Sopenharmony_ci call_rcu(&old->rhead, avc_node_free); 45862306a36Sopenharmony_ci atomic_dec(&selinux_avc.avc_cache.active_nodes); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic inline int avc_reclaim_node(void) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct avc_node *node; 46462306a36Sopenharmony_ci int hvalue, try, ecx; 46562306a36Sopenharmony_ci unsigned long flags; 46662306a36Sopenharmony_ci struct hlist_head *head; 46762306a36Sopenharmony_ci spinlock_t *lock; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) { 47062306a36Sopenharmony_ci hvalue = atomic_inc_return(&selinux_avc.avc_cache.lru_hint) & 47162306a36Sopenharmony_ci (AVC_CACHE_SLOTS - 1); 47262306a36Sopenharmony_ci head = &selinux_avc.avc_cache.slots[hvalue]; 47362306a36Sopenharmony_ci lock = &selinux_avc.avc_cache.slots_lock[hvalue]; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (!spin_trylock_irqsave(lock, flags)) 47662306a36Sopenharmony_ci continue; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci rcu_read_lock(); 47962306a36Sopenharmony_ci hlist_for_each_entry(node, head, list) { 48062306a36Sopenharmony_ci avc_node_delete(node); 48162306a36Sopenharmony_ci avc_cache_stats_incr(reclaims); 48262306a36Sopenharmony_ci ecx++; 48362306a36Sopenharmony_ci if (ecx >= AVC_CACHE_RECLAIM) { 48462306a36Sopenharmony_ci rcu_read_unlock(); 48562306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 48662306a36Sopenharmony_ci goto out; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci rcu_read_unlock(); 49062306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ciout: 49362306a36Sopenharmony_ci return ecx; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic struct avc_node *avc_alloc_node(void) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct avc_node *node; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT | __GFP_NOWARN); 50162306a36Sopenharmony_ci if (!node) 50262306a36Sopenharmony_ci goto out; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci INIT_HLIST_NODE(&node->list); 50562306a36Sopenharmony_ci avc_cache_stats_incr(allocations); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (atomic_inc_return(&selinux_avc.avc_cache.active_nodes) > 50862306a36Sopenharmony_ci selinux_avc.avc_cache_threshold) 50962306a36Sopenharmony_ci avc_reclaim_node(); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ciout: 51262306a36Sopenharmony_ci return node; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci node->ae.ssid = ssid; 51862306a36Sopenharmony_ci node->ae.tsid = tsid; 51962306a36Sopenharmony_ci node->ae.tclass = tclass; 52062306a36Sopenharmony_ci memcpy(&node->ae.avd, avd, sizeof(node->ae.avd)); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct avc_node *node, *ret = NULL; 52662306a36Sopenharmony_ci u32 hvalue; 52762306a36Sopenharmony_ci struct hlist_head *head; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci hvalue = avc_hash(ssid, tsid, tclass); 53062306a36Sopenharmony_ci head = &selinux_avc.avc_cache.slots[hvalue]; 53162306a36Sopenharmony_ci hlist_for_each_entry_rcu(node, head, list) { 53262306a36Sopenharmony_ci if (ssid == node->ae.ssid && 53362306a36Sopenharmony_ci tclass == node->ae.tclass && 53462306a36Sopenharmony_ci tsid == node->ae.tsid) { 53562306a36Sopenharmony_ci ret = node; 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return ret; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci/** 54462306a36Sopenharmony_ci * avc_lookup - Look up an AVC entry. 54562306a36Sopenharmony_ci * @ssid: source security identifier 54662306a36Sopenharmony_ci * @tsid: target security identifier 54762306a36Sopenharmony_ci * @tclass: target security class 54862306a36Sopenharmony_ci * 54962306a36Sopenharmony_ci * Look up an AVC entry that is valid for the 55062306a36Sopenharmony_ci * (@ssid, @tsid), interpreting the permissions 55162306a36Sopenharmony_ci * based on @tclass. If a valid AVC entry exists, 55262306a36Sopenharmony_ci * then this function returns the avc_node. 55362306a36Sopenharmony_ci * Otherwise, this function returns NULL. 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_cistatic struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct avc_node *node; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci avc_cache_stats_incr(lookups); 56062306a36Sopenharmony_ci node = avc_search_node(ssid, tsid, tclass); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (node) 56362306a36Sopenharmony_ci return node; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci avc_cache_stats_incr(misses); 56662306a36Sopenharmony_ci return NULL; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int avc_latest_notif_update(u32 seqno, int is_insert) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci int ret = 0; 57262306a36Sopenharmony_ci static DEFINE_SPINLOCK(notif_lock); 57362306a36Sopenharmony_ci unsigned long flag; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci spin_lock_irqsave(¬if_lock, flag); 57662306a36Sopenharmony_ci if (is_insert) { 57762306a36Sopenharmony_ci if (seqno < selinux_avc.avc_cache.latest_notif) { 57862306a36Sopenharmony_ci pr_warn("SELinux: avc: seqno %d < latest_notif %d\n", 57962306a36Sopenharmony_ci seqno, selinux_avc.avc_cache.latest_notif); 58062306a36Sopenharmony_ci ret = -EAGAIN; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci } else { 58362306a36Sopenharmony_ci if (seqno > selinux_avc.avc_cache.latest_notif) 58462306a36Sopenharmony_ci selinux_avc.avc_cache.latest_notif = seqno; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci spin_unlock_irqrestore(¬if_lock, flag); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return ret; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/** 59262306a36Sopenharmony_ci * avc_insert - Insert an AVC entry. 59362306a36Sopenharmony_ci * @ssid: source security identifier 59462306a36Sopenharmony_ci * @tsid: target security identifier 59562306a36Sopenharmony_ci * @tclass: target security class 59662306a36Sopenharmony_ci * @avd: resulting av decision 59762306a36Sopenharmony_ci * @xp_node: resulting extended permissions 59862306a36Sopenharmony_ci * 59962306a36Sopenharmony_ci * Insert an AVC entry for the SID pair 60062306a36Sopenharmony_ci * (@ssid, @tsid) and class @tclass. 60162306a36Sopenharmony_ci * The access vectors and the sequence number are 60262306a36Sopenharmony_ci * normally provided by the security server in 60362306a36Sopenharmony_ci * response to a security_compute_av() call. If the 60462306a36Sopenharmony_ci * sequence number @avd->seqno is not less than the latest 60562306a36Sopenharmony_ci * revocation notification, then the function copies 60662306a36Sopenharmony_ci * the access vectors into a cache entry. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_cistatic void avc_insert(u32 ssid, u32 tsid, u16 tclass, 60962306a36Sopenharmony_ci struct av_decision *avd, struct avc_xperms_node *xp_node) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct avc_node *pos, *node = NULL; 61262306a36Sopenharmony_ci u32 hvalue; 61362306a36Sopenharmony_ci unsigned long flag; 61462306a36Sopenharmony_ci spinlock_t *lock; 61562306a36Sopenharmony_ci struct hlist_head *head; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (avc_latest_notif_update(avd->seqno, 1)) 61862306a36Sopenharmony_ci return; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci node = avc_alloc_node(); 62162306a36Sopenharmony_ci if (!node) 62262306a36Sopenharmony_ci return; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci avc_node_populate(node, ssid, tsid, tclass, avd); 62562306a36Sopenharmony_ci if (avc_xperms_populate(node, xp_node)) { 62662306a36Sopenharmony_ci avc_node_kill(node); 62762306a36Sopenharmony_ci return; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci hvalue = avc_hash(ssid, tsid, tclass); 63162306a36Sopenharmony_ci head = &selinux_avc.avc_cache.slots[hvalue]; 63262306a36Sopenharmony_ci lock = &selinux_avc.avc_cache.slots_lock[hvalue]; 63362306a36Sopenharmony_ci spin_lock_irqsave(lock, flag); 63462306a36Sopenharmony_ci hlist_for_each_entry(pos, head, list) { 63562306a36Sopenharmony_ci if (pos->ae.ssid == ssid && 63662306a36Sopenharmony_ci pos->ae.tsid == tsid && 63762306a36Sopenharmony_ci pos->ae.tclass == tclass) { 63862306a36Sopenharmony_ci avc_node_replace(node, pos); 63962306a36Sopenharmony_ci goto found; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci hlist_add_head_rcu(&node->list, head); 64362306a36Sopenharmony_cifound: 64462306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flag); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/** 64862306a36Sopenharmony_ci * avc_audit_pre_callback - SELinux specific information 64962306a36Sopenharmony_ci * will be called by generic audit code 65062306a36Sopenharmony_ci * @ab: the audit buffer 65162306a36Sopenharmony_ci * @a: audit_data 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_cistatic void avc_audit_pre_callback(struct audit_buffer *ab, void *a) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct common_audit_data *ad = a; 65662306a36Sopenharmony_ci struct selinux_audit_data *sad = ad->selinux_audit_data; 65762306a36Sopenharmony_ci u32 av = sad->audited, perm; 65862306a36Sopenharmony_ci const char *const *perms; 65962306a36Sopenharmony_ci u32 i; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci audit_log_format(ab, "avc: %s ", sad->denied ? "denied" : "granted"); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (av == 0) { 66462306a36Sopenharmony_ci audit_log_format(ab, " null"); 66562306a36Sopenharmony_ci return; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci perms = secclass_map[sad->tclass-1].perms; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci audit_log_format(ab, " {"); 67162306a36Sopenharmony_ci i = 0; 67262306a36Sopenharmony_ci perm = 1; 67362306a36Sopenharmony_ci while (i < (sizeof(av) * 8)) { 67462306a36Sopenharmony_ci if ((perm & av) && perms[i]) { 67562306a36Sopenharmony_ci audit_log_format(ab, " %s", perms[i]); 67662306a36Sopenharmony_ci av &= ~perm; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci i++; 67962306a36Sopenharmony_ci perm <<= 1; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (av) 68362306a36Sopenharmony_ci audit_log_format(ab, " 0x%x", av); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci audit_log_format(ab, " } for "); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci/** 68962306a36Sopenharmony_ci * avc_audit_post_callback - SELinux specific information 69062306a36Sopenharmony_ci * will be called by generic audit code 69162306a36Sopenharmony_ci * @ab: the audit buffer 69262306a36Sopenharmony_ci * @a: audit_data 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_cistatic void avc_audit_post_callback(struct audit_buffer *ab, void *a) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct common_audit_data *ad = a; 69762306a36Sopenharmony_ci struct selinux_audit_data *sad = ad->selinux_audit_data; 69862306a36Sopenharmony_ci char *scontext = NULL; 69962306a36Sopenharmony_ci char *tcontext = NULL; 70062306a36Sopenharmony_ci const char *tclass = NULL; 70162306a36Sopenharmony_ci u32 scontext_len; 70262306a36Sopenharmony_ci u32 tcontext_len; 70362306a36Sopenharmony_ci int rc; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci rc = security_sid_to_context(sad->ssid, &scontext, 70662306a36Sopenharmony_ci &scontext_len); 70762306a36Sopenharmony_ci if (rc) 70862306a36Sopenharmony_ci audit_log_format(ab, " ssid=%d", sad->ssid); 70962306a36Sopenharmony_ci else 71062306a36Sopenharmony_ci audit_log_format(ab, " scontext=%s", scontext); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci rc = security_sid_to_context(sad->tsid, &tcontext, 71362306a36Sopenharmony_ci &tcontext_len); 71462306a36Sopenharmony_ci if (rc) 71562306a36Sopenharmony_ci audit_log_format(ab, " tsid=%d", sad->tsid); 71662306a36Sopenharmony_ci else 71762306a36Sopenharmony_ci audit_log_format(ab, " tcontext=%s", tcontext); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci tclass = secclass_map[sad->tclass-1].name; 72062306a36Sopenharmony_ci audit_log_format(ab, " tclass=%s", tclass); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (sad->denied) 72362306a36Sopenharmony_ci audit_log_format(ab, " permissive=%u", sad->result ? 0 : 1); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci trace_selinux_audited(sad, scontext, tcontext, tclass); 72662306a36Sopenharmony_ci kfree(tcontext); 72762306a36Sopenharmony_ci kfree(scontext); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* in case of invalid context report also the actual context string */ 73062306a36Sopenharmony_ci rc = security_sid_to_context_inval(sad->ssid, &scontext, 73162306a36Sopenharmony_ci &scontext_len); 73262306a36Sopenharmony_ci if (!rc && scontext) { 73362306a36Sopenharmony_ci if (scontext_len && scontext[scontext_len - 1] == '\0') 73462306a36Sopenharmony_ci scontext_len--; 73562306a36Sopenharmony_ci audit_log_format(ab, " srawcon="); 73662306a36Sopenharmony_ci audit_log_n_untrustedstring(ab, scontext, scontext_len); 73762306a36Sopenharmony_ci kfree(scontext); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci rc = security_sid_to_context_inval(sad->tsid, &scontext, 74162306a36Sopenharmony_ci &scontext_len); 74262306a36Sopenharmony_ci if (!rc && scontext) { 74362306a36Sopenharmony_ci if (scontext_len && scontext[scontext_len - 1] == '\0') 74462306a36Sopenharmony_ci scontext_len--; 74562306a36Sopenharmony_ci audit_log_format(ab, " trawcon="); 74662306a36Sopenharmony_ci audit_log_n_untrustedstring(ab, scontext, scontext_len); 74762306a36Sopenharmony_ci kfree(scontext); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/* 75262306a36Sopenharmony_ci * This is the slow part of avc audit with big stack footprint. 75362306a36Sopenharmony_ci * Note that it is non-blocking and can be called from under 75462306a36Sopenharmony_ci * rcu_read_lock(). 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_cinoinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, 75762306a36Sopenharmony_ci u32 requested, u32 audited, u32 denied, int result, 75862306a36Sopenharmony_ci struct common_audit_data *a) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct common_audit_data stack_data; 76162306a36Sopenharmony_ci struct selinux_audit_data sad; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (WARN_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map))) 76462306a36Sopenharmony_ci return -EINVAL; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (!a) { 76762306a36Sopenharmony_ci a = &stack_data; 76862306a36Sopenharmony_ci a->type = LSM_AUDIT_DATA_NONE; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci sad.tclass = tclass; 77262306a36Sopenharmony_ci sad.requested = requested; 77362306a36Sopenharmony_ci sad.ssid = ssid; 77462306a36Sopenharmony_ci sad.tsid = tsid; 77562306a36Sopenharmony_ci sad.audited = audited; 77662306a36Sopenharmony_ci sad.denied = denied; 77762306a36Sopenharmony_ci sad.result = result; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci a->selinux_audit_data = &sad; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback); 78262306a36Sopenharmony_ci return 0; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/** 78662306a36Sopenharmony_ci * avc_add_callback - Register a callback for security events. 78762306a36Sopenharmony_ci * @callback: callback function 78862306a36Sopenharmony_ci * @events: security events 78962306a36Sopenharmony_ci * 79062306a36Sopenharmony_ci * Register a callback function for events in the set @events. 79162306a36Sopenharmony_ci * Returns %0 on success or -%ENOMEM if insufficient memory 79262306a36Sopenharmony_ci * exists to add the callback. 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_ciint __init avc_add_callback(int (*callback)(u32 event), u32 events) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci struct avc_callback_node *c; 79762306a36Sopenharmony_ci int rc = 0; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci c = kmalloc(sizeof(*c), GFP_KERNEL); 80062306a36Sopenharmony_ci if (!c) { 80162306a36Sopenharmony_ci rc = -ENOMEM; 80262306a36Sopenharmony_ci goto out; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci c->callback = callback; 80662306a36Sopenharmony_ci c->events = events; 80762306a36Sopenharmony_ci c->next = avc_callbacks; 80862306a36Sopenharmony_ci avc_callbacks = c; 80962306a36Sopenharmony_ciout: 81062306a36Sopenharmony_ci return rc; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci/** 81462306a36Sopenharmony_ci * avc_update_node - Update an AVC entry 81562306a36Sopenharmony_ci * @event : Updating event 81662306a36Sopenharmony_ci * @perms : Permission mask bits 81762306a36Sopenharmony_ci * @driver: xperm driver information 81862306a36Sopenharmony_ci * @xperm: xperm permissions 81962306a36Sopenharmony_ci * @ssid: AVC entry source sid 82062306a36Sopenharmony_ci * @tsid: AVC entry target sid 82162306a36Sopenharmony_ci * @tclass : AVC entry target object class 82262306a36Sopenharmony_ci * @seqno : sequence number when decision was made 82362306a36Sopenharmony_ci * @xpd: extended_perms_decision to be added to the node 82462306a36Sopenharmony_ci * @flags: the AVC_* flags, e.g. AVC_EXTENDED_PERMS, or 0. 82562306a36Sopenharmony_ci * 82662306a36Sopenharmony_ci * if a valid AVC entry doesn't exist,this function returns -ENOENT. 82762306a36Sopenharmony_ci * if kmalloc() called internal returns NULL, this function returns -ENOMEM. 82862306a36Sopenharmony_ci * otherwise, this function updates the AVC entry. The original AVC-entry object 82962306a36Sopenharmony_ci * will release later by RCU. 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_cistatic int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid, 83262306a36Sopenharmony_ci u32 tsid, u16 tclass, u32 seqno, 83362306a36Sopenharmony_ci struct extended_perms_decision *xpd, 83462306a36Sopenharmony_ci u32 flags) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci u32 hvalue; 83762306a36Sopenharmony_ci int rc = 0; 83862306a36Sopenharmony_ci unsigned long flag; 83962306a36Sopenharmony_ci struct avc_node *pos, *node, *orig = NULL; 84062306a36Sopenharmony_ci struct hlist_head *head; 84162306a36Sopenharmony_ci spinlock_t *lock; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci node = avc_alloc_node(); 84462306a36Sopenharmony_ci if (!node) { 84562306a36Sopenharmony_ci rc = -ENOMEM; 84662306a36Sopenharmony_ci goto out; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* Lock the target slot */ 85062306a36Sopenharmony_ci hvalue = avc_hash(ssid, tsid, tclass); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci head = &selinux_avc.avc_cache.slots[hvalue]; 85362306a36Sopenharmony_ci lock = &selinux_avc.avc_cache.slots_lock[hvalue]; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci spin_lock_irqsave(lock, flag); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci hlist_for_each_entry(pos, head, list) { 85862306a36Sopenharmony_ci if (ssid == pos->ae.ssid && 85962306a36Sopenharmony_ci tsid == pos->ae.tsid && 86062306a36Sopenharmony_ci tclass == pos->ae.tclass && 86162306a36Sopenharmony_ci seqno == pos->ae.avd.seqno){ 86262306a36Sopenharmony_ci orig = pos; 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (!orig) { 86862306a36Sopenharmony_ci rc = -ENOENT; 86962306a36Sopenharmony_ci avc_node_kill(node); 87062306a36Sopenharmony_ci goto out_unlock; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* 87462306a36Sopenharmony_ci * Copy and replace original node. 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (orig->ae.xp_node) { 88062306a36Sopenharmony_ci rc = avc_xperms_populate(node, orig->ae.xp_node); 88162306a36Sopenharmony_ci if (rc) { 88262306a36Sopenharmony_ci avc_node_kill(node); 88362306a36Sopenharmony_ci goto out_unlock; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci switch (event) { 88862306a36Sopenharmony_ci case AVC_CALLBACK_GRANT: 88962306a36Sopenharmony_ci node->ae.avd.allowed |= perms; 89062306a36Sopenharmony_ci if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS)) 89162306a36Sopenharmony_ci avc_xperms_allow_perm(node->ae.xp_node, driver, xperm); 89262306a36Sopenharmony_ci break; 89362306a36Sopenharmony_ci case AVC_CALLBACK_TRY_REVOKE: 89462306a36Sopenharmony_ci case AVC_CALLBACK_REVOKE: 89562306a36Sopenharmony_ci node->ae.avd.allowed &= ~perms; 89662306a36Sopenharmony_ci break; 89762306a36Sopenharmony_ci case AVC_CALLBACK_AUDITALLOW_ENABLE: 89862306a36Sopenharmony_ci node->ae.avd.auditallow |= perms; 89962306a36Sopenharmony_ci break; 90062306a36Sopenharmony_ci case AVC_CALLBACK_AUDITALLOW_DISABLE: 90162306a36Sopenharmony_ci node->ae.avd.auditallow &= ~perms; 90262306a36Sopenharmony_ci break; 90362306a36Sopenharmony_ci case AVC_CALLBACK_AUDITDENY_ENABLE: 90462306a36Sopenharmony_ci node->ae.avd.auditdeny |= perms; 90562306a36Sopenharmony_ci break; 90662306a36Sopenharmony_ci case AVC_CALLBACK_AUDITDENY_DISABLE: 90762306a36Sopenharmony_ci node->ae.avd.auditdeny &= ~perms; 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci case AVC_CALLBACK_ADD_XPERMS: 91062306a36Sopenharmony_ci avc_add_xperms_decision(node, xpd); 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci avc_node_replace(node, orig); 91462306a36Sopenharmony_ciout_unlock: 91562306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flag); 91662306a36Sopenharmony_ciout: 91762306a36Sopenharmony_ci return rc; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci/** 92162306a36Sopenharmony_ci * avc_flush - Flush the cache 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_cistatic void avc_flush(void) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct hlist_head *head; 92662306a36Sopenharmony_ci struct avc_node *node; 92762306a36Sopenharmony_ci spinlock_t *lock; 92862306a36Sopenharmony_ci unsigned long flag; 92962306a36Sopenharmony_ci int i; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci for (i = 0; i < AVC_CACHE_SLOTS; i++) { 93262306a36Sopenharmony_ci head = &selinux_avc.avc_cache.slots[i]; 93362306a36Sopenharmony_ci lock = &selinux_avc.avc_cache.slots_lock[i]; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci spin_lock_irqsave(lock, flag); 93662306a36Sopenharmony_ci /* 93762306a36Sopenharmony_ci * With preemptable RCU, the outer spinlock does not 93862306a36Sopenharmony_ci * prevent RCU grace periods from ending. 93962306a36Sopenharmony_ci */ 94062306a36Sopenharmony_ci rcu_read_lock(); 94162306a36Sopenharmony_ci hlist_for_each_entry(node, head, list) 94262306a36Sopenharmony_ci avc_node_delete(node); 94362306a36Sopenharmony_ci rcu_read_unlock(); 94462306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flag); 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/** 94962306a36Sopenharmony_ci * avc_ss_reset - Flush the cache and revalidate migrated permissions. 95062306a36Sopenharmony_ci * @seqno: policy sequence number 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_ciint avc_ss_reset(u32 seqno) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct avc_callback_node *c; 95562306a36Sopenharmony_ci int rc = 0, tmprc; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci avc_flush(); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci for (c = avc_callbacks; c; c = c->next) { 96062306a36Sopenharmony_ci if (c->events & AVC_CALLBACK_RESET) { 96162306a36Sopenharmony_ci tmprc = c->callback(AVC_CALLBACK_RESET); 96262306a36Sopenharmony_ci /* save the first error encountered for the return 96362306a36Sopenharmony_ci value and continue processing the callbacks */ 96462306a36Sopenharmony_ci if (!rc) 96562306a36Sopenharmony_ci rc = tmprc; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci avc_latest_notif_update(seqno, 0); 97062306a36Sopenharmony_ci return rc; 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci/** 97462306a36Sopenharmony_ci * avc_compute_av - Add an entry to the AVC based on the security policy 97562306a36Sopenharmony_ci * @ssid: subject 97662306a36Sopenharmony_ci * @tsid: object/target 97762306a36Sopenharmony_ci * @tclass: object class 97862306a36Sopenharmony_ci * @avd: access vector decision 97962306a36Sopenharmony_ci * @xp_node: AVC extended permissions node 98062306a36Sopenharmony_ci * 98162306a36Sopenharmony_ci * Slow-path helper function for avc_has_perm_noaudit, when the avc_node lookup 98262306a36Sopenharmony_ci * fails. Don't inline this, since it's the slow-path and just results in a 98362306a36Sopenharmony_ci * bigger stack frame. 98462306a36Sopenharmony_ci */ 98562306a36Sopenharmony_cistatic noinline void avc_compute_av(u32 ssid, u32 tsid, u16 tclass, 98662306a36Sopenharmony_ci struct av_decision *avd, 98762306a36Sopenharmony_ci struct avc_xperms_node *xp_node) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci INIT_LIST_HEAD(&xp_node->xpd_head); 99062306a36Sopenharmony_ci security_compute_av(ssid, tsid, tclass, avd, &xp_node->xp); 99162306a36Sopenharmony_ci avc_insert(ssid, tsid, tclass, avd, xp_node); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic noinline int avc_denied(u32 ssid, u32 tsid, 99562306a36Sopenharmony_ci u16 tclass, u32 requested, 99662306a36Sopenharmony_ci u8 driver, u8 xperm, unsigned int flags, 99762306a36Sopenharmony_ci struct av_decision *avd) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci if (flags & AVC_STRICT) 100062306a36Sopenharmony_ci return -EACCES; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (enforcing_enabled() && 100362306a36Sopenharmony_ci !(avd->flags & AVD_FLAGS_PERMISSIVE)) 100462306a36Sopenharmony_ci return -EACCES; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci avc_update_node(AVC_CALLBACK_GRANT, requested, driver, 100762306a36Sopenharmony_ci xperm, ssid, tsid, tclass, avd->seqno, NULL, flags); 100862306a36Sopenharmony_ci return 0; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci/* 101262306a36Sopenharmony_ci * The avc extended permissions logic adds an additional 256 bits of 101362306a36Sopenharmony_ci * permissions to an avc node when extended permissions for that node are 101462306a36Sopenharmony_ci * specified in the avtab. If the additional 256 permissions is not adequate, 101562306a36Sopenharmony_ci * as-is the case with ioctls, then multiple may be chained together and the 101662306a36Sopenharmony_ci * driver field is used to specify which set contains the permission. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ciint avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested, 101962306a36Sopenharmony_ci u8 driver, u8 xperm, struct common_audit_data *ad) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci struct avc_node *node; 102262306a36Sopenharmony_ci struct av_decision avd; 102362306a36Sopenharmony_ci u32 denied; 102462306a36Sopenharmony_ci struct extended_perms_decision local_xpd; 102562306a36Sopenharmony_ci struct extended_perms_decision *xpd = NULL; 102662306a36Sopenharmony_ci struct extended_perms_data allowed; 102762306a36Sopenharmony_ci struct extended_perms_data auditallow; 102862306a36Sopenharmony_ci struct extended_perms_data dontaudit; 102962306a36Sopenharmony_ci struct avc_xperms_node local_xp_node; 103062306a36Sopenharmony_ci struct avc_xperms_node *xp_node; 103162306a36Sopenharmony_ci int rc = 0, rc2; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci xp_node = &local_xp_node; 103462306a36Sopenharmony_ci if (WARN_ON(!requested)) 103562306a36Sopenharmony_ci return -EACCES; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci rcu_read_lock(); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci node = avc_lookup(ssid, tsid, tclass); 104062306a36Sopenharmony_ci if (unlikely(!node)) { 104162306a36Sopenharmony_ci avc_compute_av(ssid, tsid, tclass, &avd, xp_node); 104262306a36Sopenharmony_ci } else { 104362306a36Sopenharmony_ci memcpy(&avd, &node->ae.avd, sizeof(avd)); 104462306a36Sopenharmony_ci xp_node = node->ae.xp_node; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci /* if extended permissions are not defined, only consider av_decision */ 104762306a36Sopenharmony_ci if (!xp_node || !xp_node->xp.len) 104862306a36Sopenharmony_ci goto decision; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci local_xpd.allowed = &allowed; 105162306a36Sopenharmony_ci local_xpd.auditallow = &auditallow; 105262306a36Sopenharmony_ci local_xpd.dontaudit = &dontaudit; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci xpd = avc_xperms_decision_lookup(driver, xp_node); 105562306a36Sopenharmony_ci if (unlikely(!xpd)) { 105662306a36Sopenharmony_ci /* 105762306a36Sopenharmony_ci * Compute the extended_perms_decision only if the driver 105862306a36Sopenharmony_ci * is flagged 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ci if (!security_xperm_test(xp_node->xp.drivers.p, driver)) { 106162306a36Sopenharmony_ci avd.allowed &= ~requested; 106262306a36Sopenharmony_ci goto decision; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci rcu_read_unlock(); 106562306a36Sopenharmony_ci security_compute_xperms_decision(ssid, tsid, tclass, 106662306a36Sopenharmony_ci driver, &local_xpd); 106762306a36Sopenharmony_ci rcu_read_lock(); 106862306a36Sopenharmony_ci avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, 106962306a36Sopenharmony_ci driver, xperm, ssid, tsid, tclass, avd.seqno, 107062306a36Sopenharmony_ci &local_xpd, 0); 107162306a36Sopenharmony_ci } else { 107262306a36Sopenharmony_ci avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd); 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci xpd = &local_xpd; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (!avc_xperms_has_perm(xpd, xperm, XPERMS_ALLOWED)) 107762306a36Sopenharmony_ci avd.allowed &= ~requested; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cidecision: 108062306a36Sopenharmony_ci denied = requested & ~(avd.allowed); 108162306a36Sopenharmony_ci if (unlikely(denied)) 108262306a36Sopenharmony_ci rc = avc_denied(ssid, tsid, tclass, requested, 108362306a36Sopenharmony_ci driver, xperm, AVC_EXTENDED_PERMS, &avd); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci rcu_read_unlock(); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci rc2 = avc_xperms_audit(ssid, tsid, tclass, requested, 108862306a36Sopenharmony_ci &avd, xpd, xperm, rc, ad); 108962306a36Sopenharmony_ci if (rc2) 109062306a36Sopenharmony_ci return rc2; 109162306a36Sopenharmony_ci return rc; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci/** 109562306a36Sopenharmony_ci * avc_perm_nonode - Add an entry to the AVC 109662306a36Sopenharmony_ci * @ssid: subject 109762306a36Sopenharmony_ci * @tsid: object/target 109862306a36Sopenharmony_ci * @tclass: object class 109962306a36Sopenharmony_ci * @requested: requested permissions 110062306a36Sopenharmony_ci * @flags: AVC flags 110162306a36Sopenharmony_ci * @avd: access vector decision 110262306a36Sopenharmony_ci * 110362306a36Sopenharmony_ci * This is the "we have no node" part of avc_has_perm_noaudit(), which is 110462306a36Sopenharmony_ci * unlikely and needs extra stack space for the new node that we generate, so 110562306a36Sopenharmony_ci * don't inline it. 110662306a36Sopenharmony_ci */ 110762306a36Sopenharmony_cistatic noinline int avc_perm_nonode(u32 ssid, u32 tsid, u16 tclass, 110862306a36Sopenharmony_ci u32 requested, unsigned int flags, 110962306a36Sopenharmony_ci struct av_decision *avd) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci u32 denied; 111262306a36Sopenharmony_ci struct avc_xperms_node xp_node; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci avc_compute_av(ssid, tsid, tclass, avd, &xp_node); 111562306a36Sopenharmony_ci denied = requested & ~(avd->allowed); 111662306a36Sopenharmony_ci if (unlikely(denied)) 111762306a36Sopenharmony_ci return avc_denied(ssid, tsid, tclass, requested, 0, 0, 111862306a36Sopenharmony_ci flags, avd); 111962306a36Sopenharmony_ci return 0; 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci/** 112362306a36Sopenharmony_ci * avc_has_perm_noaudit - Check permissions but perform no auditing. 112462306a36Sopenharmony_ci * @ssid: source security identifier 112562306a36Sopenharmony_ci * @tsid: target security identifier 112662306a36Sopenharmony_ci * @tclass: target security class 112762306a36Sopenharmony_ci * @requested: requested permissions, interpreted based on @tclass 112862306a36Sopenharmony_ci * @flags: AVC_STRICT or 0 112962306a36Sopenharmony_ci * @avd: access vector decisions 113062306a36Sopenharmony_ci * 113162306a36Sopenharmony_ci * Check the AVC to determine whether the @requested permissions are granted 113262306a36Sopenharmony_ci * for the SID pair (@ssid, @tsid), interpreting the permissions 113362306a36Sopenharmony_ci * based on @tclass, and call the security server on a cache miss to obtain 113462306a36Sopenharmony_ci * a new decision and add it to the cache. Return a copy of the decisions 113562306a36Sopenharmony_ci * in @avd. Return %0 if all @requested permissions are granted, 113662306a36Sopenharmony_ci * -%EACCES if any permissions are denied, or another -errno upon 113762306a36Sopenharmony_ci * other errors. This function is typically called by avc_has_perm(), 113862306a36Sopenharmony_ci * but may also be called directly to separate permission checking from 113962306a36Sopenharmony_ci * auditing, e.g. in cases where a lock must be held for the check but 114062306a36Sopenharmony_ci * should be released for the auditing. 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_ciinline int avc_has_perm_noaudit(u32 ssid, u32 tsid, 114362306a36Sopenharmony_ci u16 tclass, u32 requested, 114462306a36Sopenharmony_ci unsigned int flags, 114562306a36Sopenharmony_ci struct av_decision *avd) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci u32 denied; 114862306a36Sopenharmony_ci struct avc_node *node; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (WARN_ON(!requested)) 115162306a36Sopenharmony_ci return -EACCES; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci rcu_read_lock(); 115462306a36Sopenharmony_ci node = avc_lookup(ssid, tsid, tclass); 115562306a36Sopenharmony_ci if (unlikely(!node)) { 115662306a36Sopenharmony_ci rcu_read_unlock(); 115762306a36Sopenharmony_ci return avc_perm_nonode(ssid, tsid, tclass, requested, 115862306a36Sopenharmony_ci flags, avd); 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci denied = requested & ~node->ae.avd.allowed; 116162306a36Sopenharmony_ci memcpy(avd, &node->ae.avd, sizeof(*avd)); 116262306a36Sopenharmony_ci rcu_read_unlock(); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (unlikely(denied)) 116562306a36Sopenharmony_ci return avc_denied(ssid, tsid, tclass, requested, 0, 0, 116662306a36Sopenharmony_ci flags, avd); 116762306a36Sopenharmony_ci return 0; 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci/** 117162306a36Sopenharmony_ci * avc_has_perm - Check permissions and perform any appropriate auditing. 117262306a36Sopenharmony_ci * @ssid: source security identifier 117362306a36Sopenharmony_ci * @tsid: target security identifier 117462306a36Sopenharmony_ci * @tclass: target security class 117562306a36Sopenharmony_ci * @requested: requested permissions, interpreted based on @tclass 117662306a36Sopenharmony_ci * @auditdata: auxiliary audit data 117762306a36Sopenharmony_ci * 117862306a36Sopenharmony_ci * Check the AVC to determine whether the @requested permissions are granted 117962306a36Sopenharmony_ci * for the SID pair (@ssid, @tsid), interpreting the permissions 118062306a36Sopenharmony_ci * based on @tclass, and call the security server on a cache miss to obtain 118162306a36Sopenharmony_ci * a new decision and add it to the cache. Audit the granting or denial of 118262306a36Sopenharmony_ci * permissions in accordance with the policy. Return %0 if all @requested 118362306a36Sopenharmony_ci * permissions are granted, -%EACCES if any permissions are denied, or 118462306a36Sopenharmony_ci * another -errno upon other errors. 118562306a36Sopenharmony_ci */ 118662306a36Sopenharmony_ciint avc_has_perm(u32 ssid, u32 tsid, u16 tclass, 118762306a36Sopenharmony_ci u32 requested, struct common_audit_data *auditdata) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci struct av_decision avd; 119062306a36Sopenharmony_ci int rc, rc2; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, 119362306a36Sopenharmony_ci &avd); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, 119662306a36Sopenharmony_ci auditdata); 119762306a36Sopenharmony_ci if (rc2) 119862306a36Sopenharmony_ci return rc2; 119962306a36Sopenharmony_ci return rc; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ciu32 avc_policy_seqno(void) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci return selinux_avc.avc_cache.latest_notif; 120562306a36Sopenharmony_ci} 1206