// SPDX-License-Identifier: GPL-2.0-only1/*2* Pkey table3*4* SELinux must keep a mapping of Infinband PKEYs to labels/SIDs. This5* mapping is maintained as part of the normal policy but a fast cache is6* needed to reduce the lookup overhead.7*8* This code is heavily based on the "netif" and "netport" concept originally9* developed by10* James Morris <[email protected]> and11* Paul Moore <[email protected]>12* (see security/selinux/netif.c and security/selinux/netport.c for more13* information)14*/1516/*17* (c) Mellanox Technologies, 201618*/1920#include <linux/types.h>21#include <linux/rcupdate.h>22#include <linux/list.h>23#include <linux/spinlock.h>2425#include "ibpkey.h"26#include "objsec.h"2728#define SEL_PKEY_HASH_SIZE 25629#define SEL_PKEY_HASH_BKT_LIMIT 163031struct sel_ib_pkey_bkt {32int size;33struct list_head list;34};3536struct sel_ib_pkey {37struct pkey_security_struct psec;38struct list_head list;39struct rcu_head rcu;40};4142static DEFINE_SPINLOCK(sel_ib_pkey_lock);43static struct sel_ib_pkey_bkt sel_ib_pkey_hash[SEL_PKEY_HASH_SIZE];4445/**46* sel_ib_pkey_hashfn - Hashing function for the pkey table47* @pkey: pkey number48*49* Description:50* This is the hashing function for the pkey table, it returns the bucket51* number for the given pkey.52*53*/54static unsigned int sel_ib_pkey_hashfn(u16 pkey)55{56return (pkey & (SEL_PKEY_HASH_SIZE - 1));57}5859/**60* sel_ib_pkey_find - Search for a pkey record61* @subnet_prefix: subnet_prefix62* @pkey_num: pkey_num63*64* Description:65* Search the pkey table and return the matching record. If an entry66* can not be found in the table return NULL.67*68*/69static struct sel_ib_pkey *sel_ib_pkey_find(u64 subnet_prefix, u16 pkey_num)70{71unsigned int idx;72struct sel_ib_pkey *pkey;7374idx = sel_ib_pkey_hashfn(pkey_num);75list_for_each_entry_rcu(pkey, &sel_ib_pkey_hash[idx].list, list) {76if (pkey->psec.pkey == pkey_num &&77pkey->psec.subnet_prefix == subnet_prefix)78return pkey;79}8081return NULL;82}8384/**85* sel_ib_pkey_insert - Insert a new pkey into the table86* @pkey: the new pkey record87*88* Description:89* Add a new pkey record to the hash table.90*91*/92static void sel_ib_pkey_insert(struct sel_ib_pkey *pkey)93{94unsigned int idx;9596/* we need to impose a limit on the growth of the hash table so check97* this bucket to make sure it is within the specified bounds98*/99idx = sel_ib_pkey_hashfn(pkey->psec.pkey);100list_add_rcu(&pkey->list, &sel_ib_pkey_hash[idx].list);101if (sel_ib_pkey_hash[idx].size == SEL_PKEY_HASH_BKT_LIMIT) {102struct sel_ib_pkey *tail;103104tail = list_entry(105rcu_dereference_protected(106list_tail_rcu(&sel_ib_pkey_hash[idx].list),107lockdep_is_held(&sel_ib_pkey_lock)),108struct sel_ib_pkey, list);109list_del_rcu(&tail->list);110kfree_rcu(tail, rcu);111} else {112sel_ib_pkey_hash[idx].size++;113}114}115116/**117* sel_ib_pkey_sid_slow - Lookup the SID of a pkey using the policy118* @subnet_prefix: subnet prefix119* @pkey_num: pkey number120* @sid: pkey SID121*122* Description:123* This function determines the SID of a pkey by querying the security124* policy. The result is added to the pkey table to speedup future125* queries. Returns zero on success, negative values on failure.126*127*/128static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)129{130int ret;131struct sel_ib_pkey *pkey;132struct sel_ib_pkey *new;133unsigned long flags;134135spin_lock_irqsave(&sel_ib_pkey_lock, flags);136pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);137if (pkey) {138*sid = pkey->psec.sid;139spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);140return 0;141}142143ret = security_ib_pkey_sid(subnet_prefix, pkey_num,144sid);145if (ret)146goto out;147148new = kmalloc(sizeof(*new), GFP_ATOMIC);149if (!new) {150/* If this memory allocation fails still return 0. The SID151* is valid, it just won't be added to the cache.152*/153goto out;154}155156new->psec.subnet_prefix = subnet_prefix;157new->psec.pkey = pkey_num;158new->psec.sid = *sid;159sel_ib_pkey_insert(new);160161out:162spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);163return ret;164}165166/**167* sel_ib_pkey_sid - Lookup the SID of a PKEY168* @subnet_prefix: subnet_prefix169* @pkey_num: pkey number170* @sid: pkey SID171*172* Description:173* This function determines the SID of a PKEY using the fastest method174* possible. First the pkey table is queried, but if an entry can't be found175* then the policy is queried and the result is added to the table to speedup176* future queries. Returns zero on success, negative values on failure.177*178*/179int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid)180{181struct sel_ib_pkey *pkey;182183rcu_read_lock();184pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);185if (likely(pkey)) {186*sid = pkey->psec.sid;187rcu_read_unlock();188return 0;189}190rcu_read_unlock();191192return sel_ib_pkey_sid_slow(subnet_prefix, pkey_num, sid);193}194195/**196* sel_ib_pkey_flush - Flush the entire pkey table197*198* Description:199* Remove all entries from the pkey table200*201*/202void sel_ib_pkey_flush(void)203{204unsigned int idx;205struct sel_ib_pkey *pkey, *pkey_tmp;206unsigned long flags;207208spin_lock_irqsave(&sel_ib_pkey_lock, flags);209for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {210list_for_each_entry_safe(pkey, pkey_tmp,211&sel_ib_pkey_hash[idx].list, list) {212list_del_rcu(&pkey->list);213kfree_rcu(pkey, rcu);214}215sel_ib_pkey_hash[idx].size = 0;216}217spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);218}219220static __init int sel_ib_pkey_init(void)221{222int iter;223224if (!selinux_enabled_boot)225return 0;226227for (iter = 0; iter < SEL_PKEY_HASH_SIZE; iter++) {228INIT_LIST_HEAD(&sel_ib_pkey_hash[iter].list);229sel_ib_pkey_hash[iter].size = 0;230}231232return 0;233}234235subsys_initcall(sel_ib_pkey_init);236237238