xref: /openbmc/linux/security/selinux/netnode.c (revision a1e58bbd)
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 (unlikely(ret)) {
268 		printk(KERN_WARNING
269 		       "SELinux: failure in sel_netnode_sid_slow(),"
270 		       " unable to determine network node label\n");
271 		kfree(new);
272 	}
273 	return ret;
274 }
275 
276 /**
277  * sel_netnode_sid - Lookup the SID of a network address
278  * @addr: the IP address
279  * @family: the address family
280  * @sid: node SID
281  *
282  * Description:
283  * This function determines the SID of a network address using the fastest
284  * method possible.  First the address table is queried, but if an entry
285  * can't be found then the policy is queried and the result is added to the
286  * table to speedup future queries.  Returns zero on success, negative values
287  * on failure.
288  *
289  */
290 int sel_netnode_sid(void *addr, u16 family, u32 *sid)
291 {
292 	struct sel_netnode *node;
293 
294 	rcu_read_lock();
295 	node = sel_netnode_find(addr, family);
296 	if (node != NULL) {
297 		*sid = node->nsec.sid;
298 		rcu_read_unlock();
299 		return 0;
300 	}
301 	rcu_read_unlock();
302 
303 	return sel_netnode_sid_slow(addr, family, sid);
304 }
305 
306 /**
307  * sel_netnode_flush - Flush the entire network address table
308  *
309  * Description:
310  * Remove all entries from the network address table.
311  *
312  */
313 static void sel_netnode_flush(void)
314 {
315 	u32 idx;
316 	struct sel_netnode *node;
317 
318 	spin_lock_bh(&sel_netnode_lock);
319 	for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++)
320 		list_for_each_entry(node, &sel_netnode_hash[idx], list)
321 			sel_netnode_destroy(node);
322 	spin_unlock_bh(&sel_netnode_lock);
323 }
324 
325 static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid,
326 				    u16 class, u32 perms, u32 *retained)
327 {
328 	if (event == AVC_CALLBACK_RESET) {
329 		sel_netnode_flush();
330 		synchronize_net();
331 	}
332 	return 0;
333 }
334 
335 static __init int sel_netnode_init(void)
336 {
337 	int iter;
338 	int ret;
339 
340 	if (!selinux_enabled)
341 		return 0;
342 
343 	for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++)
344 		INIT_LIST_HEAD(&sel_netnode_hash[iter]);
345 
346 	ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET,
347 	                       SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
348 	if (ret != 0)
349 		panic("avc_add_callback() failed, error %d\n", ret);
350 
351 	return ret;
352 }
353 
354 __initcall(sel_netnode_init);
355