// 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 "initcalls.h"26#include "ibpkey.h"27#include "objsec.h"2829#define SEL_PKEY_HASH_SIZE 25630#define SEL_PKEY_HASH_BKT_LIMIT 163132struct sel_ib_pkey_bkt {33int size;34struct list_head list;35};3637struct sel_ib_pkey {38struct pkey_security_struct psec;39struct list_head list;40struct rcu_head rcu;41};4243static DEFINE_SPINLOCK(sel_ib_pkey_lock);44static struct sel_ib_pkey_bkt sel_ib_pkey_hash[SEL_PKEY_HASH_SIZE];4546/**47* sel_ib_pkey_hashfn - Hashing function for the pkey table48* @pkey: pkey number49*50* Description:51* This is the hashing function for the pkey table, it returns the bucket52* number for the given pkey.53*54*/55static unsigned int sel_ib_pkey_hashfn(u16 pkey)56{57return (pkey & (SEL_PKEY_HASH_SIZE - 1));58}5960/**61* sel_ib_pkey_find - Search for a pkey record62* @subnet_prefix: subnet_prefix63* @pkey_num: pkey_num64*65* Description:66* Search the pkey table and return the matching record. If an entry67* can not be found in the table return NULL.68*69*/70static struct sel_ib_pkey *sel_ib_pkey_find(u64 subnet_prefix, u16 pkey_num)71{72unsigned int idx;73struct sel_ib_pkey *pkey;7475idx = sel_ib_pkey_hashfn(pkey_num);76list_for_each_entry_rcu(pkey, &sel_ib_pkey_hash[idx].list, list) {77if (pkey->psec.pkey == pkey_num &&78pkey->psec.subnet_prefix == subnet_prefix)79return pkey;80}8182return NULL;83}8485/**86* sel_ib_pkey_insert - Insert a new pkey into the table87* @pkey: the new pkey record88*89* Description:90* Add a new pkey record to the hash table.91*92*/93static void sel_ib_pkey_insert(struct sel_ib_pkey *pkey)94{95unsigned int idx;9697/* we need to impose a limit on the growth of the hash table so check98* this bucket to make sure it is within the specified bounds99*/100idx = sel_ib_pkey_hashfn(pkey->psec.pkey);101list_add_rcu(&pkey->list, &sel_ib_pkey_hash[idx].list);102if (sel_ib_pkey_hash[idx].size == SEL_PKEY_HASH_BKT_LIMIT) {103struct sel_ib_pkey *tail;104105tail = list_entry(106rcu_dereference_protected(107list_tail_rcu(&sel_ib_pkey_hash[idx].list),108lockdep_is_held(&sel_ib_pkey_lock)),109struct sel_ib_pkey, list);110list_del_rcu(&tail->list);111kfree_rcu(tail, rcu);112} else {113sel_ib_pkey_hash[idx].size++;114}115}116117/**118* sel_ib_pkey_sid_slow - Lookup the SID of a pkey using the policy119* @subnet_prefix: subnet prefix120* @pkey_num: pkey number121* @sid: pkey SID122*123* Description:124* This function determines the SID of a pkey by querying the security125* policy. The result is added to the pkey table to speedup future126* queries. Returns zero on success, negative values on failure.127*128*/129static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)130{131int ret;132struct sel_ib_pkey *pkey;133struct sel_ib_pkey *new;134unsigned long flags;135136spin_lock_irqsave(&sel_ib_pkey_lock, flags);137pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);138if (pkey) {139*sid = pkey->psec.sid;140spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);141return 0;142}143144ret = security_ib_pkey_sid(subnet_prefix, pkey_num,145sid);146if (ret)147goto out;148149new = kmalloc(sizeof(*new), GFP_ATOMIC);150if (!new) {151/* If this memory allocation fails still return 0. The SID152* is valid, it just won't be added to the cache.153*/154goto out;155}156157new->psec.subnet_prefix = subnet_prefix;158new->psec.pkey = pkey_num;159new->psec.sid = *sid;160sel_ib_pkey_insert(new);161162out:163spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);164return ret;165}166167/**168* sel_ib_pkey_sid - Lookup the SID of a PKEY169* @subnet_prefix: subnet_prefix170* @pkey_num: pkey number171* @sid: pkey SID172*173* Description:174* This function determines the SID of a PKEY using the fastest method175* possible. First the pkey table is queried, but if an entry can't be found176* then the policy is queried and the result is added to the table to speedup177* future queries. Returns zero on success, negative values on failure.178*179*/180int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid)181{182struct sel_ib_pkey *pkey;183184rcu_read_lock();185pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);186if (likely(pkey)) {187*sid = pkey->psec.sid;188rcu_read_unlock();189return 0;190}191rcu_read_unlock();192193return sel_ib_pkey_sid_slow(subnet_prefix, pkey_num, sid);194}195196/**197* sel_ib_pkey_flush - Flush the entire pkey table198*199* Description:200* Remove all entries from the pkey table201*202*/203void sel_ib_pkey_flush(void)204{205unsigned int idx;206struct sel_ib_pkey *pkey, *pkey_tmp;207unsigned long flags;208209spin_lock_irqsave(&sel_ib_pkey_lock, flags);210for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {211list_for_each_entry_safe(pkey, pkey_tmp,212&sel_ib_pkey_hash[idx].list, list) {213list_del_rcu(&pkey->list);214kfree_rcu(pkey, rcu);215}216sel_ib_pkey_hash[idx].size = 0;217}218spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);219}220221int __init sel_ib_pkey_init(void)222{223int iter;224225if (!selinux_enabled_boot)226return 0;227228for (iter = 0; iter < SEL_PKEY_HASH_SIZE; iter++) {229INIT_LIST_HEAD(&sel_ib_pkey_hash[iter].list);230sel_ib_pkey_hash[iter].size = 0;231}232233return 0;234}235236237