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