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(&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 172 out: 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 */ 190 int 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 */ 213 void 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 231 static __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 246 subsys_initcall(sel_ib_pkey_init); 247