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