Root/
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@paul-moore.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/slab.h> |
35 | #include <linux/spinlock.h> |
36 | #include <linux/in.h> |
37 | #include <linux/in6.h> |
38 | #include <linux/ip.h> |
39 | #include <linux/ipv6.h> |
40 | #include <net/ip.h> |
41 | #include <net/ipv6.h> |
42 | |
43 | #include "netnode.h" |
44 | #include "objsec.h" |
45 | |
46 | #define SEL_NETNODE_HASH_SIZE 256 |
47 | #define SEL_NETNODE_HASH_BKT_LIMIT 16 |
48 | |
49 | struct sel_netnode_bkt { |
50 | unsigned int size; |
51 | struct list_head list; |
52 | }; |
53 | |
54 | struct sel_netnode { |
55 | struct netnode_security_struct nsec; |
56 | |
57 | struct list_head list; |
58 | struct rcu_head rcu; |
59 | }; |
60 | |
61 | /* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason |
62 | * for this is that I suspect most users will not make heavy use of both |
63 | * address families at the same time so one table will usually end up wasted, |
64 | * if this becomes a problem we can always add a hash table for each address |
65 | * family later */ |
66 | |
67 | static LIST_HEAD(sel_netnode_list); |
68 | static DEFINE_SPINLOCK(sel_netnode_lock); |
69 | static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE]; |
70 | |
71 | /** |
72 | * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table |
73 | * @addr: IPv4 address |
74 | * |
75 | * Description: |
76 | * This is the IPv4 hashing function for the node interface table, it returns |
77 | * the bucket number for the given IP address. |
78 | * |
79 | */ |
80 | static unsigned int sel_netnode_hashfn_ipv4(__be32 addr) |
81 | { |
82 | /* at some point we should determine if the mismatch in byte order |
83 | * affects the hash function dramatically */ |
84 | return (addr & (SEL_NETNODE_HASH_SIZE - 1)); |
85 | } |
86 | |
87 | /** |
88 | * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table |
89 | * @addr: IPv6 address |
90 | * |
91 | * Description: |
92 | * This is the IPv6 hashing function for the node interface table, it returns |
93 | * the bucket number for the given IP address. |
94 | * |
95 | */ |
96 | static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr) |
97 | { |
98 | /* just hash the least significant 32 bits to keep things fast (they |
99 | * are the most likely to be different anyway), we can revisit this |
100 | * later if needed */ |
101 | return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1)); |
102 | } |
103 | |
104 | /** |
105 | * sel_netnode_find - Search for a node record |
106 | * @addr: IP address |
107 | * @family: address family |
108 | * |
109 | * Description: |
110 | * Search the network node table and return the record matching @addr. If an |
111 | * entry can not be found in the table return NULL. |
112 | * |
113 | */ |
114 | static struct sel_netnode *sel_netnode_find(const void *addr, u16 family) |
115 | { |
116 | unsigned int idx; |
117 | struct sel_netnode *node; |
118 | |
119 | switch (family) { |
120 | case PF_INET: |
121 | idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr); |
122 | break; |
123 | case PF_INET6: |
124 | idx = sel_netnode_hashfn_ipv6(addr); |
125 | break; |
126 | default: |
127 | BUG(); |
128 | return NULL; |
129 | } |
130 | |
131 | list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list) |
132 | if (node->nsec.family == family) |
133 | switch (family) { |
134 | case PF_INET: |
135 | if (node->nsec.addr.ipv4 == *(__be32 *)addr) |
136 | return node; |
137 | break; |
138 | case PF_INET6: |
139 | if (ipv6_addr_equal(&node->nsec.addr.ipv6, |
140 | addr)) |
141 | return node; |
142 | break; |
143 | } |
144 | |
145 | return NULL; |
146 | } |
147 | |
148 | /** |
149 | * sel_netnode_insert - Insert a new node into the table |
150 | * @node: the new node record |
151 | * |
152 | * Description: |
153 | * Add a new node record to the network address hash table. |
154 | * |
155 | */ |
156 | static void sel_netnode_insert(struct sel_netnode *node) |
157 | { |
158 | unsigned int idx; |
159 | |
160 | switch (node->nsec.family) { |
161 | case PF_INET: |
162 | idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4); |
163 | break; |
164 | case PF_INET6: |
165 | idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6); |
166 | break; |
167 | default: |
168 | BUG(); |
169 | return; |
170 | } |
171 | |
172 | /* we need to impose a limit on the growth of the hash table so check |
173 | * this bucket to make sure it is within the specified bounds */ |
174 | list_add_rcu(&node->list, &sel_netnode_hash[idx].list); |
175 | if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) { |
176 | struct sel_netnode *tail; |
177 | tail = list_entry( |
178 | rcu_dereference_protected(sel_netnode_hash[idx].list.prev, |
179 | lockdep_is_held(&sel_netnode_lock)), |
180 | struct sel_netnode, list); |
181 | list_del_rcu(&tail->list); |
182 | kfree_rcu(tail, rcu); |
183 | } else |
184 | sel_netnode_hash[idx].size++; |
185 | } |
186 | |
187 | /** |
188 | * sel_netnode_sid_slow - Lookup the SID of a network address using the policy |
189 | * @addr: the IP address |
190 | * @family: the address family |
191 | * @sid: node SID |
192 | * |
193 | * Description: |
194 | * This function determines the SID of a network address by quering the |
195 | * security policy. The result is added to the network address table to |
196 | * speedup future queries. Returns zero on success, negative values on |
197 | * failure. |
198 | * |
199 | */ |
200 | static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid) |
201 | { |
202 | int ret = -ENOMEM; |
203 | struct sel_netnode *node; |
204 | struct sel_netnode *new = NULL; |
205 | |
206 | spin_lock_bh(&sel_netnode_lock); |
207 | node = sel_netnode_find(addr, family); |
208 | if (node != NULL) { |
209 | *sid = node->nsec.sid; |
210 | spin_unlock_bh(&sel_netnode_lock); |
211 | return 0; |
212 | } |
213 | new = kzalloc(sizeof(*new), GFP_ATOMIC); |
214 | if (new == NULL) |
215 | goto out; |
216 | switch (family) { |
217 | case PF_INET: |
218 | ret = security_node_sid(PF_INET, |
219 | addr, sizeof(struct in_addr), sid); |
220 | new->nsec.addr.ipv4 = *(__be32 *)addr; |
221 | break; |
222 | case PF_INET6: |
223 | ret = security_node_sid(PF_INET6, |
224 | addr, sizeof(struct in6_addr), sid); |
225 | new->nsec.addr.ipv6 = *(struct in6_addr *)addr; |
226 | break; |
227 | default: |
228 | BUG(); |
229 | ret = -EINVAL; |
230 | } |
231 | if (ret != 0) |
232 | goto out; |
233 | |
234 | new->nsec.family = family; |
235 | new->nsec.sid = *sid; |
236 | sel_netnode_insert(new); |
237 | |
238 | out: |
239 | spin_unlock_bh(&sel_netnode_lock); |
240 | if (unlikely(ret)) { |
241 | printk(KERN_WARNING |
242 | "SELinux: failure in sel_netnode_sid_slow()," |
243 | " unable to determine network node label\n"); |
244 | kfree(new); |
245 | } |
246 | return ret; |
247 | } |
248 | |
249 | /** |
250 | * sel_netnode_sid - Lookup the SID of a network address |
251 | * @addr: the IP address |
252 | * @family: the address family |
253 | * @sid: node SID |
254 | * |
255 | * Description: |
256 | * This function determines the SID of a network address using the fastest |
257 | * method possible. First the address table is queried, but if an entry |
258 | * can't be found then the policy is queried and the result is added to the |
259 | * table to speedup future queries. Returns zero on success, negative values |
260 | * on failure. |
261 | * |
262 | */ |
263 | int sel_netnode_sid(void *addr, u16 family, u32 *sid) |
264 | { |
265 | struct sel_netnode *node; |
266 | |
267 | rcu_read_lock(); |
268 | node = sel_netnode_find(addr, family); |
269 | if (node != NULL) { |
270 | *sid = node->nsec.sid; |
271 | rcu_read_unlock(); |
272 | return 0; |
273 | } |
274 | rcu_read_unlock(); |
275 | |
276 | return sel_netnode_sid_slow(addr, family, sid); |
277 | } |
278 | |
279 | /** |
280 | * sel_netnode_flush - Flush the entire network address table |
281 | * |
282 | * Description: |
283 | * Remove all entries from the network address table. |
284 | * |
285 | */ |
286 | static void sel_netnode_flush(void) |
287 | { |
288 | unsigned int idx; |
289 | struct sel_netnode *node, *node_tmp; |
290 | |
291 | spin_lock_bh(&sel_netnode_lock); |
292 | for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) { |
293 | list_for_each_entry_safe(node, node_tmp, |
294 | &sel_netnode_hash[idx].list, list) { |
295 | list_del_rcu(&node->list); |
296 | kfree_rcu(node, rcu); |
297 | } |
298 | sel_netnode_hash[idx].size = 0; |
299 | } |
300 | spin_unlock_bh(&sel_netnode_lock); |
301 | } |
302 | |
303 | static int sel_netnode_avc_callback(u32 event) |
304 | { |
305 | if (event == AVC_CALLBACK_RESET) { |
306 | sel_netnode_flush(); |
307 | synchronize_net(); |
308 | } |
309 | return 0; |
310 | } |
311 | |
312 | static __init int sel_netnode_init(void) |
313 | { |
314 | int iter; |
315 | int ret; |
316 | |
317 | if (!selinux_enabled) |
318 | return 0; |
319 | |
320 | for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) { |
321 | INIT_LIST_HEAD(&sel_netnode_hash[iter].list); |
322 | sel_netnode_hash[iter].size = 0; |
323 | } |
324 | |
325 | ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET); |
326 | if (ret != 0) |
327 | panic("avc_add_callback() failed, error %d\n", ret); |
328 | |
329 | return ret; |
330 | } |
331 | |
332 | __initcall(sel_netnode_init); |
333 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9