linux/security/selinux/ibpkey.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Pkey table
   4 *
   5 * SELinux must keep a mapping of Infinband PKEYs to labels/SIDs.  This
   6 * mapping is maintained as part of the normal policy but a fast cache is
   7 * needed to reduce the lookup overhead.
   8 *
   9 * This code is heavily based on the "netif" and "netport" concept originally
  10 * developed by
  11 * James Morris <jmorris@redhat.com> and
  12 * Paul Moore <paul@paul-moore.com>
  13 *   (see security/selinux/netif.c and security/selinux/netport.c for more
  14 *   information)
  15 */
  16
  17/*
  18 * (c) Mellanox Technologies, 2016
  19 */
  20
  21#include <linux/types.h>
  22#include <linux/rcupdate.h>
  23#include <linux/list.h>
  24#include <linux/spinlock.h>
  25
  26#include "ibpkey.h"
  27#include "objsec.h"
  28
  29#define SEL_PKEY_HASH_SIZE       256
  30#define SEL_PKEY_HASH_BKT_LIMIT   16
  31
  32struct sel_ib_pkey_bkt {
  33        int size;
  34        struct list_head list;
  35};
  36
  37struct sel_ib_pkey {
  38        struct pkey_security_struct psec;
  39        struct list_head list;
  40        struct rcu_head rcu;
  41};
  42
  43static LIST_HEAD(sel_ib_pkey_list);
  44static DEFINE_SPINLOCK(sel_ib_pkey_lock);
  45static struct sel_ib_pkey_bkt sel_ib_pkey_hash[SEL_PKEY_HASH_SIZE];
  46
  47/**
  48 * sel_ib_pkey_hashfn - Hashing function for the pkey table
  49 * @pkey: pkey number
  50 *
  51 * Description:
  52 * This is the hashing function for the pkey table, it returns the bucket
  53 * number for the given pkey.
  54 *
  55 */
  56static unsigned int sel_ib_pkey_hashfn(u16 pkey)
  57{
  58        return (pkey & (SEL_PKEY_HASH_SIZE - 1));
  59}
  60
  61/**
  62 * sel_ib_pkey_find - Search for a pkey record
  63 * @subnet_prefix: subnet_prefix
  64 * @pkey_num: pkey_num
  65 *
  66 * Description:
  67 * Search the pkey table and return the matching record.  If an entry
  68 * can not be found in the table return NULL.
  69 *
  70 */
  71static struct sel_ib_pkey *sel_ib_pkey_find(u64 subnet_prefix, u16 pkey_num)
  72{
  73        unsigned int idx;
  74        struct sel_ib_pkey *pkey;
  75
  76        idx = sel_ib_pkey_hashfn(pkey_num);
  77        list_for_each_entry_rcu(pkey, &sel_ib_pkey_hash[idx].list, list) {
  78                if (pkey->psec.pkey == pkey_num &&
  79                    pkey->psec.subnet_prefix == subnet_prefix)
  80                        return pkey;
  81        }
  82
  83        return NULL;
  84}
  85
  86/**
  87 * sel_ib_pkey_insert - Insert a new pkey into the table
  88 * @pkey: the new pkey record
  89 *
  90 * Description:
  91 * Add a new pkey record to the hash table.
  92 *
  93 */
  94static void sel_ib_pkey_insert(struct sel_ib_pkey *pkey)
  95{
  96        unsigned int idx;
  97
  98        /* we need to impose a limit on the growth of the hash table so check
  99         * this bucket to make sure it is within the specified bounds
 100         */
 101        idx = sel_ib_pkey_hashfn(pkey->psec.pkey);
 102        list_add_rcu(&pkey->list, &sel_ib_pkey_hash[idx].list);
 103        if (sel_ib_pkey_hash[idx].size == SEL_PKEY_HASH_BKT_LIMIT) {
 104                struct sel_ib_pkey *tail;
 105
 106                tail = list_entry(
 107                        rcu_dereference_protected(
 108                                sel_ib_pkey_hash[idx].list.prev,
 109                                lockdep_is_held(&sel_ib_pkey_lock)),
 110                        struct sel_ib_pkey, list);
 111                list_del_rcu(&tail->list);
 112                kfree_rcu(tail, rcu);
 113        } else {
 114                sel_ib_pkey_hash[idx].size++;
 115        }
 116}
 117
 118/**
 119 * sel_ib_pkey_sid_slow - Lookup the SID of a pkey using the policy
 120 * @subnet_prefix: subnet prefix
 121 * @pkey_num: pkey number
 122 * @sid: pkey SID
 123 *
 124 * Description:
 125 * This function determines the SID of a pkey by querying the security
 126 * policy.  The result is added to the pkey table to speedup future
 127 * queries.  Returns zero on success, negative values on failure.
 128 *
 129 */
 130static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
 131{
 132        int ret;
 133        struct sel_ib_pkey *pkey;
 134        struct sel_ib_pkey *new = NULL;
 135        unsigned long flags;
 136
 137        spin_lock_irqsave(&sel_ib_pkey_lock, flags);
 138        pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
 139        if (pkey) {
 140                *sid = pkey->psec.sid;
 141                spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
 142                return 0;
 143        }
 144
 145        ret = security_ib_pkey_sid(&selinux_state, subnet_prefix, pkey_num,
 146                                   sid);
 147        if (ret)
 148                goto out;
 149
 150        /* If this memory allocation fails still return 0. The SID
 151         * is valid, it just won't be added to the cache.
 152         */
 153        new = kzalloc(sizeof(*new), GFP_ATOMIC);
 154        if (!new)
 155                goto out;
 156
 157        new->psec.subnet_prefix = subnet_prefix;
 158        new->psec.pkey = pkey_num;
 159        new->psec.sid = *sid;
 160        sel_ib_pkey_insert(new);
 161
 162out:
 163        spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
 164        return ret;
 165}
 166
 167/**
 168 * sel_ib_pkey_sid - Lookup the SID of a PKEY
 169 * @subnet_prefix: subnet_prefix
 170 * @pkey_num: pkey number
 171 * @sid: pkey SID
 172 *
 173 * Description:
 174 * This function determines the SID of a PKEY using the fastest method
 175 * possible.  First the pkey table is queried, but if an entry can't be found
 176 * then the policy is queried and the result is added to the table to speedup
 177 * 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{
 182        struct sel_ib_pkey *pkey;
 183
 184        rcu_read_lock();
 185        pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
 186        if (pkey) {
 187                *sid = pkey->psec.sid;
 188                rcu_read_unlock();
 189                return 0;
 190        }
 191        rcu_read_unlock();
 192
 193        return sel_ib_pkey_sid_slow(subnet_prefix, pkey_num, sid);
 194}
 195
 196/**
 197 * sel_ib_pkey_flush - Flush the entire pkey table
 198 *
 199 * Description:
 200 * Remove all entries from the pkey table
 201 *
 202 */
 203void sel_ib_pkey_flush(void)
 204{
 205        unsigned int idx;
 206        struct sel_ib_pkey *pkey, *pkey_tmp;
 207        unsigned long flags;
 208
 209        spin_lock_irqsave(&sel_ib_pkey_lock, flags);
 210        for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {
 211                list_for_each_entry_safe(pkey, pkey_tmp,
 212                                         &sel_ib_pkey_hash[idx].list, list) {
 213                        list_del_rcu(&pkey->list);
 214                        kfree_rcu(pkey, rcu);
 215                }
 216                sel_ib_pkey_hash[idx].size = 0;
 217        }
 218        spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
 219}
 220
 221static __init int sel_ib_pkey_init(void)
 222{
 223        int iter;
 224
 225        if (!selinux_enabled)
 226                return 0;
 227
 228        for (iter = 0; iter < SEL_PKEY_HASH_SIZE; iter++) {
 229                INIT_LIST_HEAD(&sel_ib_pkey_hash[iter].list);
 230                sel_ib_pkey_hash[iter].size = 0;
 231        }
 232
 233        return 0;
 234}
 235
 236subsys_initcall(sel_ib_pkey_init);
 237