Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * Network node table
4 : : *
5 : : * SELinux must keep a mapping of network nodes to labels/SIDs. This
6 : : * mapping is maintained as part of the normal policy but a fast cache is
7 : : * needed to reduce the lookup overhead since most of these queries happen on
8 : : * a per-packet basis.
9 : : *
10 : : * Author: Paul Moore <paul@paul-moore.com>
11 : : *
12 : : * This code is heavily based on the "netif" concept originally developed by
13 : : * James Morris <jmorris@redhat.com>
14 : : * (see security/selinux/netif.c for more information)
15 : : */
16 : :
17 : : /*
18 : : * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
19 : : */
20 : :
21 : : #include <linux/types.h>
22 : : #include <linux/rcupdate.h>
23 : : #include <linux/list.h>
24 : : #include <linux/slab.h>
25 : : #include <linux/spinlock.h>
26 : : #include <linux/in.h>
27 : : #include <linux/in6.h>
28 : : #include <linux/ip.h>
29 : : #include <linux/ipv6.h>
30 : : #include <net/ip.h>
31 : : #include <net/ipv6.h>
32 : :
33 : : #include "netnode.h"
34 : : #include "objsec.h"
35 : :
36 : : #define SEL_NETNODE_HASH_SIZE 256
37 : : #define SEL_NETNODE_HASH_BKT_LIMIT 16
38 : :
39 : : struct sel_netnode_bkt {
40 : : unsigned int size;
41 : : struct list_head list;
42 : : };
43 : :
44 : : struct sel_netnode {
45 : : struct netnode_security_struct nsec;
46 : :
47 : : struct list_head list;
48 : : struct rcu_head rcu;
49 : : };
50 : :
51 : : /* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
52 : : * for this is that I suspect most users will not make heavy use of both
53 : : * address families at the same time so one table will usually end up wasted,
54 : : * if this becomes a problem we can always add a hash table for each address
55 : : * family later */
56 : :
57 : : static LIST_HEAD(sel_netnode_list);
58 : : static DEFINE_SPINLOCK(sel_netnode_lock);
59 : : static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
60 : :
61 : : /**
62 : : * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
63 : : * @addr: IPv4 address
64 : : *
65 : : * Description:
66 : : * This is the IPv4 hashing function for the node interface table, it returns
67 : : * the bucket number for the given IP address.
68 : : *
69 : : */
70 : 33 : static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)
71 : : {
72 : : /* at some point we should determine if the mismatch in byte order
73 : : * affects the hash function dramatically */
74 : 33 : return (addr & (SEL_NETNODE_HASH_SIZE - 1));
75 : : }
76 : :
77 : : /**
78 : : * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
79 : : * @addr: IPv6 address
80 : : *
81 : : * Description:
82 : : * This is the IPv6 hashing function for the node interface table, it returns
83 : : * the bucket number for the given IP address.
84 : : *
85 : : */
86 : 33 : static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
87 : : {
88 : : /* just hash the least significant 32 bits to keep things fast (they
89 : : * are the most likely to be different anyway), we can revisit this
90 : : * later if needed */
91 : 33 : return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
92 : : }
93 : :
94 : : /**
95 : : * sel_netnode_find - Search for a node record
96 : : * @addr: IP address
97 : : * @family: address family
98 : : *
99 : : * Description:
100 : : * Search the network node table and return the record matching @addr. If an
101 : : * entry can not be found in the table return NULL.
102 : : *
103 : : */
104 : 44 : static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
105 : : {
106 : 44 : unsigned int idx;
107 : 44 : struct sel_netnode *node;
108 : :
109 [ + + - ]: 44 : switch (family) {
110 : 22 : case PF_INET:
111 : 22 : idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr);
112 : 22 : break;
113 : 22 : case PF_INET6:
114 : 22 : idx = sel_netnode_hashfn_ipv6(addr);
115 : 22 : break;
116 : 0 : default:
117 : 0 : BUG();
118 : : return NULL;
119 : : }
120 : :
121 [ + + ]: 66 : list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
122 [ - + ]: 22 : if (node->nsec.family == family)
123 [ # # # ]: 0 : switch (family) {
124 : 0 : case PF_INET:
125 [ # # ]: 0 : if (node->nsec.addr.ipv4 == *(__be32 *)addr)
126 : 0 : return node;
127 : : break;
128 : 0 : case PF_INET6:
129 [ # # ]: 0 : if (ipv6_addr_equal(&node->nsec.addr.ipv6,
130 : : addr))
131 : 0 : return node;
132 : : break;
133 : : }
134 : :
135 : 22 : return NULL;
136 : : }
137 : :
138 : : /**
139 : : * sel_netnode_insert - Insert a new node into the table
140 : : * @node: the new node record
141 : : *
142 : : * Description:
143 : : * Add a new node record to the network address hash table.
144 : : *
145 : : */
146 : 22 : static void sel_netnode_insert(struct sel_netnode *node)
147 : : {
148 : 22 : unsigned int idx;
149 : :
150 [ + + - ]: 22 : switch (node->nsec.family) {
151 : 11 : case PF_INET:
152 : 11 : idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
153 : 11 : break;
154 : 11 : case PF_INET6:
155 : 11 : idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
156 : 11 : break;
157 : 0 : default:
158 : 0 : BUG();
159 : 22 : return;
160 : : }
161 : :
162 : : /* we need to impose a limit on the growth of the hash table so check
163 : : * this bucket to make sure it is within the specified bounds */
164 : 22 : list_add_rcu(&node->list, &sel_netnode_hash[idx].list);
165 [ - + ]: 22 : if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {
166 : 0 : struct sel_netnode *tail;
167 : 0 : tail = list_entry(
168 : : rcu_dereference_protected(sel_netnode_hash[idx].list.prev,
169 : : lockdep_is_held(&sel_netnode_lock)),
170 : : struct sel_netnode, list);
171 [ # # ]: 0 : list_del_rcu(&tail->list);
172 [ # # ]: 0 : kfree_rcu(tail, rcu);
173 : : } else
174 : 22 : sel_netnode_hash[idx].size++;
175 : 22 : }
176 : :
177 : : /**
178 : : * sel_netnode_sid_slow - Lookup the SID of a network address using the policy
179 : : * @addr: the IP address
180 : : * @family: the address family
181 : : * @sid: node SID
182 : : *
183 : : * Description:
184 : : * This function determines the SID of a network address by quering the
185 : : * security policy. The result is added to the network address table to
186 : : * speedup future queries. Returns zero on success, negative values on
187 : : * failure.
188 : : *
189 : : */
190 : 22 : static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
191 : : {
192 : 22 : int ret;
193 : 22 : struct sel_netnode *node;
194 : 22 : struct sel_netnode *new;
195 : :
196 : 22 : spin_lock_bh(&sel_netnode_lock);
197 : 22 : node = sel_netnode_find(addr, family);
198 [ - + ]: 22 : if (node != NULL) {
199 : 0 : *sid = node->nsec.sid;
200 : 0 : spin_unlock_bh(&sel_netnode_lock);
201 : 0 : return 0;
202 : : }
203 : :
204 : 22 : new = kzalloc(sizeof(*new), GFP_ATOMIC);
205 [ + + - ]: 22 : switch (family) {
206 : 11 : case PF_INET:
207 : 11 : ret = security_node_sid(&selinux_state, PF_INET,
208 : : addr, sizeof(struct in_addr), sid);
209 [ + - ]: 11 : if (new)
210 : 11 : new->nsec.addr.ipv4 = *(__be32 *)addr;
211 : : break;
212 : 11 : case PF_INET6:
213 : 11 : ret = security_node_sid(&selinux_state, PF_INET6,
214 : : addr, sizeof(struct in6_addr), sid);
215 [ + - ]: 11 : if (new)
216 : 11 : new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
217 : : break;
218 : 0 : default:
219 : 0 : BUG();
220 : : ret = -EINVAL;
221 : : }
222 [ + - ]: 22 : if (ret == 0 && new) {
223 : 22 : new->nsec.family = family;
224 : 22 : new->nsec.sid = *sid;
225 : 22 : sel_netnode_insert(new);
226 : : } else
227 : 0 : kfree(new);
228 : :
229 : 22 : spin_unlock_bh(&sel_netnode_lock);
230 [ - + ]: 22 : if (unlikely(ret))
231 : 0 : pr_warn("SELinux: failure in %s(), unable to determine network node label\n",
232 : : __func__);
233 : : return ret;
234 : : }
235 : :
236 : : /**
237 : : * sel_netnode_sid - Lookup the SID of a network address
238 : : * @addr: the IP address
239 : : * @family: the address family
240 : : * @sid: node SID
241 : : *
242 : : * Description:
243 : : * This function determines the SID of a network address using the fastest
244 : : * method possible. First the address table is queried, but if an entry
245 : : * can't be found then the policy is queried and the result is added to the
246 : : * table to speedup future queries. Returns zero on success, negative values
247 : : * on failure.
248 : : *
249 : : */
250 : 22 : int sel_netnode_sid(void *addr, u16 family, u32 *sid)
251 : : {
252 : 22 : struct sel_netnode *node;
253 : :
254 : 22 : rcu_read_lock();
255 : 22 : node = sel_netnode_find(addr, family);
256 [ - + ]: 22 : if (node != NULL) {
257 : 0 : *sid = node->nsec.sid;
258 : 0 : rcu_read_unlock();
259 : 0 : return 0;
260 : : }
261 : 22 : rcu_read_unlock();
262 : :
263 : 22 : return sel_netnode_sid_slow(addr, family, sid);
264 : : }
265 : :
266 : : /**
267 : : * sel_netnode_flush - Flush the entire network address table
268 : : *
269 : : * Description:
270 : : * Remove all entries from the network address table.
271 : : *
272 : : */
273 : 0 : void sel_netnode_flush(void)
274 : : {
275 : 0 : unsigned int idx;
276 : 0 : struct sel_netnode *node, *node_tmp;
277 : :
278 : 0 : spin_lock_bh(&sel_netnode_lock);
279 [ # # ]: 0 : for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
280 [ # # ]: 0 : list_for_each_entry_safe(node, node_tmp,
281 : : &sel_netnode_hash[idx].list, list) {
282 [ # # ]: 0 : list_del_rcu(&node->list);
283 [ # # ]: 0 : kfree_rcu(node, rcu);
284 : : }
285 : 0 : sel_netnode_hash[idx].size = 0;
286 : : }
287 : 0 : spin_unlock_bh(&sel_netnode_lock);
288 : 0 : }
289 : :
290 : 11 : static __init int sel_netnode_init(void)
291 : : {
292 : 11 : int iter;
293 : :
294 [ + - ]: 11 : if (!selinux_enabled_boot)
295 : : return 0;
296 : :
297 [ + + ]: 2827 : for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {
298 : 2816 : INIT_LIST_HEAD(&sel_netnode_hash[iter].list);
299 : 2816 : sel_netnode_hash[iter].size = 0;
300 : : }
301 : :
302 : : return 0;
303 : : }
304 : :
305 : : __initcall(sel_netnode_init);
|