Branch data Line data Source code
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 : 0 : static void netlbl_unlhsh_free_iface(struct rcu_head *entry)
148 : : {
149 : 0 : struct netlbl_unlhsh_iface *iface;
150 : 0 : struct netlbl_af4list *iter4;
151 : 0 : struct netlbl_af4list *tmp4;
152 : : #if IS_ENABLED(CONFIG_IPV6)
153 : 0 : struct netlbl_af6list *iter6;
154 : 0 : struct netlbl_af6list *tmp6;
155 : : #endif /* IPv6 */
156 : :
157 : 0 : 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 [ # # ]: 0 : netlbl_af4list_foreach_safe(iter4, tmp4, &iface->addr4_list) {
163 : 0 : netlbl_af4list_remove_entry(iter4);
164 : 0 : kfree(netlbl_unlhsh_addr4_entry(iter4));
165 : : }
166 : : #if IS_ENABLED(CONFIG_IPV6)
167 [ # # ]: 0 : netlbl_af6list_foreach_safe(iter6, tmp6, &iface->addr6_list) {
168 : 0 : netlbl_af6list_remove_entry(iter6);
169 : 0 : kfree(netlbl_unlhsh_addr6_entry(iter6));
170 : : }
171 : : #endif /* IPv6 */
172 : 0 : kfree(iface);
173 : 0 : }
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 : 0 : static u32 netlbl_unlhsh_hash(int ifindex)
187 : : {
188 : 0 : 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 : 0 : static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
203 : : {
204 : 0 : u32 bkt;
205 : 0 : struct list_head *bkt_list;
206 : 0 : struct netlbl_unlhsh_iface *iter;
207 : :
208 : 0 : bkt = netlbl_unlhsh_hash(ifindex);
209 : 0 : bkt_list = &netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->tbl[bkt];
210 [ # # ]: 0 : list_for_each_entry_rcu(iter, bkt_list, list,
211 : : lockdep_is_held(&netlbl_unlhsh_lock))
212 [ # # # # ]: 0 : if (iter->valid && iter->ifindex == ifindex)
213 : 0 : 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(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(&netlbl_unlhsh_lock);
249 : : ret_val = netlbl_af4list_add(&entry->list, &iface->addr4_list);
250 : : spin_unlock(&netlbl_unlhsh_lock);
251 : :
252 : : if (ret_val != 0)
253 : : kfree(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(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(&netlbl_unlhsh_lock);
293 : : ret_val = netlbl_af6list_add(&entry->list, &iface->addr6_list);
294 : : spin_unlock(&netlbl_unlhsh_lock);
295 : :
296 : : if (ret_val != 0)
297 : : kfree(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 : 0 : static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
313 : : {
314 : 0 : u32 bkt;
315 : 0 : struct netlbl_unlhsh_iface *iface;
316 : :
317 : 0 : iface = kzalloc(sizeof(*iface), GFP_ATOMIC);
318 [ # # ]: 0 : if (iface == NULL)
319 : : return NULL;
320 : :
321 : 0 : iface->ifindex = ifindex;
322 : 0 : INIT_LIST_HEAD(&iface->addr4_list);
323 : 0 : INIT_LIST_HEAD(&iface->addr6_list);
324 : 0 : iface->valid = 1;
325 : :
326 : 0 : spin_lock(&netlbl_unlhsh_lock);
327 [ # # ]: 0 : if (ifindex > 0) {
328 : 0 : bkt = netlbl_unlhsh_hash(ifindex);
329 [ # # ]: 0 : if (netlbl_unlhsh_search_iface(ifindex) != NULL)
330 : 0 : goto add_iface_failure;
331 : 0 : list_add_tail_rcu(&iface->list,
332 : 0 : &netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->tbl[bkt]);
333 : : } else {
334 [ # # ]: 0 : INIT_LIST_HEAD(&iface->list);
335 [ # # ]: 0 : if (netlbl_unlhsh_rcu_deref(netlbl_unlhsh_def) != NULL)
336 : 0 : goto add_iface_failure;
337 : 0 : rcu_assign_pointer(netlbl_unlhsh_def, iface);
338 : : }
339 : 0 : spin_unlock(&netlbl_unlhsh_lock);
340 : :
341 : 0 : return iface;
342 : :
343 : 0 : add_iface_failure:
344 : 0 : spin_unlock(&netlbl_unlhsh_lock);
345 : 0 : kfree(iface);
346 : 0 : 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 : 0 : 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 : 0 : int ret_val;
373 : 0 : int ifindex;
374 : 0 : struct net_device *dev;
375 : 0 : struct netlbl_unlhsh_iface *iface;
376 : 0 : struct audit_buffer *audit_buf = NULL;
377 : 0 : char *secctx = NULL;
378 : 0 : u32 secctx_len;
379 : :
380 : 0 : if (addr_len != sizeof(struct in_addr) &&
381 [ # # ]: 0 : addr_len != sizeof(struct in6_addr))
382 : : return -EINVAL;
383 : :
384 : 0 : rcu_read_lock();
385 [ # # ]: 0 : if (dev_name != NULL) {
386 : 0 : dev = dev_get_by_name_rcu(net, dev_name);
387 [ # # ]: 0 : if (dev == NULL) {
388 : 0 : ret_val = -ENODEV;
389 : 0 : goto unlhsh_add_return;
390 : : }
391 : 0 : ifindex = dev->ifindex;
392 : 0 : iface = netlbl_unlhsh_search_iface(ifindex);
393 : : } else {
394 : 0 : ifindex = 0;
395 : 0 : iface = rcu_dereference(netlbl_unlhsh_def);
396 : : }
397 [ # # ]: 0 : if (iface == NULL) {
398 : 0 : iface = netlbl_unlhsh_add_iface(ifindex);
399 [ # # ]: 0 : if (iface == NULL) {
400 : 0 : ret_val = -ENOMEM;
401 : 0 : goto unlhsh_add_return;
402 : : }
403 : : }
404 : 0 : audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCADD,
405 : : audit_info);
406 [ # # # ]: 0 : switch (addr_len) {
407 : 0 : case sizeof(struct in_addr): {
408 : 0 : const struct in_addr *addr4 = addr;
409 : 0 : const struct in_addr *mask4 = mask;
410 : :
411 : 0 : ret_val = netlbl_unlhsh_add_addr4(iface, addr4, mask4, secid);
412 [ # # ]: 0 : if (audit_buf != NULL)
413 : 0 : netlbl_af4list_audit_addr(audit_buf, 1,
414 : : dev_name,
415 : : addr4->s_addr,
416 : : mask4->s_addr);
417 : : break;
418 : : }
419 : : #if IS_ENABLED(CONFIG_IPV6)
420 : 0 : case sizeof(struct in6_addr): {
421 : 0 : const struct in6_addr *addr6 = addr;
422 : 0 : const struct in6_addr *mask6 = mask;
423 : :
424 : 0 : ret_val = netlbl_unlhsh_add_addr6(iface, addr6, mask6, secid);
425 [ # # ]: 0 : if (audit_buf != NULL)
426 : 0 : netlbl_af6list_audit_addr(audit_buf, 1,
427 : : dev_name,
428 : : addr6, mask6);
429 : : break;
430 : : }
431 : : #endif /* IPv6 */
432 : : default:
433 : : ret_val = -EINVAL;
434 : : }
435 [ # # ]: 0 : if (ret_val == 0)
436 : 0 : atomic_inc(&netlabel_mgmt_protocount);
437 : :
438 : 0 : unlhsh_add_return:
439 : 0 : rcu_read_unlock();
440 [ # # ]: 0 : if (audit_buf != NULL) {
441 [ # # ]: 0 : if (security_secid_to_secctx(secid,
442 : : &secctx,
443 : : &secctx_len) == 0) {
444 : 0 : audit_log_format(audit_buf, " sec_obj=%s", secctx);
445 : 0 : security_release_secctx(secctx, secctx_len);
446 : : }
447 : 0 : audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
448 : 0 : audit_log_end(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(&netlbl_unlhsh_lock);
480 : : list_entry = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
481 : : &iface->addr4_list);
482 : : spin_unlock(&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, iface->ifindex);
492 : : netlbl_af4list_audit_addr(audit_buf, 1,
493 : : (dev != NULL ? dev->name : NULL),
494 : : addr->s_addr, mask->s_addr);
495 : : if (dev != NULL)
496 : : dev_put(dev);
497 : : if (entry != NULL &&
498 : : security_secid_to_secctx(entry->secid,
499 : : &secctx, &secctx_len) == 0) {
500 : : audit_log_format(audit_buf, " sec_obj=%s", secctx);
501 : : security_release_secctx(secctx, secctx_len);
502 : : }
503 : : audit_log_format(audit_buf, " res=%u", entry != NULL ? 1 : 0);
504 : : audit_log_end(audit_buf);
505 : : }
506 : :
507 : : if (entry == NULL)
508 : : return -ENOENT;
509 : :
510 : : kfree_rcu(entry, rcu);
511 : : return 0;
512 : : }
513 : :
514 : : #if IS_ENABLED(CONFIG_IPV6)
515 : : /**
516 : : * netlbl_unlhsh_remove_addr6 - Remove an IPv6 address entry
517 : : * @net: network namespace
518 : : * @iface: interface entry
519 : : * @addr: IP address
520 : : * @mask: IP address mask
521 : : * @audit_info: NetLabel audit information
522 : : *
523 : : * Description:
524 : : * Remove an IP address entry from the unlabeled connection hash table.
525 : : * Returns zero on success, negative values on failure.
526 : : *
527 : : */
528 : 0 : static int netlbl_unlhsh_remove_addr6(struct net *net,
529 : : struct netlbl_unlhsh_iface *iface,
530 : : const struct in6_addr *addr,
531 : : const struct in6_addr *mask,
532 : : struct netlbl_audit *audit_info)
533 : : {
534 : 0 : struct netlbl_af6list *list_entry;
535 : 0 : struct netlbl_unlhsh_addr6 *entry;
536 : 0 : struct audit_buffer *audit_buf;
537 : 0 : struct net_device *dev;
538 : 0 : char *secctx;
539 : 0 : u32 secctx_len;
540 : :
541 : 0 : spin_lock(&netlbl_unlhsh_lock);
542 : 0 : list_entry = netlbl_af6list_remove(addr, mask, &iface->addr6_list);
543 : 0 : spin_unlock(&netlbl_unlhsh_lock);
544 [ # # ]: 0 : if (list_entry != NULL)
545 : 0 : entry = netlbl_unlhsh_addr6_entry(list_entry);
546 : : else
547 : : entry = NULL;
548 : :
549 : 0 : audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
550 : : audit_info);
551 [ # # ]: 0 : if (audit_buf != NULL) {
552 : 0 : dev = dev_get_by_index(net, iface->ifindex);
553 [ # # ]: 0 : netlbl_af6list_audit_addr(audit_buf, 1,
554 : : (dev != NULL ? dev->name : NULL),
555 : : addr, mask);
556 [ # # ]: 0 : if (dev != NULL)
557 : 0 : dev_put(dev);
558 [ # # # # ]: 0 : if (entry != NULL &&
559 : 0 : security_secid_to_secctx(entry->secid,
560 : : &secctx, &secctx_len) == 0) {
561 : 0 : audit_log_format(audit_buf, " sec_obj=%s", secctx);
562 : 0 : security_release_secctx(secctx, secctx_len);
563 : : }
564 : 0 : audit_log_format(audit_buf, " res=%u", entry != NULL ? 1 : 0);
565 : 0 : audit_log_end(audit_buf);
566 : : }
567 : :
568 [ # # ]: 0 : if (entry == NULL)
569 : : return -ENOENT;
570 : :
571 : 0 : kfree_rcu(entry, rcu);
572 : 0 : return 0;
573 : : }
574 : : #endif /* IPv6 */
575 : :
576 : : /**
577 : : * netlbl_unlhsh_condremove_iface - Remove an interface entry
578 : : * @iface: the interface entry
579 : : *
580 : : * Description:
581 : : * Remove an interface entry from the unlabeled connection hash table if it is
582 : : * empty. An interface entry is considered to be empty if there are no
583 : : * address entries assigned to it.
584 : : *
585 : : */
586 : 0 : static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface)
587 : : {
588 : 0 : struct netlbl_af4list *iter4;
589 : : #if IS_ENABLED(CONFIG_IPV6)
590 : 0 : struct netlbl_af6list *iter6;
591 : : #endif /* IPv6 */
592 : :
593 : 0 : spin_lock(&netlbl_unlhsh_lock);
594 [ # # ]: 0 : netlbl_af4list_foreach_rcu(iter4, &iface->addr4_list)
595 : 0 : goto unlhsh_condremove_failure;
596 : : #if IS_ENABLED(CONFIG_IPV6)
597 [ # # ]: 0 : netlbl_af6list_foreach_rcu(iter6, &iface->addr6_list)
598 : 0 : goto unlhsh_condremove_failure;
599 : : #endif /* IPv6 */
600 : 0 : iface->valid = 0;
601 [ # # ]: 0 : if (iface->ifindex > 0)
602 : 0 : list_del_rcu(&iface->list);
603 : : else
604 : 0 : RCU_INIT_POINTER(netlbl_unlhsh_def, NULL);
605 : 0 : spin_unlock(&netlbl_unlhsh_lock);
606 : :
607 : 0 : call_rcu(&iface->rcu, netlbl_unlhsh_free_iface);
608 : 0 : return;
609 : :
610 : 0 : unlhsh_condremove_failure:
611 : 0 : spin_unlock(&netlbl_unlhsh_lock);
612 : : }
613 : :
614 : : /**
615 : : * netlbl_unlhsh_remove - Remove an entry from the unlabeled hash table
616 : : * @net: network namespace
617 : : * @dev_name: interface name
618 : : * @addr: IP address in network byte order
619 : : * @mask: address mask in network byte order
620 : : * @addr_len: length of address/mask (4 for IPv4, 16 for IPv6)
621 : : * @audit_info: NetLabel audit information
622 : : *
623 : : * Description:
624 : : * Removes and existing entry from the unlabeled connection hash table.
625 : : * Returns zero on success, negative values on failure.
626 : : *
627 : : */
628 : 0 : int netlbl_unlhsh_remove(struct net *net,
629 : : const char *dev_name,
630 : : const void *addr,
631 : : const void *mask,
632 : : u32 addr_len,
633 : : struct netlbl_audit *audit_info)
634 : : {
635 : 0 : int ret_val;
636 : 0 : struct net_device *dev;
637 : 0 : struct netlbl_unlhsh_iface *iface;
638 : :
639 : 0 : if (addr_len != sizeof(struct in_addr) &&
640 [ # # ]: 0 : addr_len != sizeof(struct in6_addr))
641 : : return -EINVAL;
642 : :
643 : 0 : rcu_read_lock();
644 [ # # ]: 0 : if (dev_name != NULL) {
645 : 0 : dev = dev_get_by_name_rcu(net, dev_name);
646 [ # # ]: 0 : if (dev == NULL) {
647 : 0 : ret_val = -ENODEV;
648 : 0 : goto unlhsh_remove_return;
649 : : }
650 : 0 : iface = netlbl_unlhsh_search_iface(dev->ifindex);
651 : : } else
652 : 0 : iface = rcu_dereference(netlbl_unlhsh_def);
653 [ # # ]: 0 : if (iface == NULL) {
654 : 0 : ret_val = -ENOENT;
655 : 0 : goto unlhsh_remove_return;
656 : : }
657 [ # # # ]: 0 : switch (addr_len) {
658 : 0 : case sizeof(struct in_addr):
659 : 0 : ret_val = netlbl_unlhsh_remove_addr4(net,
660 : : iface, addr, mask,
661 : : audit_info);
662 : 0 : break;
663 : : #if IS_ENABLED(CONFIG_IPV6)
664 : 0 : case sizeof(struct in6_addr):
665 : 0 : ret_val = netlbl_unlhsh_remove_addr6(net,
666 : : iface, addr, mask,
667 : : audit_info);
668 : 0 : break;
669 : : #endif /* IPv6 */
670 : : default:
671 : : ret_val = -EINVAL;
672 : : }
673 [ # # ]: 0 : if (ret_val == 0) {
674 : 0 : netlbl_unlhsh_condremove_iface(iface);
675 : 0 : atomic_dec(&netlabel_mgmt_protocount);
676 : : }
677 : :
678 : 0 : unlhsh_remove_return:
679 : 0 : rcu_read_unlock();
680 : 0 : return ret_val;
681 : : }
682 : :
683 : : /*
684 : : * General Helper Functions
685 : : */
686 : :
687 : : /**
688 : : * netlbl_unlhsh_netdev_handler - Network device notification handler
689 : : * @this: notifier block
690 : : * @event: the event
691 : : * @ptr: the netdevice notifier info (cast to void)
692 : : *
693 : : * Description:
694 : : * Handle network device events, although at present all we care about is a
695 : : * network device going away. In the case of a device going away we clear any
696 : : * related entries from the unlabeled connection hash table.
697 : : *
698 : : */
699 : 140 : static int netlbl_unlhsh_netdev_handler(struct notifier_block *this,
700 : : unsigned long event, void *ptr)
701 : : {
702 [ + - ]: 140 : struct net_device *dev = netdev_notifier_info_to_dev(ptr);
703 : 140 : struct netlbl_unlhsh_iface *iface = NULL;
704 : :
705 [ + - ]: 140 : if (!net_eq(dev_net(dev), &init_net))
706 : : return NOTIFY_DONE;
707 : :
708 : : /* XXX - should this be a check for NETDEV_DOWN or _UNREGISTER? */
709 [ - + ]: 140 : if (event == NETDEV_DOWN) {
710 : 0 : spin_lock(&netlbl_unlhsh_lock);
711 : 0 : iface = netlbl_unlhsh_search_iface(dev->ifindex);
712 [ # # # # ]: 0 : if (iface != NULL && iface->valid) {
713 : 0 : iface->valid = 0;
714 : 0 : list_del_rcu(&iface->list);
715 : : } else
716 : : iface = NULL;
717 : 0 : spin_unlock(&netlbl_unlhsh_lock);
718 : : }
719 : :
720 [ # # ]: 0 : if (iface != NULL)
721 : 0 : call_rcu(&iface->rcu, netlbl_unlhsh_free_iface);
722 : :
723 : : return NOTIFY_DONE;
724 : : }
725 : :
726 : : /**
727 : : * netlbl_unlabel_acceptflg_set - Set the unlabeled accept flag
728 : : * @value: desired value
729 : : * @audit_info: NetLabel audit information
730 : : *
731 : : * Description:
732 : : * Set the value of the unlabeled accept flag to @value.
733 : : *
734 : : */
735 : 28 : static void netlbl_unlabel_acceptflg_set(u8 value,
736 : : struct netlbl_audit *audit_info)
737 : : {
738 : 28 : struct audit_buffer *audit_buf;
739 : 28 : u8 old_val;
740 : :
741 : 28 : old_val = netlabel_unlabel_acceptflg;
742 : 28 : netlabel_unlabel_acceptflg = value;
743 : 28 : audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_ALLOW,
744 : : audit_info);
745 [ - + ]: 28 : if (audit_buf != NULL) {
746 : 0 : audit_log_format(audit_buf,
747 : : " unlbl_accept=%u old=%u", value, old_val);
748 : 0 : audit_log_end(audit_buf);
749 : : }
750 : 28 : }
751 : :
752 : : /**
753 : : * netlbl_unlabel_addrinfo_get - Get the IPv4/6 address information
754 : : * @info: the Generic NETLINK info block
755 : : * @addr: the IP address
756 : : * @mask: the IP address mask
757 : : * @len: the address length
758 : : *
759 : : * Description:
760 : : * Examine the Generic NETLINK message and extract the IP address information.
761 : : * Returns zero on success, negative values on failure.
762 : : *
763 : : */
764 : : static int netlbl_unlabel_addrinfo_get(struct genl_info *info,
765 : : void **addr,
766 : : void **mask,
767 : : u32 *len)
768 : : {
769 : : u32 addr_len;
770 : :
771 : : if (info->attrs[NLBL_UNLABEL_A_IPV4ADDR] &&
772 : : info->attrs[NLBL_UNLABEL_A_IPV4MASK]) {
773 : : addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]);
774 : : if (addr_len != sizeof(struct in_addr) &&
775 : : addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV4MASK]))
776 : : return -EINVAL;
777 : : *len = addr_len;
778 : : *addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]);
779 : : *mask = nla_data(info->attrs[NLBL_UNLABEL_A_IPV4MASK]);
780 : : return 0;
781 : : } else if (info->attrs[NLBL_UNLABEL_A_IPV6ADDR]) {
782 : : addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]);
783 : : if (addr_len != sizeof(struct in6_addr) &&
784 : : addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV6MASK]))
785 : : return -EINVAL;
786 : : *len = addr_len;
787 : : *addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]);
788 : : *mask = nla_data(info->attrs[NLBL_UNLABEL_A_IPV6MASK]);
789 : : return 0;
790 : : }
791 : :
792 : : return -EINVAL;
793 : : }
794 : :
795 : : /*
796 : : * NetLabel Command Handlers
797 : : */
798 : :
799 : : /**
800 : : * netlbl_unlabel_accept - Handle an ACCEPT message
801 : : * @skb: the NETLINK buffer
802 : : * @info: the Generic NETLINK info block
803 : : *
804 : : * Description:
805 : : * Process a user generated ACCEPT message and set the accept flag accordingly.
806 : : * Returns zero on success, negative values on failure.
807 : : *
808 : : */
809 : 0 : static int netlbl_unlabel_accept(struct sk_buff *skb, struct genl_info *info)
810 : : {
811 : 0 : u8 value;
812 : 0 : struct netlbl_audit audit_info;
813 : :
814 [ # # ]: 0 : if (info->attrs[NLBL_UNLABEL_A_ACPTFLG]) {
815 [ # # ]: 0 : value = nla_get_u8(info->attrs[NLBL_UNLABEL_A_ACPTFLG]);
816 [ # # ]: 0 : if (value == 1 || value == 0) {
817 : 0 : netlbl_netlink_auditinfo(skb, &audit_info);
818 : 0 : netlbl_unlabel_acceptflg_set(value, &audit_info);
819 : 0 : return 0;
820 : : }
821 : : }
822 : :
823 : : return -EINVAL;
824 : : }
825 : :
826 : : /**
827 : : * netlbl_unlabel_list - Handle a LIST message
828 : : * @skb: the NETLINK buffer
829 : : * @info: the Generic NETLINK info block
830 : : *
831 : : * Description:
832 : : * Process a user generated LIST message and respond with the current status.
833 : : * Returns zero on success, negative values on failure.
834 : : *
835 : : */
836 : 0 : static int netlbl_unlabel_list(struct sk_buff *skb, struct genl_info *info)
837 : : {
838 : 0 : int ret_val = -EINVAL;
839 : 0 : struct sk_buff *ans_skb;
840 : 0 : void *data;
841 : :
842 : 0 : ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
843 [ # # ]: 0 : if (ans_skb == NULL)
844 : 0 : goto list_failure;
845 : 0 : data = genlmsg_put_reply(ans_skb, info, &netlbl_unlabel_gnl_family,
846 : : 0, NLBL_UNLABEL_C_LIST);
847 [ # # ]: 0 : if (data == NULL) {
848 : 0 : ret_val = -ENOMEM;
849 : 0 : goto list_failure;
850 : : }
851 : :
852 : 0 : ret_val = nla_put_u8(ans_skb,
853 : : NLBL_UNLABEL_A_ACPTFLG,
854 : : netlabel_unlabel_acceptflg);
855 [ # # ]: 0 : if (ret_val != 0)
856 : 0 : goto list_failure;
857 : :
858 : 0 : genlmsg_end(ans_skb, data);
859 : 0 : return genlmsg_reply(ans_skb, info);
860 : :
861 : 0 : list_failure:
862 : 0 : kfree_skb(ans_skb);
863 : 0 : return ret_val;
864 : : }
865 : :
866 : : /**
867 : : * netlbl_unlabel_staticadd - Handle a STATICADD message
868 : : * @skb: the NETLINK buffer
869 : : * @info: the Generic NETLINK info block
870 : : *
871 : : * Description:
872 : : * Process a user generated STATICADD message and add a new unlabeled
873 : : * connection entry to the hash table. Returns zero on success, negative
874 : : * values on failure.
875 : : *
876 : : */
877 : 0 : static int netlbl_unlabel_staticadd(struct sk_buff *skb,
878 : : struct genl_info *info)
879 : : {
880 : 0 : int ret_val;
881 : 0 : char *dev_name;
882 : 0 : void *addr;
883 : 0 : void *mask;
884 : 0 : u32 addr_len;
885 : 0 : u32 secid;
886 : 0 : struct netlbl_audit audit_info;
887 : :
888 : : /* Don't allow users to add both IPv4 and IPv6 addresses for a
889 : : * single entry. However, allow users to create two entries, one each
890 : : * for IPv4 and IPv4, with the same LSM security context which should
891 : : * achieve the same result. */
892 [ # # ]: 0 : if (!info->attrs[NLBL_UNLABEL_A_SECCTX] ||
893 [ # # # # ]: 0 : !info->attrs[NLBL_UNLABEL_A_IFACE] ||
894 [ # # ]: 0 : !((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
895 [ # # ]: 0 : !info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
896 [ # # ]: 0 : (!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
897 [ # # ]: 0 : !info->attrs[NLBL_UNLABEL_A_IPV6MASK])))
898 : : return -EINVAL;
899 : :
900 : 0 : netlbl_netlink_auditinfo(skb, &audit_info);
901 : :
902 : 0 : ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len);
903 [ # # ]: 0 : if (ret_val != 0)
904 : : return ret_val;
905 : 0 : dev_name = nla_data(info->attrs[NLBL_UNLABEL_A_IFACE]);
906 : 0 : ret_val = security_secctx_to_secid(
907 : : nla_data(info->attrs[NLBL_UNLABEL_A_SECCTX]),
908 : 0 : nla_len(info->attrs[NLBL_UNLABEL_A_SECCTX]),
909 : : &secid);
910 [ # # ]: 0 : if (ret_val != 0)
911 : : return ret_val;
912 : :
913 : 0 : return netlbl_unlhsh_add(&init_net,
914 : : dev_name, addr, mask, addr_len, secid,
915 : : &audit_info);
916 : : }
917 : :
918 : : /**
919 : : * netlbl_unlabel_staticadddef - Handle a STATICADDDEF message
920 : : * @skb: the NETLINK buffer
921 : : * @info: the Generic NETLINK info block
922 : : *
923 : : * Description:
924 : : * Process a user generated STATICADDDEF message and add a new default
925 : : * unlabeled connection entry. Returns zero on success, negative values on
926 : : * failure.
927 : : *
928 : : */
929 : 0 : static int netlbl_unlabel_staticadddef(struct sk_buff *skb,
930 : : struct genl_info *info)
931 : : {
932 : 0 : int ret_val;
933 : 0 : void *addr;
934 : 0 : void *mask;
935 : 0 : u32 addr_len;
936 : 0 : u32 secid;
937 : 0 : struct netlbl_audit audit_info;
938 : :
939 : : /* Don't allow users to add both IPv4 and IPv6 addresses for a
940 : : * single entry. However, allow users to create two entries, one each
941 : : * for IPv4 and IPv6, with the same LSM security context which should
942 : : * achieve the same result. */
943 [ # # # # ]: 0 : if (!info->attrs[NLBL_UNLABEL_A_SECCTX] ||
944 [ # # ]: 0 : !((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
945 [ # # ]: 0 : !info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
946 [ # # ]: 0 : (!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
947 [ # # ]: 0 : !info->attrs[NLBL_UNLABEL_A_IPV6MASK])))
948 : : return -EINVAL;
949 : :
950 : 0 : netlbl_netlink_auditinfo(skb, &audit_info);
951 : :
952 : 0 : ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len);
953 [ # # ]: 0 : if (ret_val != 0)
954 : : return ret_val;
955 : 0 : ret_val = security_secctx_to_secid(
956 : : nla_data(info->attrs[NLBL_UNLABEL_A_SECCTX]),
957 : 0 : nla_len(info->attrs[NLBL_UNLABEL_A_SECCTX]),
958 : : &secid);
959 [ # # ]: 0 : if (ret_val != 0)
960 : : return ret_val;
961 : :
962 : 0 : return netlbl_unlhsh_add(&init_net,
963 : : NULL, addr, mask, addr_len, secid,
964 : : &audit_info);
965 : : }
966 : :
967 : : /**
968 : : * netlbl_unlabel_staticremove - Handle a STATICREMOVE message
969 : : * @skb: the NETLINK buffer
970 : : * @info: the Generic NETLINK info block
971 : : *
972 : : * Description:
973 : : * Process a user generated STATICREMOVE message and remove the specified
974 : : * unlabeled connection entry. Returns zero on success, negative values on
975 : : * failure.
976 : : *
977 : : */
978 : 0 : static int netlbl_unlabel_staticremove(struct sk_buff *skb,
979 : : struct genl_info *info)
980 : : {
981 : 0 : int ret_val;
982 : 0 : char *dev_name;
983 : 0 : void *addr;
984 : 0 : void *mask;
985 : 0 : u32 addr_len;
986 : 0 : struct netlbl_audit audit_info;
987 : :
988 : : /* See the note in netlbl_unlabel_staticadd() about not allowing both
989 : : * IPv4 and IPv6 in the same entry. */
990 [ # # # # ]: 0 : if (!info->attrs[NLBL_UNLABEL_A_IFACE] ||
991 [ # # ]: 0 : !((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
992 [ # # ]: 0 : !info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
993 [ # # ]: 0 : (!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
994 [ # # ]: 0 : !info->attrs[NLBL_UNLABEL_A_IPV6MASK])))
995 : : return -EINVAL;
996 : :
997 : 0 : netlbl_netlink_auditinfo(skb, &audit_info);
998 : :
999 : 0 : ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len);
1000 [ # # ]: 0 : if (ret_val != 0)
1001 : : return ret_val;
1002 : 0 : dev_name = nla_data(info->attrs[NLBL_UNLABEL_A_IFACE]);
1003 : :
1004 : 0 : return netlbl_unlhsh_remove(&init_net,
1005 : : dev_name, addr, mask, addr_len,
1006 : : &audit_info);
1007 : : }
1008 : :
1009 : : /**
1010 : : * netlbl_unlabel_staticremovedef - Handle a STATICREMOVEDEF message
1011 : : * @skb: the NETLINK buffer
1012 : : * @info: the Generic NETLINK info block
1013 : : *
1014 : : * Description:
1015 : : * Process a user generated STATICREMOVEDEF message and remove the default
1016 : : * unlabeled connection entry. Returns zero on success, negative values on
1017 : : * failure.
1018 : : *
1019 : : */
1020 : 0 : static int netlbl_unlabel_staticremovedef(struct sk_buff *skb,
1021 : : struct genl_info *info)
1022 : : {
1023 : 0 : int ret_val;
1024 : 0 : void *addr;
1025 : 0 : void *mask;
1026 : 0 : u32 addr_len;
1027 : 0 : struct netlbl_audit audit_info;
1028 : :
1029 : : /* See the note in netlbl_unlabel_staticadd() about not allowing both
1030 : : * IPv4 and IPv6 in the same entry. */
1031 [ # # # # ]: 0 : if (!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
1032 [ # # ]: 0 : !info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
1033 [ # # ]: 0 : (!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
1034 [ # # ]: 0 : !info->attrs[NLBL_UNLABEL_A_IPV6MASK])))
1035 : : return -EINVAL;
1036 : :
1037 : 0 : netlbl_netlink_auditinfo(skb, &audit_info);
1038 : :
1039 : 0 : ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len);
1040 [ # # ]: 0 : if (ret_val != 0)
1041 : : return ret_val;
1042 : :
1043 : 0 : return netlbl_unlhsh_remove(&init_net,
1044 : : NULL, addr, mask, addr_len,
1045 : : &audit_info);
1046 : : }
1047 : :
1048 : :
1049 : : /**
1050 : : * netlbl_unlabel_staticlist_gen - Generate messages for STATICLIST[DEF]
1051 : : * @cmd: command/message
1052 : : * @iface: the interface entry
1053 : : * @addr4: the IPv4 address entry
1054 : : * @addr6: the IPv6 address entry
1055 : : * @arg: the netlbl_unlhsh_walk_arg structure
1056 : : *
1057 : : * Description:
1058 : : * This function is designed to be used to generate a response for a
1059 : : * STATICLIST or STATICLISTDEF message. When called either @addr4 or @addr6
1060 : : * can be specified, not both, the other unspecified entry should be set to
1061 : : * NULL by the caller. Returns the size of the message on success, negative
1062 : : * values on failure.
1063 : : *
1064 : : */
1065 : : static int netlbl_unlabel_staticlist_gen(u32 cmd,
1066 : : const struct netlbl_unlhsh_iface *iface,
1067 : : const struct netlbl_unlhsh_addr4 *addr4,
1068 : : const struct netlbl_unlhsh_addr6 *addr6,
1069 : : void *arg)
1070 : : {
1071 : : int ret_val = -ENOMEM;
1072 : : struct netlbl_unlhsh_walk_arg *cb_arg = arg;
1073 : : struct net_device *dev;
1074 : : void *data;
1075 : : u32 secid;
1076 : : char *secctx;
1077 : : u32 secctx_len;
1078 : :
1079 : : data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
1080 : : cb_arg->seq, &netlbl_unlabel_gnl_family,
1081 : : NLM_F_MULTI, cmd);
1082 : : if (data == NULL)
1083 : : goto list_cb_failure;
1084 : :
1085 : : if (iface->ifindex > 0) {
1086 : : dev = dev_get_by_index(&init_net, iface->ifindex);
1087 : : if (!dev) {
1088 : : ret_val = -ENODEV;
1089 : : goto list_cb_failure;
1090 : : }
1091 : : ret_val = nla_put_string(cb_arg->skb,
1092 : : NLBL_UNLABEL_A_IFACE, dev->name);
1093 : : dev_put(dev);
1094 : : if (ret_val != 0)
1095 : : goto list_cb_failure;
1096 : : }
1097 : :
1098 : : if (addr4) {
1099 : : struct in_addr addr_struct;
1100 : :
1101 : : addr_struct.s_addr = addr4->list.addr;
1102 : : ret_val = nla_put_in_addr(cb_arg->skb,
1103 : : NLBL_UNLABEL_A_IPV4ADDR,
1104 : : addr_struct.s_addr);
1105 : : if (ret_val != 0)
1106 : : goto list_cb_failure;
1107 : :
1108 : : addr_struct.s_addr = addr4->list.mask;
1109 : : ret_val = nla_put_in_addr(cb_arg->skb,
1110 : : NLBL_UNLABEL_A_IPV4MASK,
1111 : : addr_struct.s_addr);
1112 : : if (ret_val != 0)
1113 : : goto list_cb_failure;
1114 : :
1115 : : secid = addr4->secid;
1116 : : } else {
1117 : : ret_val = nla_put_in6_addr(cb_arg->skb,
1118 : : NLBL_UNLABEL_A_IPV6ADDR,
1119 : : &addr6->list.addr);
1120 : : if (ret_val != 0)
1121 : : goto list_cb_failure;
1122 : :
1123 : : ret_val = nla_put_in6_addr(cb_arg->skb,
1124 : : NLBL_UNLABEL_A_IPV6MASK,
1125 : : &addr6->list.mask);
1126 : : if (ret_val != 0)
1127 : : goto list_cb_failure;
1128 : :
1129 : : secid = addr6->secid;
1130 : : }
1131 : :
1132 : : ret_val = security_secid_to_secctx(secid, &secctx, &secctx_len);
1133 : : if (ret_val != 0)
1134 : : goto list_cb_failure;
1135 : : ret_val = nla_put(cb_arg->skb,
1136 : : NLBL_UNLABEL_A_SECCTX,
1137 : : secctx_len,
1138 : : secctx);
1139 : : security_release_secctx(secctx, secctx_len);
1140 : : if (ret_val != 0)
1141 : : goto list_cb_failure;
1142 : :
1143 : : cb_arg->seq++;
1144 : : genlmsg_end(cb_arg->skb, data);
1145 : : return 0;
1146 : :
1147 : : list_cb_failure:
1148 : : genlmsg_cancel(cb_arg->skb, data);
1149 : : return ret_val;
1150 : : }
1151 : :
1152 : : /**
1153 : : * netlbl_unlabel_staticlist - Handle a STATICLIST message
1154 : : * @skb: the NETLINK buffer
1155 : : * @cb: the NETLINK callback
1156 : : *
1157 : : * Description:
1158 : : * Process a user generated STATICLIST message and dump the unlabeled
1159 : : * connection hash table in a form suitable for use in a kernel generated
1160 : : * STATICLIST message. Returns the length of @skb.
1161 : : *
1162 : : */
1163 : 0 : static int netlbl_unlabel_staticlist(struct sk_buff *skb,
1164 : : struct netlink_callback *cb)
1165 : : {
1166 : 0 : struct netlbl_unlhsh_walk_arg cb_arg;
1167 : 0 : u32 skip_bkt = cb->args[0];
1168 : 0 : u32 skip_chain = cb->args[1];
1169 : 0 : u32 iter_bkt;
1170 : 0 : u32 iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0;
1171 : 0 : struct netlbl_unlhsh_iface *iface;
1172 : 0 : struct list_head *iter_list;
1173 : 0 : struct netlbl_af4list *addr4;
1174 : : #if IS_ENABLED(CONFIG_IPV6)
1175 : 0 : struct netlbl_af6list *addr6;
1176 : : #endif
1177 : :
1178 : 0 : cb_arg.nl_cb = cb;
1179 : 0 : cb_arg.skb = skb;
1180 : 0 : cb_arg.seq = cb->nlh->nlmsg_seq;
1181 : :
1182 : 0 : rcu_read_lock();
1183 [ # # ]: 0 : for (iter_bkt = skip_bkt;
1184 [ # # ]: 0 : iter_bkt < rcu_dereference(netlbl_unlhsh)->size;
1185 : 0 : iter_bkt++, iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0) {
1186 : 0 : iter_list = &rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt];
1187 [ # # ]: 0 : list_for_each_entry_rcu(iface, iter_list, list) {
1188 [ # # ]: 0 : if (!iface->valid ||
1189 [ # # ]: 0 : iter_chain++ < skip_chain)
1190 : 0 : continue;
1191 [ # # ]: 0 : netlbl_af4list_foreach_rcu(addr4,
1192 : : &iface->addr4_list) {
1193 [ # # ]: 0 : if (iter_addr4++ < cb->args[2])
1194 : 0 : continue;
1195 [ # # ]: 0 : if (netlbl_unlabel_staticlist_gen(
1196 : : NLBL_UNLABEL_C_STATICLIST,
1197 : : iface,
1198 : 0 : netlbl_unlhsh_addr4_entry(addr4),
1199 : : NULL,
1200 : : &cb_arg) < 0) {
1201 : 0 : iter_addr4--;
1202 : 0 : iter_chain--;
1203 : 0 : goto unlabel_staticlist_return;
1204 : : }
1205 : : }
1206 : : #if IS_ENABLED(CONFIG_IPV6)
1207 [ # # ]: 0 : netlbl_af6list_foreach_rcu(addr6,
1208 : : &iface->addr6_list) {
1209 [ # # ]: 0 : if (iter_addr6++ < cb->args[3])
1210 : 0 : continue;
1211 [ # # ]: 0 : if (netlbl_unlabel_staticlist_gen(
1212 : : NLBL_UNLABEL_C_STATICLIST,
1213 : : iface,
1214 : : NULL,
1215 : 0 : netlbl_unlhsh_addr6_entry(addr6),
1216 : : &cb_arg) < 0) {
1217 : 0 : iter_addr6--;
1218 : 0 : iter_chain--;
1219 : 0 : goto unlabel_staticlist_return;
1220 : : }
1221 : : }
1222 : : #endif /* IPv6 */
1223 : : }
1224 : : }
1225 : :
1226 : 0 : unlabel_staticlist_return:
1227 : 0 : rcu_read_unlock();
1228 : 0 : cb->args[0] = iter_bkt;
1229 : 0 : cb->args[1] = iter_chain;
1230 : 0 : cb->args[2] = iter_addr4;
1231 : 0 : cb->args[3] = iter_addr6;
1232 : 0 : return skb->len;
1233 : : }
1234 : :
1235 : : /**
1236 : : * netlbl_unlabel_staticlistdef - Handle a STATICLISTDEF message
1237 : : * @skb: the NETLINK buffer
1238 : : * @cb: the NETLINK callback
1239 : : *
1240 : : * Description:
1241 : : * Process a user generated STATICLISTDEF message and dump the default
1242 : : * unlabeled connection entry in a form suitable for use in a kernel generated
1243 : : * STATICLISTDEF message. Returns the length of @skb.
1244 : : *
1245 : : */
1246 : 0 : static int netlbl_unlabel_staticlistdef(struct sk_buff *skb,
1247 : : struct netlink_callback *cb)
1248 : : {
1249 : 0 : struct netlbl_unlhsh_walk_arg cb_arg;
1250 : 0 : struct netlbl_unlhsh_iface *iface;
1251 : 0 : u32 iter_addr4 = 0, iter_addr6 = 0;
1252 : 0 : struct netlbl_af4list *addr4;
1253 : : #if IS_ENABLED(CONFIG_IPV6)
1254 : 0 : struct netlbl_af6list *addr6;
1255 : : #endif
1256 : :
1257 : 0 : cb_arg.nl_cb = cb;
1258 : 0 : cb_arg.skb = skb;
1259 : 0 : cb_arg.seq = cb->nlh->nlmsg_seq;
1260 : :
1261 : 0 : rcu_read_lock();
1262 [ # # ]: 0 : iface = rcu_dereference(netlbl_unlhsh_def);
1263 [ # # # # ]: 0 : if (iface == NULL || !iface->valid)
1264 : 0 : goto unlabel_staticlistdef_return;
1265 : :
1266 [ # # ]: 0 : netlbl_af4list_foreach_rcu(addr4, &iface->addr4_list) {
1267 [ # # ]: 0 : if (iter_addr4++ < cb->args[0])
1268 : 0 : continue;
1269 [ # # ]: 0 : if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
1270 : : iface,
1271 : 0 : netlbl_unlhsh_addr4_entry(addr4),
1272 : : NULL,
1273 : : &cb_arg) < 0) {
1274 : 0 : iter_addr4--;
1275 : 0 : goto unlabel_staticlistdef_return;
1276 : : }
1277 : : }
1278 : : #if IS_ENABLED(CONFIG_IPV6)
1279 [ # # ]: 0 : netlbl_af6list_foreach_rcu(addr6, &iface->addr6_list) {
1280 [ # # ]: 0 : if (iter_addr6++ < cb->args[1])
1281 : 0 : continue;
1282 [ # # ]: 0 : if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
1283 : : iface,
1284 : : NULL,
1285 : 0 : netlbl_unlhsh_addr6_entry(addr6),
1286 : : &cb_arg) < 0) {
1287 : 0 : iter_addr6--;
1288 : 0 : goto unlabel_staticlistdef_return;
1289 : : }
1290 : : }
1291 : : #endif /* IPv6 */
1292 : :
1293 : 0 : unlabel_staticlistdef_return:
1294 : 0 : rcu_read_unlock();
1295 : 0 : cb->args[0] = iter_addr4;
1296 : 0 : cb->args[1] = iter_addr6;
1297 : 0 : return skb->len;
1298 : : }
1299 : :
1300 : : /*
1301 : : * NetLabel Generic NETLINK Command Definitions
1302 : : */
1303 : :
1304 : : static const struct genl_ops netlbl_unlabel_genl_ops[] = {
1305 : : {
1306 : : .cmd = NLBL_UNLABEL_C_STATICADD,
1307 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1308 : : .flags = GENL_ADMIN_PERM,
1309 : : .doit = netlbl_unlabel_staticadd,
1310 : : .dumpit = NULL,
1311 : : },
1312 : : {
1313 : : .cmd = NLBL_UNLABEL_C_STATICREMOVE,
1314 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1315 : : .flags = GENL_ADMIN_PERM,
1316 : : .doit = netlbl_unlabel_staticremove,
1317 : : .dumpit = NULL,
1318 : : },
1319 : : {
1320 : : .cmd = NLBL_UNLABEL_C_STATICLIST,
1321 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1322 : : .flags = 0,
1323 : : .doit = NULL,
1324 : : .dumpit = netlbl_unlabel_staticlist,
1325 : : },
1326 : : {
1327 : : .cmd = NLBL_UNLABEL_C_STATICADDDEF,
1328 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1329 : : .flags = GENL_ADMIN_PERM,
1330 : : .doit = netlbl_unlabel_staticadddef,
1331 : : .dumpit = NULL,
1332 : : },
1333 : : {
1334 : : .cmd = NLBL_UNLABEL_C_STATICREMOVEDEF,
1335 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1336 : : .flags = GENL_ADMIN_PERM,
1337 : : .doit = netlbl_unlabel_staticremovedef,
1338 : : .dumpit = NULL,
1339 : : },
1340 : : {
1341 : : .cmd = NLBL_UNLABEL_C_STATICLISTDEF,
1342 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1343 : : .flags = 0,
1344 : : .doit = NULL,
1345 : : .dumpit = netlbl_unlabel_staticlistdef,
1346 : : },
1347 : : {
1348 : : .cmd = NLBL_UNLABEL_C_ACCEPT,
1349 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1350 : : .flags = GENL_ADMIN_PERM,
1351 : : .doit = netlbl_unlabel_accept,
1352 : : .dumpit = NULL,
1353 : : },
1354 : : {
1355 : : .cmd = NLBL_UNLABEL_C_LIST,
1356 : : .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1357 : : .flags = 0,
1358 : : .doit = netlbl_unlabel_list,
1359 : : .dumpit = NULL,
1360 : : },
1361 : : };
1362 : :
1363 : : static struct genl_family netlbl_unlabel_gnl_family __ro_after_init = {
1364 : : .hdrsize = 0,
1365 : : .name = NETLBL_NLTYPE_UNLABELED_NAME,
1366 : : .version = NETLBL_PROTO_VERSION,
1367 : : .maxattr = NLBL_UNLABEL_A_MAX,
1368 : : .policy = netlbl_unlabel_genl_policy,
1369 : : .module = THIS_MODULE,
1370 : : .ops = netlbl_unlabel_genl_ops,
1371 : : .n_ops = ARRAY_SIZE(netlbl_unlabel_genl_ops),
1372 : : };
1373 : :
1374 : : /*
1375 : : * NetLabel Generic NETLINK Protocol Functions
1376 : : */
1377 : :
1378 : : /**
1379 : : * netlbl_unlabel_genl_init - Register the Unlabeled NetLabel component
1380 : : *
1381 : : * Description:
1382 : : * Register the unlabeled packet NetLabel component with the Generic NETLINK
1383 : : * mechanism. Returns zero on success, negative values on failure.
1384 : : *
1385 : : */
1386 : 28 : int __init netlbl_unlabel_genl_init(void)
1387 : : {
1388 : 28 : return genl_register_family(&netlbl_unlabel_gnl_family);
1389 : : }
1390 : :
1391 : : /*
1392 : : * NetLabel KAPI Hooks
1393 : : */
1394 : :
1395 : : static struct notifier_block netlbl_unlhsh_netdev_notifier = {
1396 : : .notifier_call = netlbl_unlhsh_netdev_handler,
1397 : : };
1398 : :
1399 : : /**
1400 : : * netlbl_unlabel_init - Initialize the unlabeled connection hash table
1401 : : * @size: the number of bits to use for the hash buckets
1402 : : *
1403 : : * Description:
1404 : : * Initializes the unlabeled connection hash table and registers a network
1405 : : * device notification handler. This function should only be called by the
1406 : : * NetLabel subsystem itself during initialization. Returns zero on success,
1407 : : * non-zero values on error.
1408 : : *
1409 : : */
1410 : 28 : int __init netlbl_unlabel_init(u32 size)
1411 : : {
1412 : 28 : u32 iter;
1413 : 28 : struct netlbl_unlhsh_tbl *hsh_tbl;
1414 : :
1415 [ + - ]: 28 : if (size == 0)
1416 : : return -EINVAL;
1417 : :
1418 : 28 : hsh_tbl = kmalloc(sizeof(*hsh_tbl), GFP_KERNEL);
1419 [ + - ]: 28 : if (hsh_tbl == NULL)
1420 : : return -ENOMEM;
1421 : 28 : hsh_tbl->size = 1 << size;
1422 : 28 : hsh_tbl->tbl = kcalloc(hsh_tbl->size,
1423 : : sizeof(struct list_head),
1424 : : GFP_KERNEL);
1425 [ - + ]: 28 : if (hsh_tbl->tbl == NULL) {
1426 : 0 : kfree(hsh_tbl);
1427 : 0 : return -ENOMEM;
1428 : : }
1429 [ + + ]: 3612 : for (iter = 0; iter < hsh_tbl->size; iter++)
1430 : 3584 : INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
1431 : :
1432 : 28 : spin_lock(&netlbl_unlhsh_lock);
1433 : 28 : rcu_assign_pointer(netlbl_unlhsh, hsh_tbl);
1434 : 28 : spin_unlock(&netlbl_unlhsh_lock);
1435 : :
1436 : 28 : register_netdevice_notifier(&netlbl_unlhsh_netdev_notifier);
1437 : :
1438 : 28 : return 0;
1439 : : }
1440 : :
1441 : : /**
1442 : : * netlbl_unlabel_getattr - Get the security attributes for an unlabled packet
1443 : : * @skb: the packet
1444 : : * @family: protocol family
1445 : : * @secattr: the security attributes
1446 : : *
1447 : : * Description:
1448 : : * Determine the security attributes, if any, for an unlabled packet and return
1449 : : * them in @secattr. Returns zero on success and negative values on failure.
1450 : : *
1451 : : */
1452 : 0 : int netlbl_unlabel_getattr(const struct sk_buff *skb,
1453 : : u16 family,
1454 : : struct netlbl_lsm_secattr *secattr)
1455 : : {
1456 : 0 : struct netlbl_unlhsh_iface *iface;
1457 : :
1458 : 0 : rcu_read_lock();
1459 : 0 : iface = netlbl_unlhsh_search_iface(skb->skb_iif);
1460 [ # # ]: 0 : if (iface == NULL)
1461 : 0 : iface = rcu_dereference(netlbl_unlhsh_def);
1462 [ # # # # ]: 0 : if (iface == NULL || !iface->valid)
1463 : 0 : goto unlabel_getattr_nolabel;
1464 : :
1465 : : #if IS_ENABLED(CONFIG_IPV6)
1466 : : /* When resolving a fallback label, check the sk_buff version as
1467 : : * it is possible (e.g. SCTP) to have family = PF_INET6 while
1468 : : * receiving ip_hdr(skb)->version = 4.
1469 : : */
1470 [ # # # # ]: 0 : if (family == PF_INET6 && ip_hdr(skb)->version == 4)
1471 : : family = PF_INET;
1472 : : #endif /* IPv6 */
1473 : :
1474 [ # # # ]: 0 : switch (family) {
1475 : : case PF_INET: {
1476 : 0 : struct iphdr *hdr4;
1477 : 0 : struct netlbl_af4list *addr4;
1478 : :
1479 : 0 : hdr4 = ip_hdr(skb);
1480 : 0 : addr4 = netlbl_af4list_search(hdr4->saddr,
1481 : : &iface->addr4_list);
1482 [ # # ]: 0 : if (addr4 == NULL)
1483 : 0 : goto unlabel_getattr_nolabel;
1484 : 0 : secattr->attr.secid = netlbl_unlhsh_addr4_entry(addr4)->secid;
1485 : 0 : break;
1486 : : }
1487 : : #if IS_ENABLED(CONFIG_IPV6)
1488 : : case PF_INET6: {
1489 : 0 : struct ipv6hdr *hdr6;
1490 : 0 : struct netlbl_af6list *addr6;
1491 : :
1492 : 0 : hdr6 = ipv6_hdr(skb);
1493 : 0 : addr6 = netlbl_af6list_search(&hdr6->saddr,
1494 : : &iface->addr6_list);
1495 [ # # ]: 0 : if (addr6 == NULL)
1496 : 0 : goto unlabel_getattr_nolabel;
1497 : 0 : secattr->attr.secid = netlbl_unlhsh_addr6_entry(addr6)->secid;
1498 : 0 : break;
1499 : : }
1500 : : #endif /* IPv6 */
1501 : 0 : default:
1502 : 0 : goto unlabel_getattr_nolabel;
1503 : : }
1504 : 0 : rcu_read_unlock();
1505 : :
1506 : 0 : secattr->flags |= NETLBL_SECATTR_SECID;
1507 : 0 : secattr->type = NETLBL_NLTYPE_UNLABELED;
1508 : 0 : return 0;
1509 : :
1510 : 0 : unlabel_getattr_nolabel:
1511 : 0 : rcu_read_unlock();
1512 [ # # ]: 0 : if (netlabel_unlabel_acceptflg == 0)
1513 : : return -ENOMSG;
1514 : 0 : secattr->type = NETLBL_NLTYPE_UNLABELED;
1515 : 0 : return 0;
1516 : : }
1517 : :
1518 : : /**
1519 : : * netlbl_unlabel_defconf - Set the default config to allow unlabeled packets
1520 : : *
1521 : : * Description:
1522 : : * Set the default NetLabel configuration to allow incoming unlabeled packets
1523 : : * and to send unlabeled network traffic by default.
1524 : : *
1525 : : */
1526 : 28 : int __init netlbl_unlabel_defconf(void)
1527 : : {
1528 : 28 : int ret_val;
1529 : 28 : struct netlbl_dom_map *entry;
1530 : 28 : struct netlbl_audit audit_info;
1531 : :
1532 : : /* Only the kernel is allowed to call this function and the only time
1533 : : * it is called is at bootup before the audit subsystem is reporting
1534 : : * messages so don't worry to much about these values. */
1535 : 28 : security_task_getsecid(current, &audit_info.secid);
1536 : 28 : audit_info.loginuid = GLOBAL_ROOT_UID;
1537 : 28 : audit_info.sessionid = 0;
1538 : :
1539 : 28 : entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1540 [ + - ]: 28 : if (entry == NULL)
1541 : : return -ENOMEM;
1542 : 28 : entry->family = AF_UNSPEC;
1543 : 28 : entry->def.type = NETLBL_NLTYPE_UNLABELED;
1544 : 28 : ret_val = netlbl_domhsh_add_default(entry, &audit_info);
1545 [ + - ]: 28 : if (ret_val != 0)
1546 : : return ret_val;
1547 : :
1548 : 28 : netlbl_unlabel_acceptflg_set(1, &audit_info);
1549 : :
1550 : 28 : return 0;
1551 : : }
|