SELinux: Add a network node caching mechanism similar to the sel_netif_*() functions
[linux-2.6.git] / 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);