Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selinux: Add a cache for quicker retreival of PKey SIDs

It is likely that the SID for the same PKey will be requested many
times. To reduce the time to modify QPs and process MADs use a cache to
store PKey SIDs.

This code is heavily based on the "netif" and "netport" concept
originally developed by James Morris <jmorris@redhat.com> and Paul Moore
<paul@paul-moore.com> (see security/selinux/netif.c and
security/selinux/netport.c for more information)

Signed-off-by: Daniel Jurgens <danielj@mellanox.com>
Acked-by: Doug Ledford <dledford@redhat.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Daniel Jurgens and committed by
Paul Moore
409dcf31 ab861dfc

+288 -3
+1 -1
security/selinux/Makefile
··· 5 5 obj-$(CONFIG_SECURITY_SELINUX) := selinux.o 6 6 7 7 selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \ 8 - netnode.o netport.o exports.o \ 8 + netnode.o netport.o ibpkey.o exports.o \ 9 9 ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \ 10 10 ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o 11 11
+5 -2
security/selinux/hooks.c
··· 91 91 #include "netif.h" 92 92 #include "netnode.h" 93 93 #include "netport.h" 94 + #include "ibpkey.h" 94 95 #include "xfrm.h" 95 96 #include "netlabel.h" 96 97 #include "audit.h" ··· 175 174 176 175 static int selinux_lsm_notifier_avc_callback(u32 event) 177 176 { 178 - if (event == AVC_CALLBACK_RESET) 177 + if (event == AVC_CALLBACK_RESET) { 178 + sel_ib_pkey_flush(); 179 179 call_lsm_notifier(LSM_POLICY_CHANGE, NULL); 180 + } 180 181 181 182 return 0; 182 183 } ··· 6159 6156 struct ib_security_struct *sec = ib_sec; 6160 6157 struct lsm_ibpkey_audit ibpkey; 6161 6158 6162 - err = security_ib_pkey_sid(subnet_prefix, pkey_val, &sid); 6159 + err = sel_ib_pkey_sid(subnet_prefix, pkey_val, &sid); 6163 6160 if (err) 6164 6161 return err; 6165 6162
+245
security/selinux/ibpkey.c
··· 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);
+31
security/selinux/include/ibpkey.h
··· 1 + /* 2 + * pkey table 3 + * 4 + * SELinux must keep a mapping of 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 + */ 9 + 10 + /* 11 + * (c) Mellanox Technologies, 2016 12 + * 13 + * This program is free software: you can redistribute it and/or modify 14 + * it under the terms of version 2 of the GNU General Public License as 15 + * published by the Free Software Foundation. 16 + * 17 + * This program is distributed in the hope that it will be useful, 18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 + * GNU General Public License for more details. 21 + * 22 + */ 23 + 24 + #ifndef _SELINUX_IB_PKEY_H 25 + #define _SELINUX_IB_PKEY_H 26 + 27 + void sel_ib_pkey_flush(void); 28 + 29 + int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey, u32 *sid); 30 + 31 + #endif
+6
security/selinux/include/objsec.h
··· 144 144 u32 sid; /* SID of the queue pair or MAD agent */ 145 145 }; 146 146 147 + struct pkey_security_struct { 148 + u64 subnet_prefix; /* Port subnet prefix */ 149 + u16 pkey; /* PKey number */ 150 + u32 sid; /* SID of pkey */ 151 + }; 152 + 147 153 extern unsigned int selinux_checkreqprot; 148 154 149 155 #endif /* _SELINUX_OBJSEC_H_ */