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