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