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

SELinux: Add a network node caching mechanism similar to the sel_netif_*() functions

This patch adds a SELinux IP address/node SID caching mechanism similar to the
sel_netif_*() functions. The node SID queries in the SELinux hooks files are
also modified to take advantage of this new functionality. In addition, remove
the address length information from the sk_buff parsing routines as it is
redundant since we already have the address family.

Signed-off-by: Paul Moore <paul.moore@hp.com>
Signed-off-by: James Morris <jmorris@namei.org>

authored by

Paul Moore and committed by
James Morris
224dfbd8 da5645a2

+416 -17
+8 -1
security/selinux/Makefile
··· 4 4 5 5 obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/ 6 6 7 - selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o exports.o 7 + selinux-y := avc.o \ 8 + hooks.o \ 9 + selinuxfs.o \ 10 + netlink.o \ 11 + nlmsgtab.o \ 12 + netif.o \ 13 + netnode.o \ 14 + exports.o 8 15 9 16 selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o 10 17
+17 -16
security/selinux/hooks.c
··· 76 76 #include "avc.h" 77 77 #include "objsec.h" 78 78 #include "netif.h" 79 + #include "netnode.h" 79 80 #include "xfrm.h" 80 81 #include "netlabel.h" 81 82 ··· 3396 3395 #endif /* IPV6 */ 3397 3396 3398 3397 static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, 3399 - char **addrp, int *len, int src, u8 *proto) 3398 + char **addrp, int src, u8 *proto) 3400 3399 { 3401 3400 int ret = 0; 3402 3401 ··· 3405 3404 ret = selinux_parse_skb_ipv4(skb, ad, proto); 3406 3405 if (ret || !addrp) 3407 3406 break; 3408 - *len = 4; 3409 3407 *addrp = (char *)(src ? &ad->u.net.v4info.saddr : 3410 3408 &ad->u.net.v4info.daddr); 3411 3409 break; ··· 3414 3414 ret = selinux_parse_skb_ipv6(skb, ad, proto); 3415 3415 if (ret || !addrp) 3416 3416 break; 3417 - *len = 16; 3418 3417 *addrp = (char *)(src ? &ad->u.net.v6info.saddr : 3419 3418 &ad->u.net.v6info.daddr); 3420 3419 break; ··· 3613 3614 break; 3614 3615 } 3615 3616 3616 - err = security_node_sid(family, addrp, addrlen, &sid); 3617 + err = sel_netnode_sid(addrp, family, &sid); 3617 3618 if (err) 3618 3619 goto out; 3619 3620 ··· 3825 3826 } 3826 3827 3827 3828 static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, 3828 - struct avc_audit_data *ad, u16 family, char *addrp, int len) 3829 + struct avc_audit_data *ad, 3830 + u16 family, char *addrp) 3829 3831 { 3830 3832 int err = 0; 3831 3833 u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; ··· 3886 3886 if (err) 3887 3887 goto out; 3888 3888 3889 - err = security_node_sid(family, addrp, len, &node_sid); 3889 + err = sel_netnode_sid(addrp, family, &node_sid); 3890 3890 if (err) 3891 3891 goto out; 3892 3892 ··· 3915 3915 { 3916 3916 u16 family; 3917 3917 char *addrp; 3918 - int len, err = 0; 3918 + int err = 0; 3919 3919 struct avc_audit_data ad; 3920 3920 struct sk_security_struct *sksec = sk->sk_security; 3921 3921 ··· 3931 3931 ad.u.net.netif = skb->iif; 3932 3932 ad.u.net.family = family; 3933 3933 3934 - err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL); 3934 + err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); 3935 3935 if (err) 3936 3936 goto out; 3937 3937 3938 3938 if (selinux_compat_net) 3939 - err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family, 3940 - addrp, len); 3939 + err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family, addrp); 3941 3940 else 3942 3941 err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, 3943 3942 PACKET__RECV, &ad); ··· 4157 4158 4158 4159 #ifdef CONFIG_NETFILTER 4159 4160 4160 - static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev, 4161 + static int selinux_ip_postroute_last_compat(struct sock *sk, 4162 + struct net_device *dev, 4161 4163 struct avc_audit_data *ad, 4162 - u16 family, char *addrp, int len) 4164 + u16 family, 4165 + char *addrp) 4163 4166 { 4164 4167 int err = 0; 4165 4168 u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0; ··· 4212 4211 if (err) 4213 4212 goto out; 4214 4213 4215 - err = security_node_sid(family, addrp, len, &node_sid); 4214 + err = sel_netnode_sid(addrp, family, &node_sid); 4216 4215 if (err) 4217 4216 goto out; 4218 4217 ··· 4246 4245 u16 family) 4247 4246 { 4248 4247 char *addrp; 4249 - int len, err = 0; 4248 + int err = 0; 4250 4249 struct sock *sk; 4251 4250 struct avc_audit_data ad; 4252 4251 struct net_device *dev = (struct net_device *)out; ··· 4263 4262 ad.u.net.netif = dev->ifindex; 4264 4263 ad.u.net.family = family; 4265 4264 4266 - err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto); 4265 + err = selinux_parse_skb(skb, &ad, &addrp, 0, &proto); 4267 4266 if (err) 4268 4267 goto out; 4269 4268 4270 4269 if (selinux_compat_net) 4271 4270 err = selinux_ip_postroute_last_compat(sk, dev, &ad, 4272 - family, addrp, len); 4271 + family, addrp); 4273 4272 else 4274 4273 err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, 4275 4274 PACKET__SEND, &ad);
+32
security/selinux/include/netnode.h
··· 1 + /* 2 + * Network node table 3 + * 4 + * SELinux must keep a mapping of network nodes 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 since most of these queries happen on 7 + * a per-packet basis. 8 + * 9 + * Author: Paul Moore <paul.moore@hp.com> 10 + * 11 + */ 12 + 13 + /* 14 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 15 + * 16 + * This program is free software: you can redistribute it and/or modify 17 + * it under the terms of version 2 of the GNU General Public License as 18 + * published by the Free Software Foundation. 19 + * 20 + * This program is distributed in the hope that it will be useful, 21 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 + * GNU General Public License for more details. 24 + * 25 + */ 26 + 27 + #ifndef _SELINUX_NETNODE_H 28 + #define _SELINUX_NETNODE_H 29 + 30 + int sel_netnode_sid(void *addr, u16 family, u32 *sid); 31 + 32 + #endif
+9
security/selinux/include/objsec.h
··· 100 100 u32 sid; /* SID for this interface */ 101 101 }; 102 102 103 + struct netnode_security_struct { 104 + union { 105 + __be32 ipv4; /* IPv4 node address */ 106 + struct in6_addr ipv6; /* IPv6 node address */ 107 + } addr; 108 + u32 sid; /* SID for this node */ 109 + u16 family; /* address family */ 110 + }; 111 + 103 112 struct sk_security_struct { 104 113 struct sock *sk; /* back pointer to sk object */ 105 114 u32 sid; /* SID of this object */
+350
security/selinux/netnode.c
··· 1 + /* 2 + * Network node table 3 + * 4 + * SELinux must keep a mapping of network nodes 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 since most of these queries happen on 7 + * a per-packet basis. 8 + * 9 + * Author: Paul Moore <paul.moore@hp.com> 10 + * 11 + * This code is heavily based on the "netif" concept originally developed by 12 + * James Morris <jmorris@redhat.com> 13 + * (see security/selinux/netif.c for more information) 14 + * 15 + */ 16 + 17 + /* 18 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 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 + #include <linux/in.h> 36 + #include <linux/in6.h> 37 + #include <linux/ip.h> 38 + #include <linux/ipv6.h> 39 + #include <net/ip.h> 40 + #include <net/ipv6.h> 41 + #include <asm/bug.h> 42 + 43 + #include "objsec.h" 44 + 45 + #define SEL_NETNODE_HASH_SIZE 256 46 + #define SEL_NETNODE_HASH_BKT_LIMIT 16 47 + 48 + struct sel_netnode { 49 + struct netnode_security_struct nsec; 50 + 51 + struct list_head list; 52 + struct rcu_head rcu; 53 + }; 54 + 55 + /* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason 56 + * for this is that I suspect most users will not make heavy use of both 57 + * address families at the same time so one table will usually end up wasted, 58 + * if this becomes a problem we can always add a hash table for each address 59 + * family later */ 60 + 61 + static LIST_HEAD(sel_netnode_list); 62 + static DEFINE_SPINLOCK(sel_netnode_lock); 63 + static struct list_head sel_netnode_hash[SEL_NETNODE_HASH_SIZE]; 64 + 65 + /** 66 + * sel_netnode_free - Frees a node entry 67 + * @p: the entry's RCU field 68 + * 69 + * Description: 70 + * This function is designed to be used as a callback to the call_rcu() 71 + * function so that memory allocated to a hash table node entry can be 72 + * released safely. 73 + * 74 + */ 75 + static void sel_netnode_free(struct rcu_head *p) 76 + { 77 + struct sel_netnode *node = container_of(p, struct sel_netnode, rcu); 78 + kfree(node); 79 + } 80 + 81 + /** 82 + * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table 83 + * @addr: IPv4 address 84 + * 85 + * Description: 86 + * This is the IPv4 hashing function for the node interface table, it returns 87 + * the bucket number for the given IP address. 88 + * 89 + */ 90 + static u32 sel_netnode_hashfn_ipv4(__be32 addr) 91 + { 92 + /* at some point we should determine if the mismatch in byte order 93 + * affects the hash function dramatically */ 94 + return (addr & (SEL_NETNODE_HASH_SIZE - 1)); 95 + } 96 + 97 + /** 98 + * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table 99 + * @addr: IPv6 address 100 + * 101 + * Description: 102 + * This is the IPv6 hashing function for the node interface table, it returns 103 + * the bucket number for the given IP address. 104 + * 105 + */ 106 + static u32 sel_netnode_hashfn_ipv6(const struct in6_addr *addr) 107 + { 108 + /* just hash the least significant 32 bits to keep things fast (they 109 + * are the most likely to be different anyway), we can revisit this 110 + * later if needed */ 111 + return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1)); 112 + } 113 + 114 + /** 115 + * sel_netnode_find - Search for a node record 116 + * @addr: IP address 117 + * @family: address family 118 + * 119 + * Description: 120 + * Search the network node table and return the record matching @addr. If an 121 + * entry can not be found in the table return NULL. 122 + * 123 + */ 124 + static struct sel_netnode *sel_netnode_find(const void *addr, u16 family) 125 + { 126 + u32 idx; 127 + struct sel_netnode *node; 128 + 129 + switch (family) { 130 + case PF_INET: 131 + idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr); 132 + break; 133 + case PF_INET6: 134 + idx = sel_netnode_hashfn_ipv6(addr); 135 + break; 136 + default: 137 + BUG(); 138 + } 139 + 140 + list_for_each_entry_rcu(node, &sel_netnode_hash[idx], list) 141 + if (node->nsec.family == family) 142 + switch (family) { 143 + case PF_INET: 144 + if (node->nsec.addr.ipv4 == *(__be32 *)addr) 145 + return node; 146 + break; 147 + case PF_INET6: 148 + if (ipv6_addr_equal(&node->nsec.addr.ipv6, 149 + addr)) 150 + return node; 151 + break; 152 + } 153 + 154 + return NULL; 155 + } 156 + 157 + /** 158 + * sel_netnode_insert - Insert a new node into the table 159 + * @node: the new node record 160 + * 161 + * Description: 162 + * Add a new node record to the network address hash table. Returns zero on 163 + * success, negative values on failure. 164 + * 165 + */ 166 + static int sel_netnode_insert(struct sel_netnode *node) 167 + { 168 + u32 idx; 169 + u32 count = 0; 170 + struct sel_netnode *iter; 171 + 172 + switch (node->nsec.family) { 173 + case PF_INET: 174 + idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4); 175 + break; 176 + case PF_INET6: 177 + idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6); 178 + break; 179 + default: 180 + BUG(); 181 + } 182 + list_add_rcu(&node->list, &sel_netnode_hash[idx]); 183 + 184 + /* we need to impose a limit on the growth of the hash table so check 185 + * this bucket to make sure it is within the specified bounds */ 186 + list_for_each_entry(iter, &sel_netnode_hash[idx], list) 187 + if (++count > SEL_NETNODE_HASH_BKT_LIMIT) { 188 + list_del_rcu(&iter->list); 189 + call_rcu(&iter->rcu, sel_netnode_free); 190 + break; 191 + } 192 + 193 + return 0; 194 + } 195 + 196 + /** 197 + * sel_netnode_destroy - Remove a node record from the table 198 + * @node: the existing node record 199 + * 200 + * Description: 201 + * Remove an existing node record from the network address table. 202 + * 203 + */ 204 + static void sel_netnode_destroy(struct sel_netnode *node) 205 + { 206 + list_del_rcu(&node->list); 207 + call_rcu(&node->rcu, sel_netnode_free); 208 + } 209 + 210 + /** 211 + * sel_netnode_sid_slow - Lookup the SID of a network address using the policy 212 + * @addr: the IP address 213 + * @family: the address family 214 + * @sid: node SID 215 + * 216 + * Description: 217 + * This function determines the SID of a network address by quering the 218 + * security policy. The result is added to the network address table to 219 + * speedup future queries. Returns zero on success, negative values on 220 + * failure. 221 + * 222 + */ 223 + static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid) 224 + { 225 + int ret; 226 + struct sel_netnode *node; 227 + struct sel_netnode *new = NULL; 228 + 229 + spin_lock_bh(&sel_netnode_lock); 230 + node = sel_netnode_find(addr, family); 231 + if (node != NULL) { 232 + *sid = node->nsec.sid; 233 + ret = 0; 234 + goto out; 235 + } 236 + new = kzalloc(sizeof(*new), GFP_ATOMIC); 237 + if (new == NULL) { 238 + ret = -ENOMEM; 239 + goto out; 240 + } 241 + switch (family) { 242 + case PF_INET: 243 + ret = security_node_sid(PF_INET, 244 + addr, sizeof(struct in_addr), 245 + &new->nsec.sid); 246 + new->nsec.addr.ipv4 = *(__be32 *)addr; 247 + break; 248 + case PF_INET6: 249 + ret = security_node_sid(PF_INET6, 250 + addr, sizeof(struct in6_addr), 251 + &new->nsec.sid); 252 + ipv6_addr_copy(&new->nsec.addr.ipv6, addr); 253 + break; 254 + default: 255 + BUG(); 256 + } 257 + if (ret != 0) 258 + goto out; 259 + new->nsec.family = family; 260 + ret = sel_netnode_insert(new); 261 + if (ret != 0) 262 + goto out; 263 + *sid = new->nsec.sid; 264 + 265 + out: 266 + spin_unlock_bh(&sel_netnode_lock); 267 + if (ret != 0) 268 + kfree(new); 269 + return ret; 270 + } 271 + 272 + /** 273 + * sel_netnode_sid - Lookup the SID of a network address 274 + * @addr: the IP address 275 + * @family: the address family 276 + * @sid: node SID 277 + * 278 + * Description: 279 + * This function determines the SID of a network address using the fastest 280 + * method possible. First the address table is queried, but if an entry 281 + * can't be found then the policy is queried and the result is added to the 282 + * table to speedup future queries. Returns zero on success, negative values 283 + * on failure. 284 + * 285 + */ 286 + int sel_netnode_sid(void *addr, u16 family, u32 *sid) 287 + { 288 + struct sel_netnode *node; 289 + 290 + rcu_read_lock(); 291 + node = sel_netnode_find(addr, family); 292 + if (node != NULL) { 293 + *sid = node->nsec.sid; 294 + rcu_read_unlock(); 295 + return 0; 296 + } 297 + rcu_read_unlock(); 298 + 299 + return sel_netnode_sid_slow(addr, family, sid); 300 + } 301 + 302 + /** 303 + * sel_netnode_flush - Flush the entire network address table 304 + * 305 + * Description: 306 + * Remove all entries from the network address table. 307 + * 308 + */ 309 + static void sel_netnode_flush(void) 310 + { 311 + u32 idx; 312 + struct sel_netnode *node; 313 + 314 + spin_lock_bh(&sel_netnode_lock); 315 + for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) 316 + list_for_each_entry(node, &sel_netnode_hash[idx], list) 317 + sel_netnode_destroy(node); 318 + spin_unlock_bh(&sel_netnode_lock); 319 + } 320 + 321 + static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid, 322 + u16 class, u32 perms, u32 *retained) 323 + { 324 + if (event == AVC_CALLBACK_RESET) { 325 + sel_netnode_flush(); 326 + synchronize_net(); 327 + } 328 + return 0; 329 + } 330 + 331 + static __init int sel_netnode_init(void) 332 + { 333 + int iter; 334 + int ret; 335 + 336 + if (!selinux_enabled) 337 + return 0; 338 + 339 + for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) 340 + INIT_LIST_HEAD(&sel_netnode_hash[iter]); 341 + 342 + ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET, 343 + SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); 344 + if (ret != 0) 345 + panic("avc_add_callback() failed, error %d\n", ret); 346 + 347 + return ret; 348 + } 349 + 350 + __initcall(sel_netnode_init);