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(&selinux_state, subnet_prefix, pkey_num,
 156                                   sid);
 157        if (ret)
 158                goto out;
 159
 160        /* If this memory allocation fails still return 0. The SID
 161         * is valid, it just won't be added to the cache.
 162         */
 163        new = kzalloc(sizeof(*new), GFP_ATOMIC);
 164        if (!new)
 165                goto out;
 166
 167        new->psec.subnet_prefix = subnet_prefix;
 168        new->psec.pkey = pkey_num;
 169        new->psec.sid = *sid;
 170        sel_ib_pkey_insert(new);
 171
 172out:
 173        spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
 174        return ret;
 175}
 176
 177/**
 178 * sel_ib_pkey_sid - Lookup the SID of a PKEY
 179 * @subnet_prefix: subnet_prefix
 180 * @pkey_num: pkey number
 181 * @sid: pkey SID
 182 *
 183 * Description:
 184 * This function determines the SID of a PKEY using the fastest method
 185 * possible.  First the pkey table is queried, but if an entry can't be found
 186 * then the policy is queried and the result is added to the table to speedup
 187 * future queries.  Returns zero on success, negative values on failure.
 188 *
 189 */
 190int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid)
 191{
 192        struct sel_ib_pkey *pkey;
 193
 194        rcu_read_lock();
 195        pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
 196        if (pkey) {
 197                *sid = pkey->psec.sid;
 198                rcu_read_unlock();
 199                return 0;
 200        }
 201        rcu_read_unlock();
 202
 203        return sel_ib_pkey_sid_slow(subnet_prefix, pkey_num, sid);
 204}
 205
 206/**
 207 * sel_ib_pkey_flush - Flush the entire pkey table
 208 *
 209 * Description:
 210 * Remove all entries from the pkey table
 211 *
 212 */
 213void sel_ib_pkey_flush(void)
 214{
 215        unsigned int idx;
 216        struct sel_ib_pkey *pkey, *pkey_tmp;
 217        unsigned long flags;
 218
 219        spin_lock_irqsave(&sel_ib_pkey_lock, flags);
 220        for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {
 221                list_for_each_entry_safe(pkey, pkey_tmp,
 222                                         &sel_ib_pkey_hash[idx].list, list) {
 223                        list_del_rcu(&pkey->list);
 224                        kfree_rcu(pkey, rcu);
 225                }
 226                sel_ib_pkey_hash[idx].size = 0;
 227        }
 228        spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
 229}
 230
 231static __init int sel_ib_pkey_init(void)
 232{
 233        int iter;
 234
 235        if (!selinux_enabled)
 236                return 0;
 237
 238        for (iter = 0; iter < SEL_PKEY_HASH_SIZE; iter++) {
 239                INIT_LIST_HEAD(&sel_ib_pkey_hash[iter].list);
 240                sel_ib_pkey_hash[iter].size = 0;
 241        }
 242
 243        return 0;
 244}
 245
 246subsys_initcall(sel_ib_pkey_init);
 247