Root/
1 | /* |
2 | * NetLabel Unlabeled Support |
3 | * |
4 | * This file defines functions for dealing with unlabeled packets for the |
5 | * NetLabel system. The NetLabel system manages static and dynamic label |
6 | * mappings for network protocols such as CIPSO and RIPSO. |
7 | * |
8 | * Author: Paul Moore <paul.moore@hp.com> |
9 | * |
10 | */ |
11 | |
12 | /* |
13 | * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - 2008 |
14 | * |
15 | * This program is free software; you can redistribute it and/or modify |
16 | * it under the terms of the GNU General Public License as published by |
17 | * the Free Software Foundation; either version 2 of the License, or |
18 | * (at your option) any later version. |
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 |
23 | * the GNU General Public License for more details. |
24 | * |
25 | * You should have received a copy of the GNU General Public License |
26 | * along with this program; if not, write to the Free Software |
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
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/socket.h> |
36 | #include <linux/string.h> |
37 | #include <linux/skbuff.h> |
38 | #include <linux/audit.h> |
39 | #include <linux/in.h> |
40 | #include <linux/in6.h> |
41 | #include <linux/ip.h> |
42 | #include <linux/ipv6.h> |
43 | #include <linux/notifier.h> |
44 | #include <linux/netdevice.h> |
45 | #include <linux/security.h> |
46 | #include <linux/slab.h> |
47 | #include <net/sock.h> |
48 | #include <net/netlink.h> |
49 | #include <net/genetlink.h> |
50 | #include <net/ip.h> |
51 | #include <net/ipv6.h> |
52 | #include <net/net_namespace.h> |
53 | #include <net/netlabel.h> |
54 | #include <asm/bug.h> |
55 | #include <asm/atomic.h> |
56 | |
57 | #include "netlabel_user.h" |
58 | #include "netlabel_addrlist.h" |
59 | #include "netlabel_domainhash.h" |
60 | #include "netlabel_unlabeled.h" |
61 | #include "netlabel_mgmt.h" |
62 | |
63 | /* NOTE: at present we always use init's network namespace since we don't |
64 | * presently support different namespaces even though the majority of |
65 | * the functions in this file are "namespace safe" */ |
66 | |
67 | /* The unlabeled connection hash table which we use to map network interfaces |
68 | * and addresses of unlabeled packets to a user specified secid value for the |
69 | * LSM. The hash table is used to lookup the network interface entry |
70 | * (struct netlbl_unlhsh_iface) and then the interface entry is used to |
71 | * lookup an IP address match from an ordered list. If a network interface |
72 | * match can not be found in the hash table then the default entry |
73 | * (netlbl_unlhsh_def) is used. The IP address entry list |
74 | * (struct netlbl_unlhsh_addr) is ordered such that the entries with a |
75 | * larger netmask come first. |
76 | */ |
77 | struct netlbl_unlhsh_tbl { |
78 | struct list_head *tbl; |
79 | u32 size; |
80 | }; |
81 | #define netlbl_unlhsh_addr4_entry(iter) \ |
82 | container_of(iter, struct netlbl_unlhsh_addr4, list) |
83 | struct netlbl_unlhsh_addr4 { |
84 | u32 secid; |
85 | |
86 | struct netlbl_af4list list; |
87 | struct rcu_head rcu; |
88 | }; |
89 | #define netlbl_unlhsh_addr6_entry(iter) \ |
90 | container_of(iter, struct netlbl_unlhsh_addr6, list) |
91 | struct netlbl_unlhsh_addr6 { |
92 | u32 secid; |
93 | |
94 | struct netlbl_af6list list; |
95 | struct rcu_head rcu; |
96 | }; |
97 | struct netlbl_unlhsh_iface { |
98 | int ifindex; |
99 | struct list_head addr4_list; |
100 | struct list_head addr6_list; |
101 | |
102 | u32 valid; |
103 | struct list_head list; |
104 | struct rcu_head rcu; |
105 | }; |
106 | |
107 | /* Argument struct for netlbl_unlhsh_walk() */ |
108 | struct netlbl_unlhsh_walk_arg { |
109 | struct netlink_callback *nl_cb; |
110 | struct sk_buff *skb; |
111 | u32 seq; |
112 | }; |
113 | |
114 | /* Unlabeled connection hash table */ |
115 | /* updates should be so rare that having one spinlock for the entire |
116 | * hash table should be okay */ |
117 | static DEFINE_SPINLOCK(netlbl_unlhsh_lock); |
118 | #define netlbl_unlhsh_rcu_deref(p) \ |
119 | rcu_dereference_check(p, rcu_read_lock_held() || \ |
120 | lockdep_is_held(&netlbl_unlhsh_lock)) |
121 | static struct netlbl_unlhsh_tbl *netlbl_unlhsh = NULL; |
122 | static struct netlbl_unlhsh_iface *netlbl_unlhsh_def = NULL; |
123 | |
124 | /* Accept unlabeled packets flag */ |
125 | static u8 netlabel_unlabel_acceptflg = 0; |
126 | |
127 | /* NetLabel Generic NETLINK unlabeled family */ |
128 | static struct genl_family netlbl_unlabel_gnl_family = { |
129 | .id = GENL_ID_GENERATE, |
130 | .hdrsize = 0, |
131 | .name = NETLBL_NLTYPE_UNLABELED_NAME, |
132 | .version = NETLBL_PROTO_VERSION, |
133 | .maxattr = NLBL_UNLABEL_A_MAX, |
134 | }; |
135 | |
136 | /* NetLabel Netlink attribute policy */ |
137 | static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1] = { |
138 | [NLBL_UNLABEL_A_ACPTFLG] = { .type = NLA_U8 }, |
139 | [NLBL_UNLABEL_A_IPV6ADDR] = { .type = NLA_BINARY, |
140 | .len = sizeof(struct in6_addr) }, |
141 | [NLBL_UNLABEL_A_IPV6MASK] = { .type = NLA_BINARY, |
142 | .len = sizeof(struct in6_addr) }, |
143 | [NLBL_UNLABEL_A_IPV4ADDR] = { .type = NLA_BINARY, |
144 | .len = sizeof(struct in_addr) }, |
145 | [NLBL_UNLABEL_A_IPV4MASK] = { .type = NLA_BINARY, |
146 | .len = sizeof(struct in_addr) }, |
147 | [NLBL_UNLABEL_A_IFACE] = { .type = NLA_NUL_STRING, |
148 | .len = IFNAMSIZ - 1 }, |
149 | [NLBL_UNLABEL_A_SECCTX] = { .type = NLA_BINARY } |
150 | }; |
151 | |
152 | /* |
153 | * Unlabeled Connection Hash Table Functions |
154 | */ |
155 | |
156 | /** |
157 | * netlbl_unlhsh_free_addr4 - Frees an IPv4 address entry from the hash table |
158 | * @entry: the entry's RCU field |
159 | * |
160 | * Description: |
161 | * This function is designed to be used as a callback to the call_rcu() |
162 | * function so that memory allocated to a hash table address entry can be |
163 | * released safely. |
164 | * |
165 | */ |
166 | static void netlbl_unlhsh_free_addr4(struct rcu_head *entry) |
167 | { |
168 | struct netlbl_unlhsh_addr4 *ptr; |
169 | |
170 | ptr = container_of(entry, struct netlbl_unlhsh_addr4, rcu); |
171 | kfree(ptr); |
172 | } |
173 | |
174 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
175 | /** |
176 | * netlbl_unlhsh_free_addr6 - Frees an IPv6 address entry from the hash table |
177 | * @entry: the entry's RCU field |
178 | * |
179 | * Description: |
180 | * This function is designed to be used as a callback to the call_rcu() |
181 | * function so that memory allocated to a hash table address entry can be |
182 | * released safely. |
183 | * |
184 | */ |
185 | static void netlbl_unlhsh_free_addr6(struct rcu_head *entry) |
186 | { |
187 | struct netlbl_unlhsh_addr6 *ptr; |
188 | |
189 | ptr = container_of(entry, struct netlbl_unlhsh_addr6, rcu); |
190 | kfree(ptr); |
191 | } |
192 | #endif /* IPv6 */ |
193 | |
194 | /** |
195 | * netlbl_unlhsh_free_iface - Frees an interface entry from the hash table |
196 | * @entry: the entry's RCU field |
197 | * |
198 | * Description: |
199 | * This function is designed to be used as a callback to the call_rcu() |
200 | * function so that memory allocated to a hash table interface entry can be |
201 | * released safely. It is important to note that this function does not free |
202 | * the IPv4 and IPv6 address lists contained as part of an interface entry. It |
203 | * is up to the rest of the code to make sure an interface entry is only freed |
204 | * once it's address lists are empty. |
205 | * |
206 | */ |
207 | static void netlbl_unlhsh_free_iface(struct rcu_head *entry) |
208 | { |
209 | struct netlbl_unlhsh_iface *iface; |
210 | struct netlbl_af4list *iter4; |
211 | struct netlbl_af4list *tmp4; |
212 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
213 | struct netlbl_af6list *iter6; |
214 | struct netlbl_af6list *tmp6; |
215 | #endif /* IPv6 */ |
216 | |
217 | iface = container_of(entry, struct netlbl_unlhsh_iface, rcu); |
218 | |
219 | /* no need for locks here since we are the only one with access to this |
220 | * structure */ |
221 | |
222 | netlbl_af4list_foreach_safe(iter4, tmp4, &iface->addr4_list) { |
223 | netlbl_af4list_remove_entry(iter4); |
224 | kfree(netlbl_unlhsh_addr4_entry(iter4)); |
225 | } |
226 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
227 | netlbl_af6list_foreach_safe(iter6, tmp6, &iface->addr6_list) { |
228 | netlbl_af6list_remove_entry(iter6); |
229 | kfree(netlbl_unlhsh_addr6_entry(iter6)); |
230 | } |
231 | #endif /* IPv6 */ |
232 | kfree(iface); |
233 | } |
234 | |
235 | /** |
236 | * netlbl_unlhsh_hash - Hashing function for the hash table |
237 | * @ifindex: the network interface/device to hash |
238 | * |
239 | * Description: |
240 | * This is the hashing function for the unlabeled hash table, it returns the |
241 | * bucket number for the given device/interface. The caller is responsible for |
242 | * ensuring that the hash table is protected with either a RCU read lock or |
243 | * the hash table lock. |
244 | * |
245 | */ |
246 | static u32 netlbl_unlhsh_hash(int ifindex) |
247 | { |
248 | return ifindex & (netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->size - 1); |
249 | } |
250 | |
251 | /** |
252 | * netlbl_unlhsh_search_iface - Search for a matching interface entry |
253 | * @ifindex: the network interface |
254 | * |
255 | * Description: |
256 | * Searches the unlabeled connection hash table and returns a pointer to the |
257 | * interface entry which matches @ifindex, otherwise NULL is returned. The |
258 | * caller is responsible for ensuring that the hash table is protected with |
259 | * either a RCU read lock or the hash table lock. |
260 | * |
261 | */ |
262 | static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex) |
263 | { |
264 | u32 bkt; |
265 | struct list_head *bkt_list; |
266 | struct netlbl_unlhsh_iface *iter; |
267 | |
268 | bkt = netlbl_unlhsh_hash(ifindex); |
269 | bkt_list = &netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->tbl[bkt]; |
270 | list_for_each_entry_rcu(iter, bkt_list, list) |
271 | if (iter->valid && iter->ifindex == ifindex) |
272 | return iter; |
273 | |
274 | return NULL; |
275 | } |
276 | |
277 | /** |
278 | * netlbl_unlhsh_add_addr4 - Add a new IPv4 address entry to the hash table |
279 | * @iface: the associated interface entry |
280 | * @addr: IPv4 address in network byte order |
281 | * @mask: IPv4 address mask in network byte order |
282 | * @secid: LSM secid value for entry |
283 | * |
284 | * Description: |
285 | * Add a new address entry into the unlabeled connection hash table using the |
286 | * interface entry specified by @iface. On success zero is returned, otherwise |
287 | * a negative value is returned. |
288 | * |
289 | */ |
290 | static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface, |
291 | const struct in_addr *addr, |
292 | const struct in_addr *mask, |
293 | u32 secid) |
294 | { |
295 | int ret_val; |
296 | struct netlbl_unlhsh_addr4 *entry; |
297 | |
298 | entry = kzalloc(sizeof(*entry), GFP_ATOMIC); |
299 | if (entry == NULL) |
300 | return -ENOMEM; |
301 | |
302 | entry->list.addr = addr->s_addr & mask->s_addr; |
303 | entry->list.mask = mask->s_addr; |
304 | entry->list.valid = 1; |
305 | entry->secid = secid; |
306 | |
307 | spin_lock(&netlbl_unlhsh_lock); |
308 | ret_val = netlbl_af4list_add(&entry->list, &iface->addr4_list); |
309 | spin_unlock(&netlbl_unlhsh_lock); |
310 | |
311 | if (ret_val != 0) |
312 | kfree(entry); |
313 | return ret_val; |
314 | } |
315 | |
316 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
317 | /** |
318 | * netlbl_unlhsh_add_addr6 - Add a new IPv6 address entry to the hash table |
319 | * @iface: the associated interface entry |
320 | * @addr: IPv6 address in network byte order |
321 | * @mask: IPv6 address mask in network byte order |
322 | * @secid: LSM secid value for entry |
323 | * |
324 | * Description: |
325 | * Add a new address entry into the unlabeled connection hash table using the |
326 | * interface entry specified by @iface. On success zero is returned, otherwise |
327 | * a negative value is returned. |
328 | * |
329 | */ |
330 | static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface, |
331 | const struct in6_addr *addr, |
332 | const struct in6_addr *mask, |
333 | u32 secid) |
334 | { |
335 | int ret_val; |
336 | struct netlbl_unlhsh_addr6 *entry; |
337 | |
338 | entry = kzalloc(sizeof(*entry), GFP_ATOMIC); |
339 | if (entry == NULL) |
340 | return -ENOMEM; |
341 | |
342 | ipv6_addr_copy(&entry->list.addr, addr); |
343 | entry->list.addr.s6_addr32[0] &= mask->s6_addr32[0]; |
344 | entry->list.addr.s6_addr32[1] &= mask->s6_addr32[1]; |
345 | entry->list.addr.s6_addr32[2] &= mask->s6_addr32[2]; |
346 | entry->list.addr.s6_addr32[3] &= mask->s6_addr32[3]; |
347 | ipv6_addr_copy(&entry->list.mask, mask); |
348 | entry->list.valid = 1; |
349 | entry->secid = secid; |
350 | |
351 | spin_lock(&netlbl_unlhsh_lock); |
352 | ret_val = netlbl_af6list_add(&entry->list, &iface->addr6_list); |
353 | spin_unlock(&netlbl_unlhsh_lock); |
354 | |
355 | if (ret_val != 0) |
356 | kfree(entry); |
357 | return 0; |
358 | } |
359 | #endif /* IPv6 */ |
360 | |
361 | /** |
362 | * netlbl_unlhsh_add_iface - Adds a new interface entry to the hash table |
363 | * @ifindex: network interface |
364 | * |
365 | * Description: |
366 | * Add a new, empty, interface entry into the unlabeled connection hash table. |
367 | * On success a pointer to the new interface entry is returned, on failure NULL |
368 | * is returned. |
369 | * |
370 | */ |
371 | static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex) |
372 | { |
373 | u32 bkt; |
374 | struct netlbl_unlhsh_iface *iface; |
375 | |
376 | iface = kzalloc(sizeof(*iface), GFP_ATOMIC); |
377 | if (iface == NULL) |
378 | return NULL; |
379 | |
380 | iface->ifindex = ifindex; |
381 | INIT_LIST_HEAD(&iface->addr4_list); |
382 | INIT_LIST_HEAD(&iface->addr6_list); |
383 | iface->valid = 1; |
384 | |
385 | spin_lock(&netlbl_unlhsh_lock); |
386 | if (ifindex > 0) { |
387 | bkt = netlbl_unlhsh_hash(ifindex); |
388 | if (netlbl_unlhsh_search_iface(ifindex) != NULL) |
389 | goto add_iface_failure; |
390 | list_add_tail_rcu(&iface->list, |
391 | &netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->tbl[bkt]); |
392 | } else { |
393 | INIT_LIST_HEAD(&iface->list); |
394 | if (netlbl_unlhsh_rcu_deref(netlbl_unlhsh_def) != NULL) |
395 | goto add_iface_failure; |
396 | rcu_assign_pointer(netlbl_unlhsh_def, iface); |
397 | } |
398 | spin_unlock(&netlbl_unlhsh_lock); |
399 | |
400 | return iface; |
401 | |
402 | add_iface_failure: |
403 | spin_unlock(&netlbl_unlhsh_lock); |
404 | kfree(iface); |
405 | return NULL; |
406 | } |
407 | |
408 | /** |
409 | * netlbl_unlhsh_add - Adds a new entry to the unlabeled connection hash table |
410 | * @net: network namespace |
411 | * @dev_name: interface name |
412 | * @addr: IP address in network byte order |
413 | * @mask: address mask in network byte order |
414 | * @addr_len: length of address/mask (4 for IPv4, 16 for IPv6) |
415 | * @secid: LSM secid value for the entry |
416 | * @audit_info: NetLabel audit information |
417 | * |
418 | * Description: |
419 | * Adds a new entry to the unlabeled connection hash table. Returns zero on |
420 | * success, negative values on failure. |
421 | * |
422 | */ |
423 | int netlbl_unlhsh_add(struct net *net, |
424 | const char *dev_name, |
425 | const void *addr, |
426 | const void *mask, |
427 | u32 addr_len, |
428 | u32 secid, |
429 | struct netlbl_audit *audit_info) |
430 | { |
431 | int ret_val; |
432 | int ifindex; |
433 | struct net_device *dev; |
434 | struct netlbl_unlhsh_iface *iface; |
435 | struct audit_buffer *audit_buf = NULL; |
436 | char *secctx = NULL; |
437 | u32 secctx_len; |
438 | |
439 | if (addr_len != sizeof(struct in_addr) && |
440 | addr_len != sizeof(struct in6_addr)) |
441 | return -EINVAL; |
442 | |
443 | rcu_read_lock(); |
444 | if (dev_name != NULL) { |
445 | dev = dev_get_by_name_rcu(net, dev_name); |
446 | if (dev == NULL) { |
447 | ret_val = -ENODEV; |
448 | goto unlhsh_add_return; |
449 | } |
450 | ifindex = dev->ifindex; |
451 | iface = netlbl_unlhsh_search_iface(ifindex); |
452 | } else { |
453 | ifindex = 0; |
454 | iface = rcu_dereference(netlbl_unlhsh_def); |
455 | } |
456 | if (iface == NULL) { |
457 | iface = netlbl_unlhsh_add_iface(ifindex); |
458 | if (iface == NULL) { |
459 | ret_val = -ENOMEM; |
460 | goto unlhsh_add_return; |
461 | } |
462 | } |
463 | audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCADD, |
464 | audit_info); |
465 | switch (addr_len) { |
466 | case sizeof(struct in_addr): { |
467 | struct in_addr *addr4, *mask4; |
468 | |
469 | addr4 = (struct in_addr *)addr; |
470 | mask4 = (struct in_addr *)mask; |
471 | ret_val = netlbl_unlhsh_add_addr4(iface, addr4, mask4, secid); |
472 | if (audit_buf != NULL) |
473 | netlbl_af4list_audit_addr(audit_buf, 1, |
474 | dev_name, |
475 | addr4->s_addr, |
476 | mask4->s_addr); |
477 | break; |
478 | } |
479 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
480 | case sizeof(struct in6_addr): { |
481 | struct in6_addr *addr6, *mask6; |
482 | |
483 | addr6 = (struct in6_addr *)addr; |
484 | mask6 = (struct in6_addr *)mask; |
485 | ret_val = netlbl_unlhsh_add_addr6(iface, addr6, mask6, secid); |
486 | if (audit_buf != NULL) |
487 | netlbl_af6list_audit_addr(audit_buf, 1, |
488 | dev_name, |
489 | addr6, mask6); |
490 | break; |
491 | } |
492 | #endif /* IPv6 */ |
493 | default: |
494 | ret_val = -EINVAL; |
495 | } |
496 | if (ret_val == 0) |
497 | atomic_inc(&netlabel_mgmt_protocount); |
498 | |
499 | unlhsh_add_return: |
500 | rcu_read_unlock(); |
501 | if (audit_buf != NULL) { |
502 | if (security_secid_to_secctx(secid, |
503 | &secctx, |
504 | &secctx_len) == 0) { |
505 | audit_log_format(audit_buf, " sec_obj=%s", secctx); |
506 | security_release_secctx(secctx, secctx_len); |
507 | } |
508 | audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0); |
509 | audit_log_end(audit_buf); |
510 | } |
511 | return ret_val; |
512 | } |
513 | |
514 | /** |
515 | * netlbl_unlhsh_remove_addr4 - Remove an IPv4 address entry |
516 | * @net: network namespace |
517 | * @iface: interface entry |
518 | * @addr: IP address |
519 | * @mask: IP address mask |
520 | * @audit_info: NetLabel audit information |
521 | * |
522 | * Description: |
523 | * Remove an IP address entry from the unlabeled connection hash table. |
524 | * Returns zero on success, negative values on failure. |
525 | * |
526 | */ |
527 | static int netlbl_unlhsh_remove_addr4(struct net *net, |
528 | struct netlbl_unlhsh_iface *iface, |
529 | const struct in_addr *addr, |
530 | const struct in_addr *mask, |
531 | struct netlbl_audit *audit_info) |
532 | { |
533 | struct netlbl_af4list *list_entry; |
534 | struct netlbl_unlhsh_addr4 *entry; |
535 | struct audit_buffer *audit_buf; |
536 | struct net_device *dev; |
537 | char *secctx; |
538 | u32 secctx_len; |
539 | |
540 | spin_lock(&netlbl_unlhsh_lock); |
541 | list_entry = netlbl_af4list_remove(addr->s_addr, mask->s_addr, |
542 | &iface->addr4_list); |
543 | spin_unlock(&netlbl_unlhsh_lock); |
544 | if (list_entry != NULL) |
545 | entry = netlbl_unlhsh_addr4_entry(list_entry); |
546 | else |
547 | entry = NULL; |
548 | |
549 | audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL, |
550 | audit_info); |
551 | if (audit_buf != NULL) { |
552 | dev = dev_get_by_index(net, iface->ifindex); |
553 | netlbl_af4list_audit_addr(audit_buf, 1, |
554 | (dev != NULL ? dev->name : NULL), |
555 | addr->s_addr, mask->s_addr); |
556 | if (dev != NULL) |
557 | dev_put(dev); |
558 | if (entry != NULL && |
559 | security_secid_to_secctx(entry->secid, |
560 | &secctx, &secctx_len) == 0) { |
561 | audit_log_format(audit_buf, " sec_obj=%s", secctx); |
562 | security_release_secctx(secctx, secctx_len); |
563 | } |
564 | audit_log_format(audit_buf, " res=%u", entry != NULL ? 1 : 0); |
565 | audit_log_end(audit_buf); |
566 | } |
567 | |
568 | if (entry == NULL) |
569 | return -ENOENT; |
570 | |
571 | call_rcu(&entry->rcu, netlbl_unlhsh_free_addr4); |
572 | return 0; |
573 | } |
574 | |
575 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
576 | /** |
577 | * netlbl_unlhsh_remove_addr6 - Remove an IPv6 address entry |
578 | * @net: network namespace |
579 | * @iface: interface entry |
580 | * @addr: IP address |
581 | * @mask: IP address mask |
582 | * @audit_info: NetLabel audit information |
583 | * |
584 | * Description: |
585 | * Remove an IP address entry from the unlabeled connection hash table. |
586 | * Returns zero on success, negative values on failure. |
587 | * |
588 | */ |
589 | static int netlbl_unlhsh_remove_addr6(struct net *net, |
590 | struct netlbl_unlhsh_iface *iface, |
591 | const struct in6_addr *addr, |
592 | const struct in6_addr *mask, |
593 | struct netlbl_audit *audit_info) |
594 | { |
595 | struct netlbl_af6list *list_entry; |
596 | struct netlbl_unlhsh_addr6 *entry; |
597 | struct audit_buffer *audit_buf; |
598 | struct net_device *dev; |
599 | char *secctx; |
600 | u32 secctx_len; |
601 | |
602 | spin_lock(&netlbl_unlhsh_lock); |
603 | list_entry = netlbl_af6list_remove(addr, mask, &iface->addr6_list); |
604 | spin_unlock(&netlbl_unlhsh_lock); |
605 | if (list_entry != NULL) |
606 | entry = netlbl_unlhsh_addr6_entry(list_entry); |
607 | else |
608 | entry = NULL; |
609 | |
610 | audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL, |
611 | audit_info); |
612 | if (audit_buf != NULL) { |
613 | dev = dev_get_by_index(net, iface->ifindex); |
614 | netlbl_af6list_audit_addr(audit_buf, 1, |
615 | (dev != NULL ? dev->name : NULL), |
616 | addr, mask); |
617 | if (dev != NULL) |
618 | dev_put(dev); |
619 | if (entry != NULL && |
620 | security_secid_to_secctx(entry->secid, |
621 | &secctx, &secctx_len) == 0) { |
622 | audit_log_format(audit_buf, " sec_obj=%s", secctx); |
623 | security_release_secctx(secctx, secctx_len); |
624 | } |
625 | audit_log_format(audit_buf, " res=%u", entry != NULL ? 1 : 0); |
626 | audit_log_end(audit_buf); |
627 | } |
628 | |
629 | if (entry == NULL) |
630 | return -ENOENT; |
631 | |
632 | call_rcu(&entry->rcu, netlbl_unlhsh_free_addr6); |
633 | return 0; |
634 | } |
635 | #endif /* IPv6 */ |
636 | |
637 | /** |
638 | * netlbl_unlhsh_condremove_iface - Remove an interface entry |
639 | * @iface: the interface entry |
640 | * |
641 | * Description: |
642 | * Remove an interface entry from the unlabeled connection hash table if it is |
643 | * empty. An interface entry is considered to be empty if there are no |
644 | * address entries assigned to it. |
645 | * |
646 | */ |
647 | static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface) |
648 | { |
649 | struct netlbl_af4list *iter4; |
650 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
651 | struct netlbl_af6list *iter6; |
652 | #endif /* IPv6 */ |
653 | |
654 | spin_lock(&netlbl_unlhsh_lock); |
655 | netlbl_af4list_foreach_rcu(iter4, &iface->addr4_list) |
656 | goto unlhsh_condremove_failure; |
657 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
658 | netlbl_af6list_foreach_rcu(iter6, &iface->addr6_list) |
659 | goto unlhsh_condremove_failure; |
660 | #endif /* IPv6 */ |
661 | iface->valid = 0; |
662 | if (iface->ifindex > 0) |
663 | list_del_rcu(&iface->list); |
664 | else |
665 | rcu_assign_pointer(netlbl_unlhsh_def, NULL); |
666 | spin_unlock(&netlbl_unlhsh_lock); |
667 | |
668 | call_rcu(&iface->rcu, netlbl_unlhsh_free_iface); |
669 | return; |
670 | |
671 | unlhsh_condremove_failure: |
672 | spin_unlock(&netlbl_unlhsh_lock); |
673 | } |
674 | |
675 | /** |
676 | * netlbl_unlhsh_remove - Remove an entry from the unlabeled hash table |
677 | * @net: network namespace |
678 | * @dev_name: interface name |
679 | * @addr: IP address in network byte order |
680 | * @mask: address mask in network byte order |
681 | * @addr_len: length of address/mask (4 for IPv4, 16 for IPv6) |
682 | * @audit_info: NetLabel audit information |
683 | * |
684 | * Description: |
685 | * Removes and existing entry from the unlabeled connection hash table. |
686 | * Returns zero on success, negative values on failure. |
687 | * |
688 | */ |
689 | int netlbl_unlhsh_remove(struct net *net, |
690 | const char *dev_name, |
691 | const void *addr, |
692 | const void *mask, |
693 | u32 addr_len, |
694 | struct netlbl_audit *audit_info) |
695 | { |
696 | int ret_val; |
697 | struct net_device *dev; |
698 | struct netlbl_unlhsh_iface *iface; |
699 | |
700 | if (addr_len != sizeof(struct in_addr) && |
701 | addr_len != sizeof(struct in6_addr)) |
702 | return -EINVAL; |
703 | |
704 | rcu_read_lock(); |
705 | if (dev_name != NULL) { |
706 | dev = dev_get_by_name_rcu(net, dev_name); |
707 | if (dev == NULL) { |
708 | ret_val = -ENODEV; |
709 | goto unlhsh_remove_return; |
710 | } |
711 | iface = netlbl_unlhsh_search_iface(dev->ifindex); |
712 | } else |
713 | iface = rcu_dereference(netlbl_unlhsh_def); |
714 | if (iface == NULL) { |
715 | ret_val = -ENOENT; |
716 | goto unlhsh_remove_return; |
717 | } |
718 | switch (addr_len) { |
719 | case sizeof(struct in_addr): |
720 | ret_val = netlbl_unlhsh_remove_addr4(net, |
721 | iface, addr, mask, |
722 | audit_info); |
723 | break; |
724 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
725 | case sizeof(struct in6_addr): |
726 | ret_val = netlbl_unlhsh_remove_addr6(net, |
727 | iface, addr, mask, |
728 | audit_info); |
729 | break; |
730 | #endif /* IPv6 */ |
731 | default: |
732 | ret_val = -EINVAL; |
733 | } |
734 | if (ret_val == 0) { |
735 | netlbl_unlhsh_condremove_iface(iface); |
736 | atomic_dec(&netlabel_mgmt_protocount); |
737 | } |
738 | |
739 | unlhsh_remove_return: |
740 | rcu_read_unlock(); |
741 | return ret_val; |
742 | } |
743 | |
744 | /* |
745 | * General Helper Functions |
746 | */ |
747 | |
748 | /** |
749 | * netlbl_unlhsh_netdev_handler - Network device notification handler |
750 | * @this: notifier block |
751 | * @event: the event |
752 | * @ptr: the network device (cast to void) |
753 | * |
754 | * Description: |
755 | * Handle network device events, although at present all we care about is a |
756 | * network device going away. In the case of a device going away we clear any |
757 | * related entries from the unlabeled connection hash table. |
758 | * |
759 | */ |
760 | static int netlbl_unlhsh_netdev_handler(struct notifier_block *this, |
761 | unsigned long event, |
762 | void *ptr) |
763 | { |
764 | struct net_device *dev = ptr; |
765 | struct netlbl_unlhsh_iface *iface = NULL; |
766 | |
767 | if (!net_eq(dev_net(dev), &init_net)) |
768 | return NOTIFY_DONE; |
769 | |
770 | /* XXX - should this be a check for NETDEV_DOWN or _UNREGISTER? */ |
771 | if (event == NETDEV_DOWN) { |
772 | spin_lock(&netlbl_unlhsh_lock); |
773 | iface = netlbl_unlhsh_search_iface(dev->ifindex); |
774 | if (iface != NULL && iface->valid) { |
775 | iface->valid = 0; |
776 | list_del_rcu(&iface->list); |
777 | } else |
778 | iface = NULL; |
779 | spin_unlock(&netlbl_unlhsh_lock); |
780 | } |
781 | |
782 | if (iface != NULL) |
783 | call_rcu(&iface->rcu, netlbl_unlhsh_free_iface); |
784 | |
785 | return NOTIFY_DONE; |
786 | } |
787 | |
788 | /** |
789 | * netlbl_unlabel_acceptflg_set - Set the unlabeled accept flag |
790 | * @value: desired value |
791 | * @audit_info: NetLabel audit information |
792 | * |
793 | * Description: |
794 | * Set the value of the unlabeled accept flag to @value. |
795 | * |
796 | */ |
797 | static void netlbl_unlabel_acceptflg_set(u8 value, |
798 | struct netlbl_audit *audit_info) |
799 | { |
800 | struct audit_buffer *audit_buf; |
801 | u8 old_val; |
802 | |
803 | old_val = netlabel_unlabel_acceptflg; |
804 | netlabel_unlabel_acceptflg = value; |
805 | audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_ALLOW, |
806 | audit_info); |
807 | if (audit_buf != NULL) { |
808 | audit_log_format(audit_buf, |
809 | " unlbl_accept=%u old=%u", value, old_val); |
810 | audit_log_end(audit_buf); |
811 | } |
812 | } |
813 | |
814 | /** |
815 | * netlbl_unlabel_addrinfo_get - Get the IPv4/6 address information |
816 | * @info: the Generic NETLINK info block |
817 | * @addr: the IP address |
818 | * @mask: the IP address mask |
819 | * @len: the address length |
820 | * |
821 | * Description: |
822 | * Examine the Generic NETLINK message and extract the IP address information. |
823 | * Returns zero on success, negative values on failure. |
824 | * |
825 | */ |
826 | static int netlbl_unlabel_addrinfo_get(struct genl_info *info, |
827 | void **addr, |
828 | void **mask, |
829 | u32 *len) |
830 | { |
831 | u32 addr_len; |
832 | |
833 | if (info->attrs[NLBL_UNLABEL_A_IPV4ADDR]) { |
834 | addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]); |
835 | if (addr_len != sizeof(struct in_addr) && |
836 | addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV4MASK])) |
837 | return -EINVAL; |
838 | *len = addr_len; |
839 | *addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]); |
840 | *mask = nla_data(info->attrs[NLBL_UNLABEL_A_IPV4MASK]); |
841 | return 0; |
842 | } else if (info->attrs[NLBL_UNLABEL_A_IPV6ADDR]) { |
843 | addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]); |
844 | if (addr_len != sizeof(struct in6_addr) && |
845 | addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV6MASK])) |
846 | return -EINVAL; |
847 | *len = addr_len; |
848 | *addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]); |
849 | *mask = nla_data(info->attrs[NLBL_UNLABEL_A_IPV6MASK]); |
850 | return 0; |
851 | } |
852 | |
853 | return -EINVAL; |
854 | } |
855 | |
856 | /* |
857 | * NetLabel Command Handlers |
858 | */ |
859 | |
860 | /** |
861 | * netlbl_unlabel_accept - Handle an ACCEPT message |
862 | * @skb: the NETLINK buffer |
863 | * @info: the Generic NETLINK info block |
864 | * |
865 | * Description: |
866 | * Process a user generated ACCEPT message and set the accept flag accordingly. |
867 | * Returns zero on success, negative values on failure. |
868 | * |
869 | */ |
870 | static int netlbl_unlabel_accept(struct sk_buff *skb, struct genl_info *info) |
871 | { |
872 | u8 value; |
873 | struct netlbl_audit audit_info; |
874 | |
875 | if (info->attrs[NLBL_UNLABEL_A_ACPTFLG]) { |
876 | value = nla_get_u8(info->attrs[NLBL_UNLABEL_A_ACPTFLG]); |
877 | if (value == 1 || value == 0) { |
878 | netlbl_netlink_auditinfo(skb, &audit_info); |
879 | netlbl_unlabel_acceptflg_set(value, &audit_info); |
880 | return 0; |
881 | } |
882 | } |
883 | |
884 | return -EINVAL; |
885 | } |
886 | |
887 | /** |
888 | * netlbl_unlabel_list - Handle a LIST message |
889 | * @skb: the NETLINK buffer |
890 | * @info: the Generic NETLINK info block |
891 | * |
892 | * Description: |
893 | * Process a user generated LIST message and respond with the current status. |
894 | * Returns zero on success, negative values on failure. |
895 | * |
896 | */ |
897 | static int netlbl_unlabel_list(struct sk_buff *skb, struct genl_info *info) |
898 | { |
899 | int ret_val = -EINVAL; |
900 | struct sk_buff *ans_skb; |
901 | void *data; |
902 | |
903 | ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
904 | if (ans_skb == NULL) |
905 | goto list_failure; |
906 | data = genlmsg_put_reply(ans_skb, info, &netlbl_unlabel_gnl_family, |
907 | 0, NLBL_UNLABEL_C_LIST); |
908 | if (data == NULL) { |
909 | ret_val = -ENOMEM; |
910 | goto list_failure; |
911 | } |
912 | |
913 | ret_val = nla_put_u8(ans_skb, |
914 | NLBL_UNLABEL_A_ACPTFLG, |
915 | netlabel_unlabel_acceptflg); |
916 | if (ret_val != 0) |
917 | goto list_failure; |
918 | |
919 | genlmsg_end(ans_skb, data); |
920 | return genlmsg_reply(ans_skb, info); |
921 | |
922 | list_failure: |
923 | kfree_skb(ans_skb); |
924 | return ret_val; |
925 | } |
926 | |
927 | /** |
928 | * netlbl_unlabel_staticadd - Handle a STATICADD message |
929 | * @skb: the NETLINK buffer |
930 | * @info: the Generic NETLINK info block |
931 | * |
932 | * Description: |
933 | * Process a user generated STATICADD message and add a new unlabeled |
934 | * connection entry to the hash table. Returns zero on success, negative |
935 | * values on failure. |
936 | * |
937 | */ |
938 | static int netlbl_unlabel_staticadd(struct sk_buff *skb, |
939 | struct genl_info *info) |
940 | { |
941 | int ret_val; |
942 | char *dev_name; |
943 | void *addr; |
944 | void *mask; |
945 | u32 addr_len; |
946 | u32 secid; |
947 | struct netlbl_audit audit_info; |
948 | |
949 | /* Don't allow users to add both IPv4 and IPv6 addresses for a |
950 | * single entry. However, allow users to create two entries, one each |
951 | * for IPv4 and IPv4, with the same LSM security context which should |
952 | * achieve the same result. */ |
953 | if (!info->attrs[NLBL_UNLABEL_A_SECCTX] || |
954 | !info->attrs[NLBL_UNLABEL_A_IFACE] || |
955 | !((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] || |
956 | !info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^ |
957 | (!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] || |
958 | !info->attrs[NLBL_UNLABEL_A_IPV6MASK]))) |
959 | return -EINVAL; |
960 | |
961 | netlbl_netlink_auditinfo(skb, &audit_info); |
962 | |
963 | ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len); |
964 | if (ret_val != 0) |
965 | return ret_val; |
966 | dev_name = nla_data(info->attrs[NLBL_UNLABEL_A_IFACE]); |
967 | ret_val = security_secctx_to_secid( |
968 | nla_data(info->attrs[NLBL_UNLABEL_A_SECCTX]), |
969 | nla_len(info->attrs[NLBL_UNLABEL_A_SECCTX]), |
970 | &secid); |
971 | if (ret_val != 0) |
972 | return ret_val; |
973 | |
974 | return netlbl_unlhsh_add(&init_net, |
975 | dev_name, addr, mask, addr_len, secid, |
976 | &audit_info); |
977 | } |
978 | |
979 | /** |
980 | * netlbl_unlabel_staticadddef - Handle a STATICADDDEF message |
981 | * @skb: the NETLINK buffer |
982 | * @info: the Generic NETLINK info block |
983 | * |
984 | * Description: |
985 | * Process a user generated STATICADDDEF message and add a new default |
986 | * unlabeled connection entry. Returns zero on success, negative values on |
987 | * failure. |
988 | * |
989 | */ |
990 | static int netlbl_unlabel_staticadddef(struct sk_buff *skb, |
991 | struct genl_info *info) |
992 | { |
993 | int ret_val; |
994 | void *addr; |
995 | void *mask; |
996 | u32 addr_len; |
997 | u32 secid; |
998 | struct netlbl_audit audit_info; |
999 | |
1000 | /* Don't allow users to add both IPv4 and IPv6 addresses for a |
1001 | * single entry. However, allow users to create two entries, one each |
1002 | * for IPv4 and IPv6, with the same LSM security context which should |
1003 | * achieve the same result. */ |
1004 | if (!info->attrs[NLBL_UNLABEL_A_SECCTX] || |
1005 | !((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] || |
1006 | !info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^ |
1007 | (!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] || |
1008 | !info->attrs[NLBL_UNLABEL_A_IPV6MASK]))) |
1009 | return -EINVAL; |
1010 | |
1011 | netlbl_netlink_auditinfo(skb, &audit_info); |
1012 | |
1013 | ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len); |
1014 | if (ret_val != 0) |
1015 | return ret_val; |
1016 | ret_val = security_secctx_to_secid( |
1017 | nla_data(info->attrs[NLBL_UNLABEL_A_SECCTX]), |
1018 | nla_len(info->attrs[NLBL_UNLABEL_A_SECCTX]), |
1019 | &secid); |
1020 | if (ret_val != 0) |
1021 | return ret_val; |
1022 | |
1023 | return netlbl_unlhsh_add(&init_net, |
1024 | NULL, addr, mask, addr_len, secid, |
1025 | &audit_info); |
1026 | } |
1027 | |
1028 | /** |
1029 | * netlbl_unlabel_staticremove - Handle a STATICREMOVE message |
1030 | * @skb: the NETLINK buffer |
1031 | * @info: the Generic NETLINK info block |
1032 | * |
1033 | * Description: |
1034 | * Process a user generated STATICREMOVE message and remove the specified |
1035 | * unlabeled connection entry. Returns zero on success, negative values on |
1036 | * failure. |
1037 | * |
1038 | */ |
1039 | static int netlbl_unlabel_staticremove(struct sk_buff *skb, |
1040 | struct genl_info *info) |
1041 | { |
1042 | int ret_val; |
1043 | char *dev_name; |
1044 | void *addr; |
1045 | void *mask; |
1046 | u32 addr_len; |
1047 | struct netlbl_audit audit_info; |
1048 | |
1049 | /* See the note in netlbl_unlabel_staticadd() about not allowing both |
1050 | * IPv4 and IPv6 in the same entry. */ |
1051 | if (!info->attrs[NLBL_UNLABEL_A_IFACE] || |
1052 | !((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] || |
1053 | !info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^ |
1054 | (!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] || |
1055 | !info->attrs[NLBL_UNLABEL_A_IPV6MASK]))) |
1056 | return -EINVAL; |
1057 | |
1058 | netlbl_netlink_auditinfo(skb, &audit_info); |
1059 | |
1060 | ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len); |
1061 | if (ret_val != 0) |
1062 | return ret_val; |
1063 | dev_name = nla_data(info->attrs[NLBL_UNLABEL_A_IFACE]); |
1064 | |
1065 | return netlbl_unlhsh_remove(&init_net, |
1066 | dev_name, addr, mask, addr_len, |
1067 | &audit_info); |
1068 | } |
1069 | |
1070 | /** |
1071 | * netlbl_unlabel_staticremovedef - Handle a STATICREMOVEDEF message |
1072 | * @skb: the NETLINK buffer |
1073 | * @info: the Generic NETLINK info block |
1074 | * |
1075 | * Description: |
1076 | * Process a user generated STATICREMOVEDEF message and remove the default |
1077 | * unlabeled connection entry. Returns zero on success, negative values on |
1078 | * failure. |
1079 | * |
1080 | */ |
1081 | static int netlbl_unlabel_staticremovedef(struct sk_buff *skb, |
1082 | struct genl_info *info) |
1083 | { |
1084 | int ret_val; |
1085 | void *addr; |
1086 | void *mask; |
1087 | u32 addr_len; |
1088 | struct netlbl_audit audit_info; |
1089 | |
1090 | /* See the note in netlbl_unlabel_staticadd() about not allowing both |
1091 | * IPv4 and IPv6 in the same entry. */ |
1092 | if (!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] || |
1093 | !info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^ |
1094 | (!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] || |
1095 | !info->attrs[NLBL_UNLABEL_A_IPV6MASK]))) |
1096 | return -EINVAL; |
1097 | |
1098 | netlbl_netlink_auditinfo(skb, &audit_info); |
1099 | |
1100 | ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len); |
1101 | if (ret_val != 0) |
1102 | return ret_val; |
1103 | |
1104 | return netlbl_unlhsh_remove(&init_net, |
1105 | NULL, addr, mask, addr_len, |
1106 | &audit_info); |
1107 | } |
1108 | |
1109 | |
1110 | /** |
1111 | * netlbl_unlabel_staticlist_gen - Generate messages for STATICLIST[DEF] |
1112 | * @cmd: command/message |
1113 | * @iface: the interface entry |
1114 | * @addr4: the IPv4 address entry |
1115 | * @addr6: the IPv6 address entry |
1116 | * @arg: the netlbl_unlhsh_walk_arg structure |
1117 | * |
1118 | * Description: |
1119 | * This function is designed to be used to generate a response for a |
1120 | * STATICLIST or STATICLISTDEF message. When called either @addr4 or @addr6 |
1121 | * can be specified, not both, the other unspecified entry should be set to |
1122 | * NULL by the caller. Returns the size of the message on success, negative |
1123 | * values on failure. |
1124 | * |
1125 | */ |
1126 | static int netlbl_unlabel_staticlist_gen(u32 cmd, |
1127 | const struct netlbl_unlhsh_iface *iface, |
1128 | const struct netlbl_unlhsh_addr4 *addr4, |
1129 | const struct netlbl_unlhsh_addr6 *addr6, |
1130 | void *arg) |
1131 | { |
1132 | int ret_val = -ENOMEM; |
1133 | struct netlbl_unlhsh_walk_arg *cb_arg = arg; |
1134 | struct net_device *dev; |
1135 | void *data; |
1136 | u32 secid; |
1137 | char *secctx; |
1138 | u32 secctx_len; |
1139 | |
1140 | data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid, |
1141 | cb_arg->seq, &netlbl_unlabel_gnl_family, |
1142 | NLM_F_MULTI, cmd); |
1143 | if (data == NULL) |
1144 | goto list_cb_failure; |
1145 | |
1146 | if (iface->ifindex > 0) { |
1147 | dev = dev_get_by_index(&init_net, iface->ifindex); |
1148 | if (!dev) { |
1149 | ret_val = -ENODEV; |
1150 | goto list_cb_failure; |
1151 | } |
1152 | ret_val = nla_put_string(cb_arg->skb, |
1153 | NLBL_UNLABEL_A_IFACE, dev->name); |
1154 | dev_put(dev); |
1155 | if (ret_val != 0) |
1156 | goto list_cb_failure; |
1157 | } |
1158 | |
1159 | if (addr4) { |
1160 | struct in_addr addr_struct; |
1161 | |
1162 | addr_struct.s_addr = addr4->list.addr; |
1163 | ret_val = nla_put(cb_arg->skb, |
1164 | NLBL_UNLABEL_A_IPV4ADDR, |
1165 | sizeof(struct in_addr), |
1166 | &addr_struct); |
1167 | if (ret_val != 0) |
1168 | goto list_cb_failure; |
1169 | |
1170 | addr_struct.s_addr = addr4->list.mask; |
1171 | ret_val = nla_put(cb_arg->skb, |
1172 | NLBL_UNLABEL_A_IPV4MASK, |
1173 | sizeof(struct in_addr), |
1174 | &addr_struct); |
1175 | if (ret_val != 0) |
1176 | goto list_cb_failure; |
1177 | |
1178 | secid = addr4->secid; |
1179 | } else { |
1180 | ret_val = nla_put(cb_arg->skb, |
1181 | NLBL_UNLABEL_A_IPV6ADDR, |
1182 | sizeof(struct in6_addr), |
1183 | &addr6->list.addr); |
1184 | if (ret_val != 0) |
1185 | goto list_cb_failure; |
1186 | |
1187 | ret_val = nla_put(cb_arg->skb, |
1188 | NLBL_UNLABEL_A_IPV6MASK, |
1189 | sizeof(struct in6_addr), |
1190 | &addr6->list.mask); |
1191 | if (ret_val != 0) |
1192 | goto list_cb_failure; |
1193 | |
1194 | secid = addr6->secid; |
1195 | } |
1196 | |
1197 | ret_val = security_secid_to_secctx(secid, &secctx, &secctx_len); |
1198 | if (ret_val != 0) |
1199 | goto list_cb_failure; |
1200 | ret_val = nla_put(cb_arg->skb, |
1201 | NLBL_UNLABEL_A_SECCTX, |
1202 | secctx_len, |
1203 | secctx); |
1204 | security_release_secctx(secctx, secctx_len); |
1205 | if (ret_val != 0) |
1206 | goto list_cb_failure; |
1207 | |
1208 | cb_arg->seq++; |
1209 | return genlmsg_end(cb_arg->skb, data); |
1210 | |
1211 | list_cb_failure: |
1212 | genlmsg_cancel(cb_arg->skb, data); |
1213 | return ret_val; |
1214 | } |
1215 | |
1216 | /** |
1217 | * netlbl_unlabel_staticlist - Handle a STATICLIST message |
1218 | * @skb: the NETLINK buffer |
1219 | * @cb: the NETLINK callback |
1220 | * |
1221 | * Description: |
1222 | * Process a user generated STATICLIST message and dump the unlabeled |
1223 | * connection hash table in a form suitable for use in a kernel generated |
1224 | * STATICLIST message. Returns the length of @skb. |
1225 | * |
1226 | */ |
1227 | static int netlbl_unlabel_staticlist(struct sk_buff *skb, |
1228 | struct netlink_callback *cb) |
1229 | { |
1230 | struct netlbl_unlhsh_walk_arg cb_arg; |
1231 | u32 skip_bkt = cb->args[0]; |
1232 | u32 skip_chain = cb->args[1]; |
1233 | u32 skip_addr4 = cb->args[2]; |
1234 | u32 skip_addr6 = cb->args[3]; |
1235 | u32 iter_bkt; |
1236 | u32 iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0; |
1237 | struct netlbl_unlhsh_iface *iface; |
1238 | struct list_head *iter_list; |
1239 | struct netlbl_af4list *addr4; |
1240 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
1241 | struct netlbl_af6list *addr6; |
1242 | #endif |
1243 | |
1244 | cb_arg.nl_cb = cb; |
1245 | cb_arg.skb = skb; |
1246 | cb_arg.seq = cb->nlh->nlmsg_seq; |
1247 | |
1248 | rcu_read_lock(); |
1249 | for (iter_bkt = skip_bkt; |
1250 | iter_bkt < rcu_dereference(netlbl_unlhsh)->size; |
1251 | iter_bkt++, iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0) { |
1252 | iter_list = &rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt]; |
1253 | list_for_each_entry_rcu(iface, iter_list, list) { |
1254 | if (!iface->valid || |
1255 | iter_chain++ < skip_chain) |
1256 | continue; |
1257 | netlbl_af4list_foreach_rcu(addr4, |
1258 | &iface->addr4_list) { |
1259 | if (iter_addr4++ < skip_addr4) |
1260 | continue; |
1261 | if (netlbl_unlabel_staticlist_gen( |
1262 | NLBL_UNLABEL_C_STATICLIST, |
1263 | iface, |
1264 | netlbl_unlhsh_addr4_entry(addr4), |
1265 | NULL, |
1266 | &cb_arg) < 0) { |
1267 | iter_addr4--; |
1268 | iter_chain--; |
1269 | goto unlabel_staticlist_return; |
1270 | } |
1271 | } |
1272 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
1273 | netlbl_af6list_foreach_rcu(addr6, |
1274 | &iface->addr6_list) { |
1275 | if (iter_addr6++ < skip_addr6) |
1276 | continue; |
1277 | if (netlbl_unlabel_staticlist_gen( |
1278 | NLBL_UNLABEL_C_STATICLIST, |
1279 | iface, |
1280 | NULL, |
1281 | netlbl_unlhsh_addr6_entry(addr6), |
1282 | &cb_arg) < 0) { |
1283 | iter_addr6--; |
1284 | iter_chain--; |
1285 | goto unlabel_staticlist_return; |
1286 | } |
1287 | } |
1288 | #endif /* IPv6 */ |
1289 | } |
1290 | } |
1291 | |
1292 | unlabel_staticlist_return: |
1293 | rcu_read_unlock(); |
1294 | cb->args[0] = skip_bkt; |
1295 | cb->args[1] = skip_chain; |
1296 | cb->args[2] = skip_addr4; |
1297 | cb->args[3] = skip_addr6; |
1298 | return skb->len; |
1299 | } |
1300 | |
1301 | /** |
1302 | * netlbl_unlabel_staticlistdef - Handle a STATICLISTDEF message |
1303 | * @skb: the NETLINK buffer |
1304 | * @cb: the NETLINK callback |
1305 | * |
1306 | * Description: |
1307 | * Process a user generated STATICLISTDEF message and dump the default |
1308 | * unlabeled connection entry in a form suitable for use in a kernel generated |
1309 | * STATICLISTDEF message. Returns the length of @skb. |
1310 | * |
1311 | */ |
1312 | static int netlbl_unlabel_staticlistdef(struct sk_buff *skb, |
1313 | struct netlink_callback *cb) |
1314 | { |
1315 | struct netlbl_unlhsh_walk_arg cb_arg; |
1316 | struct netlbl_unlhsh_iface *iface; |
1317 | u32 skip_addr4 = cb->args[0]; |
1318 | u32 skip_addr6 = cb->args[1]; |
1319 | u32 iter_addr4 = 0; |
1320 | struct netlbl_af4list *addr4; |
1321 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
1322 | u32 iter_addr6 = 0; |
1323 | struct netlbl_af6list *addr6; |
1324 | #endif |
1325 | |
1326 | cb_arg.nl_cb = cb; |
1327 | cb_arg.skb = skb; |
1328 | cb_arg.seq = cb->nlh->nlmsg_seq; |
1329 | |
1330 | rcu_read_lock(); |
1331 | iface = rcu_dereference(netlbl_unlhsh_def); |
1332 | if (iface == NULL || !iface->valid) |
1333 | goto unlabel_staticlistdef_return; |
1334 | |
1335 | netlbl_af4list_foreach_rcu(addr4, &iface->addr4_list) { |
1336 | if (iter_addr4++ < skip_addr4) |
1337 | continue; |
1338 | if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF, |
1339 | iface, |
1340 | netlbl_unlhsh_addr4_entry(addr4), |
1341 | NULL, |
1342 | &cb_arg) < 0) { |
1343 | iter_addr4--; |
1344 | goto unlabel_staticlistdef_return; |
1345 | } |
1346 | } |
1347 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
1348 | netlbl_af6list_foreach_rcu(addr6, &iface->addr6_list) { |
1349 | if (iter_addr6++ < skip_addr6) |
1350 | continue; |
1351 | if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF, |
1352 | iface, |
1353 | NULL, |
1354 | netlbl_unlhsh_addr6_entry(addr6), |
1355 | &cb_arg) < 0) { |
1356 | iter_addr6--; |
1357 | goto unlabel_staticlistdef_return; |
1358 | } |
1359 | } |
1360 | #endif /* IPv6 */ |
1361 | |
1362 | unlabel_staticlistdef_return: |
1363 | rcu_read_unlock(); |
1364 | cb->args[0] = skip_addr4; |
1365 | cb->args[1] = skip_addr6; |
1366 | return skb->len; |
1367 | } |
1368 | |
1369 | /* |
1370 | * NetLabel Generic NETLINK Command Definitions |
1371 | */ |
1372 | |
1373 | static struct genl_ops netlbl_unlabel_genl_ops[] = { |
1374 | { |
1375 | .cmd = NLBL_UNLABEL_C_STATICADD, |
1376 | .flags = GENL_ADMIN_PERM, |
1377 | .policy = netlbl_unlabel_genl_policy, |
1378 | .doit = netlbl_unlabel_staticadd, |
1379 | .dumpit = NULL, |
1380 | }, |
1381 | { |
1382 | .cmd = NLBL_UNLABEL_C_STATICREMOVE, |
1383 | .flags = GENL_ADMIN_PERM, |
1384 | .policy = netlbl_unlabel_genl_policy, |
1385 | .doit = netlbl_unlabel_staticremove, |
1386 | .dumpit = NULL, |
1387 | }, |
1388 | { |
1389 | .cmd = NLBL_UNLABEL_C_STATICLIST, |
1390 | .flags = 0, |
1391 | .policy = netlbl_unlabel_genl_policy, |
1392 | .doit = NULL, |
1393 | .dumpit = netlbl_unlabel_staticlist, |
1394 | }, |
1395 | { |
1396 | .cmd = NLBL_UNLABEL_C_STATICADDDEF, |
1397 | .flags = GENL_ADMIN_PERM, |
1398 | .policy = netlbl_unlabel_genl_policy, |
1399 | .doit = netlbl_unlabel_staticadddef, |
1400 | .dumpit = NULL, |
1401 | }, |
1402 | { |
1403 | .cmd = NLBL_UNLABEL_C_STATICREMOVEDEF, |
1404 | .flags = GENL_ADMIN_PERM, |
1405 | .policy = netlbl_unlabel_genl_policy, |
1406 | .doit = netlbl_unlabel_staticremovedef, |
1407 | .dumpit = NULL, |
1408 | }, |
1409 | { |
1410 | .cmd = NLBL_UNLABEL_C_STATICLISTDEF, |
1411 | .flags = 0, |
1412 | .policy = netlbl_unlabel_genl_policy, |
1413 | .doit = NULL, |
1414 | .dumpit = netlbl_unlabel_staticlistdef, |
1415 | }, |
1416 | { |
1417 | .cmd = NLBL_UNLABEL_C_ACCEPT, |
1418 | .flags = GENL_ADMIN_PERM, |
1419 | .policy = netlbl_unlabel_genl_policy, |
1420 | .doit = netlbl_unlabel_accept, |
1421 | .dumpit = NULL, |
1422 | }, |
1423 | { |
1424 | .cmd = NLBL_UNLABEL_C_LIST, |
1425 | .flags = 0, |
1426 | .policy = netlbl_unlabel_genl_policy, |
1427 | .doit = netlbl_unlabel_list, |
1428 | .dumpit = NULL, |
1429 | }, |
1430 | }; |
1431 | |
1432 | /* |
1433 | * NetLabel Generic NETLINK Protocol Functions |
1434 | */ |
1435 | |
1436 | /** |
1437 | * netlbl_unlabel_genl_init - Register the Unlabeled NetLabel component |
1438 | * |
1439 | * Description: |
1440 | * Register the unlabeled packet NetLabel component with the Generic NETLINK |
1441 | * mechanism. Returns zero on success, negative values on failure. |
1442 | * |
1443 | */ |
1444 | int __init netlbl_unlabel_genl_init(void) |
1445 | { |
1446 | return genl_register_family_with_ops(&netlbl_unlabel_gnl_family, |
1447 | netlbl_unlabel_genl_ops, ARRAY_SIZE(netlbl_unlabel_genl_ops)); |
1448 | } |
1449 | |
1450 | /* |
1451 | * NetLabel KAPI Hooks |
1452 | */ |
1453 | |
1454 | static struct notifier_block netlbl_unlhsh_netdev_notifier = { |
1455 | .notifier_call = netlbl_unlhsh_netdev_handler, |
1456 | }; |
1457 | |
1458 | /** |
1459 | * netlbl_unlabel_init - Initialize the unlabeled connection hash table |
1460 | * @size: the number of bits to use for the hash buckets |
1461 | * |
1462 | * Description: |
1463 | * Initializes the unlabeled connection hash table and registers a network |
1464 | * device notification handler. This function should only be called by the |
1465 | * NetLabel subsystem itself during initialization. Returns zero on success, |
1466 | * non-zero values on error. |
1467 | * |
1468 | */ |
1469 | int __init netlbl_unlabel_init(u32 size) |
1470 | { |
1471 | u32 iter; |
1472 | struct netlbl_unlhsh_tbl *hsh_tbl; |
1473 | |
1474 | if (size == 0) |
1475 | return -EINVAL; |
1476 | |
1477 | hsh_tbl = kmalloc(sizeof(*hsh_tbl), GFP_KERNEL); |
1478 | if (hsh_tbl == NULL) |
1479 | return -ENOMEM; |
1480 | hsh_tbl->size = 1 << size; |
1481 | hsh_tbl->tbl = kcalloc(hsh_tbl->size, |
1482 | sizeof(struct list_head), |
1483 | GFP_KERNEL); |
1484 | if (hsh_tbl->tbl == NULL) { |
1485 | kfree(hsh_tbl); |
1486 | return -ENOMEM; |
1487 | } |
1488 | for (iter = 0; iter < hsh_tbl->size; iter++) |
1489 | INIT_LIST_HEAD(&hsh_tbl->tbl[iter]); |
1490 | |
1491 | rcu_read_lock(); |
1492 | spin_lock(&netlbl_unlhsh_lock); |
1493 | rcu_assign_pointer(netlbl_unlhsh, hsh_tbl); |
1494 | spin_unlock(&netlbl_unlhsh_lock); |
1495 | rcu_read_unlock(); |
1496 | |
1497 | register_netdevice_notifier(&netlbl_unlhsh_netdev_notifier); |
1498 | |
1499 | return 0; |
1500 | } |
1501 | |
1502 | /** |
1503 | * netlbl_unlabel_getattr - Get the security attributes for an unlabled packet |
1504 | * @skb: the packet |
1505 | * @family: protocol family |
1506 | * @secattr: the security attributes |
1507 | * |
1508 | * Description: |
1509 | * Determine the security attributes, if any, for an unlabled packet and return |
1510 | * them in @secattr. Returns zero on success and negative values on failure. |
1511 | * |
1512 | */ |
1513 | int netlbl_unlabel_getattr(const struct sk_buff *skb, |
1514 | u16 family, |
1515 | struct netlbl_lsm_secattr *secattr) |
1516 | { |
1517 | struct netlbl_unlhsh_iface *iface; |
1518 | |
1519 | rcu_read_lock(); |
1520 | iface = netlbl_unlhsh_search_iface(skb->skb_iif); |
1521 | if (iface == NULL) |
1522 | iface = rcu_dereference(netlbl_unlhsh_def); |
1523 | if (iface == NULL || !iface->valid) |
1524 | goto unlabel_getattr_nolabel; |
1525 | switch (family) { |
1526 | case PF_INET: { |
1527 | struct iphdr *hdr4; |
1528 | struct netlbl_af4list *addr4; |
1529 | |
1530 | hdr4 = ip_hdr(skb); |
1531 | addr4 = netlbl_af4list_search(hdr4->saddr, |
1532 | &iface->addr4_list); |
1533 | if (addr4 == NULL) |
1534 | goto unlabel_getattr_nolabel; |
1535 | secattr->attr.secid = netlbl_unlhsh_addr4_entry(addr4)->secid; |
1536 | break; |
1537 | } |
1538 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
1539 | case PF_INET6: { |
1540 | struct ipv6hdr *hdr6; |
1541 | struct netlbl_af6list *addr6; |
1542 | |
1543 | hdr6 = ipv6_hdr(skb); |
1544 | addr6 = netlbl_af6list_search(&hdr6->saddr, |
1545 | &iface->addr6_list); |
1546 | if (addr6 == NULL) |
1547 | goto unlabel_getattr_nolabel; |
1548 | secattr->attr.secid = netlbl_unlhsh_addr6_entry(addr6)->secid; |
1549 | break; |
1550 | } |
1551 | #endif /* IPv6 */ |
1552 | default: |
1553 | goto unlabel_getattr_nolabel; |
1554 | } |
1555 | rcu_read_unlock(); |
1556 | |
1557 | secattr->flags |= NETLBL_SECATTR_SECID; |
1558 | secattr->type = NETLBL_NLTYPE_UNLABELED; |
1559 | return 0; |
1560 | |
1561 | unlabel_getattr_nolabel: |
1562 | rcu_read_unlock(); |
1563 | if (netlabel_unlabel_acceptflg == 0) |
1564 | return -ENOMSG; |
1565 | secattr->type = NETLBL_NLTYPE_UNLABELED; |
1566 | return 0; |
1567 | } |
1568 | |
1569 | /** |
1570 | * netlbl_unlabel_defconf - Set the default config to allow unlabeled packets |
1571 | * |
1572 | * Description: |
1573 | * Set the default NetLabel configuration to allow incoming unlabeled packets |
1574 | * and to send unlabeled network traffic by default. |
1575 | * |
1576 | */ |
1577 | int __init netlbl_unlabel_defconf(void) |
1578 | { |
1579 | int ret_val; |
1580 | struct netlbl_dom_map *entry; |
1581 | struct netlbl_audit audit_info; |
1582 | |
1583 | /* Only the kernel is allowed to call this function and the only time |
1584 | * it is called is at bootup before the audit subsystem is reporting |
1585 | * messages so don't worry to much about these values. */ |
1586 | security_task_getsecid(current, &audit_info.secid); |
1587 | audit_info.loginuid = 0; |
1588 | audit_info.sessionid = 0; |
1589 | |
1590 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
1591 | if (entry == NULL) |
1592 | return -ENOMEM; |
1593 | entry->type = NETLBL_NLTYPE_UNLABELED; |
1594 | ret_val = netlbl_domhsh_add_default(entry, &audit_info); |
1595 | if (ret_val != 0) |
1596 | return ret_val; |
1597 | |
1598 | netlbl_unlabel_acceptflg_set(1, &audit_info); |
1599 | |
1600 | return 0; |
1601 | } |
1602 |
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