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 42 struct sel_ib_pkey_bkt { 43 int size; 44 struct list_head list; 45 }; 46 47 struct sel_ib_pkey { 48 struct pkey_security_struct psec; 49 struct list_head list; 50 struct rcu_head rcu; 51 }; 52 53 static LIST_HEAD(sel_ib_pkey_list); 54 static DEFINE_SPINLOCK(sel_ib_pkey_lock); 55 static 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 */ 66 static 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 */ 81 static 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 */ 104 static 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 */ 140 static 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 171 out: 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 */ 189 int 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 */ 212 void 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 230 static __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 245 subsys_initcall(sel_ib_pkey_init); 246