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 DEFINE_SPINLOCK(sel_ib_pkey_lock);
  44static struct sel_ib_pkey_bkt sel_ib_pkey_hash[SEL_PKEY_HASH_SIZE];
  45
  46/**
  47 * sel_ib_pkey_hashfn - Hashing function for the pkey table
  48 * @pkey: pkey number
  49 *
  50 * Description:
  51 * This is the hashing function for the pkey table, it returns the bucket
  52 * number for the given pkey.
  53 *
  54 */
  55static unsigned int sel_ib_pkey_hashfn(u16 pkey)
  56{
  57        return (pkey & (SEL_PKEY_HASH_SIZE - 1));
  58}
  59
  60/**
  61 * sel_ib_pkey_find - Search for a pkey record
  62 * @subnet_prefix: subnet_prefix
  63 * @pkey_num: pkey_num
  64 *
  65 * Description:
  66 * Search the pkey table and return the matching record.  If an entry
  67 * 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{
  72        unsigned int idx;
  73        struct sel_ib_pkey *pkey;
  74
  75        idx = sel_ib_pkey_hashfn(pkey_num);
  76        list_for_each_entry_rcu(pkey, &sel_ib_pkey_hash[idx].list, list) {
  77                if (pkey->psec.pkey == pkey_num &&
  78                    pkey->psec.subnet_prefix == subnet_prefix)
  79                        return pkey;
  80        }
  81
  82        return NULL;
  83}
  84
  85/**
  86 * sel_ib_pkey_insert - Insert a new pkey into the table
  87 * @pkey: the new pkey record
  88 *
  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{
  95        unsigned int idx;
  96
  97        /* we need to impose a limit on the growth of the hash table so check
  98         * this bucket to make sure it is within the specified bounds
  99         */
 100        idx = sel_ib_pkey_hashfn(pkey->psec.pkey);
 101        list_add_rcu(&pkey->list, &sel_ib_pkey_hash[idx].list);
 102        if (sel_ib_pkey_hash[idx].size == SEL_PKEY_HASH_BKT_LIMIT) {
 103                struct sel_ib_pkey *tail;
 104
 105                tail = list_entry(
 106                        rcu_dereference_protected(
 107                                sel_ib_pkey_hash[idx].list.prev,
 108                                lockdep_is_held(&sel_ib_pkey_lock)),
 109                        struct sel_ib_pkey, list);
 110                list_del_rcu(&tail->list);
 111                kfree_rcu(tail, rcu);
 112        } else {
 113                sel_ib_pkey_hash[idx].size++;
 114        }
 115}
 116
 117/**
 118 * sel_ib_pkey_sid_slow - Lookup the SID of a pkey using the policy
 119 * @subnet_prefix: subnet prefix
 120 * @pkey_num: pkey number
 121 * @sid: pkey SID
 122 *
 123 * Description:
 124 * This function determines the SID of a pkey by querying the security
 125 * policy.  The result is added to the pkey table to speedup future
 126 * 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{
 131        int ret;
 132        struct sel_ib_pkey *pkey;
 133        struct sel_ib_pkey *new = NULL;
 134        unsigned long flags;
 135
 136        spin_lock_irqsave(&sel_ib_pkey_lock, flags);
 137        pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
 138        if (pkey) {
 139                *sid = pkey->psec.sid;
 140                spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
 141                return 0;
 142        }
 143
 144        ret = security_ib_pkey_sid(&selinux_state, subnet_prefix, pkey_num,
 145                                   sid);
 146        if (ret)
 147                goto out;
 148
 149        /* If this memory allocation fails still return 0. The SID
 150         * is valid, it just won't be added to the cache.
 151         */
 152        new = kzalloc(sizeof(*new), GFP_ATOMIC);
 153        if (!new) {
 154                ret = -ENOMEM;
 155                goto out;
 156        }
 157
 158        new->psec.subnet_prefix = subnet_prefix;
 159        new->psec.pkey = pkey_num;
 160        new->psec.sid = *sid;
 161        sel_ib_pkey_insert(new);
 162
 163out:
 164        spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
 165        return ret;
 166}
 167
 168/**
 169 * sel_ib_pkey_sid - Lookup the SID of a PKEY
 170 * @subnet_prefix: subnet_prefix
 171 * @pkey_num: pkey number
 172 * @sid: pkey SID
 173 *
 174 * Description:
 175 * This function determines the SID of a PKEY using the fastest method
 176 * possible.  First the pkey table is queried, but if an entry can't be found
 177 * then the policy is queried and the result is added to the table to speedup
 178 * future queries.  Returns zero on success, negative values on failure.
 179 *
 180 */
 181int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid)
 182{
 183        struct sel_ib_pkey *pkey;
 184
 185        rcu_read_lock();
 186        pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
 187        if (pkey) {
 188                *sid = pkey->psec.sid;
 189                rcu_read_unlock();
 190                return 0;
 191        }
 192        rcu_read_unlock();
 193
 194        return sel_ib_pkey_sid_slow(subnet_prefix, pkey_num, sid);
 195}
 196
 197/**
 198 * sel_ib_pkey_flush - Flush the entire pkey table
 199 *
 200 * Description:
 201 * Remove all entries from the pkey table
 202 *
 203 */
 204void sel_ib_pkey_flush(void)
 205{
 206        unsigned int idx;
 207        struct sel_ib_pkey *pkey, *pkey_tmp;
 208        unsigned long flags;
 209
 210        spin_lock_irqsave(&sel_ib_pkey_lock, flags);
 211        for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {
 212                list_for_each_entry_safe(pkey, pkey_tmp,
 213                                         &sel_ib_pkey_hash[idx].list, list) {
 214                        list_del_rcu(&pkey->list);
 215                        kfree_rcu(pkey, rcu);
 216                }
 217                sel_ib_pkey_hash[idx].size = 0;
 218        }
 219        spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
 220}
 221
 222static __init int sel_ib_pkey_init(void)
 223{
 224        int iter;
 225
 226        if (!selinux_enabled_boot)
 227                return 0;
 228
 229        for (iter = 0; iter < SEL_PKEY_HASH_SIZE; iter++) {
 230                INIT_LIST_HEAD(&sel_ib_pkey_hash[iter].list);
 231                sel_ib_pkey_hash[iter].size = 0;
 232        }
 233
 234        return 0;
 235}
 236
 237subsys_initcall(sel_ib_pkey_init);
 238