diff options
author | Huw Davies <huw@codeweavers.com> | 2016-06-27 15:02:51 -0400 |
---|---|---|
committer | Paul Moore <paul@paul-moore.com> | 2016-06-27 15:02:51 -0400 |
commit | ceba1832b1b2da0149c51de62a847c00bca1677a (patch) | |
tree | 5f03426f96c98a387cc1087865fe99b32410561c | |
parent | 3faa8f982f958961fda68b8d63e682fe77a032d4 (diff) |
calipso: Set the calipso socket label to match the secattr.
CALIPSO is a hop-by-hop IPv6 option. A lot of this patch is based on
the equivalent CISPO code. The main difference is due to manipulating
the options in the hop-by-hop header.
Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
-rw-r--r-- | include/net/ipv6.h | 2 | ||||
-rw-r--r-- | include/net/netlabel.h | 9 | ||||
-rw-r--r-- | include/uapi/linux/in6.h | 1 | ||||
-rw-r--r-- | net/ipv6/calipso.c | 595 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 1 | ||||
-rw-r--r-- | net/netlabel/Kconfig | 1 | ||||
-rw-r--r-- | net/netlabel/netlabel_calipso.c | 64 | ||||
-rw-r--r-- | net/netlabel/netlabel_calipso.h | 5 | ||||
-rw-r--r-- | net/netlabel/netlabel_kapi.c | 58 | ||||
-rw-r--r-- | security/selinux/netlabel.c | 2 |
10 files changed, 728 insertions, 10 deletions
diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 887313d978d0..4e279a83cdd0 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h | |||
@@ -319,6 +319,8 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, | |||
319 | 319 | ||
320 | bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb, | 320 | bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb, |
321 | const struct inet6_skb_parm *opt); | 321 | const struct inet6_skb_parm *opt); |
322 | struct ipv6_txoptions *ipv6_update_options(struct sock *sk, | ||
323 | struct ipv6_txoptions *opt); | ||
322 | 324 | ||
323 | static inline bool ipv6_accept_ra(struct inet6_dev *idev) | 325 | static inline bool ipv6_accept_ra(struct inet6_dev *idev) |
324 | { | 326 | { |
diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 9fc2cab9be98..918a6044c89c 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h | |||
@@ -226,6 +226,9 @@ struct netlbl_lsm_secattr { | |||
226 | * @doi_getdef: returns a reference to a DOI | 226 | * @doi_getdef: returns a reference to a DOI |
227 | * @doi_putdef: releases a reference of a DOI | 227 | * @doi_putdef: releases a reference of a DOI |
228 | * @doi_walk: enumerate the DOI list | 228 | * @doi_walk: enumerate the DOI list |
229 | * @sock_getattr: retrieve the socket's attr | ||
230 | * @sock_setattr: set the socket's attr | ||
231 | * @sock_delattr: remove the socket's attr | ||
229 | * | 232 | * |
230 | * Description: | 233 | * Description: |
231 | * This structure is filled out by the CALIPSO engine and passed | 234 | * This structure is filled out by the CALIPSO engine and passed |
@@ -243,6 +246,12 @@ struct netlbl_calipso_ops { | |||
243 | int (*doi_walk)(u32 *skip_cnt, | 246 | int (*doi_walk)(u32 *skip_cnt, |
244 | int (*callback)(struct calipso_doi *doi_def, void *arg), | 247 | int (*callback)(struct calipso_doi *doi_def, void *arg), |
245 | void *cb_arg); | 248 | void *cb_arg); |
249 | int (*sock_getattr)(struct sock *sk, | ||
250 | struct netlbl_lsm_secattr *secattr); | ||
251 | int (*sock_setattr)(struct sock *sk, | ||
252 | const struct calipso_doi *doi_def, | ||
253 | const struct netlbl_lsm_secattr *secattr); | ||
254 | void (*sock_delattr)(struct sock *sk); | ||
246 | }; | 255 | }; |
247 | 256 | ||
248 | /* | 257 | /* |
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index 318a4828bf98..b39ea4f2e701 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h | |||
@@ -143,6 +143,7 @@ struct in6_flowlabel_req { | |||
143 | #define IPV6_TLV_PAD1 0 | 143 | #define IPV6_TLV_PAD1 0 |
144 | #define IPV6_TLV_PADN 1 | 144 | #define IPV6_TLV_PADN 1 |
145 | #define IPV6_TLV_ROUTERALERT 5 | 145 | #define IPV6_TLV_ROUTERALERT 5 |
146 | #define IPV6_TLV_CALIPSO 7 /* RFC 5570 */ | ||
146 | #define IPV6_TLV_JUMBO 194 | 147 | #define IPV6_TLV_JUMBO 194 |
147 | #define IPV6_TLV_HAO 201 /* home address option */ | 148 | #define IPV6_TLV_HAO 201 /* home address option */ |
148 | 149 | ||
diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index d7df7a4bd32e..959db5268b38 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c | |||
@@ -44,6 +44,24 @@ | |||
44 | #include <linux/atomic.h> | 44 | #include <linux/atomic.h> |
45 | #include <linux/bug.h> | 45 | #include <linux/bug.h> |
46 | #include <asm/unaligned.h> | 46 | #include <asm/unaligned.h> |
47 | #include <linux/crc-ccitt.h> | ||
48 | |||
49 | /* Maximium size of the calipso option including | ||
50 | * the two-byte TLV header. | ||
51 | */ | ||
52 | #define CALIPSO_OPT_LEN_MAX (2 + 252) | ||
53 | |||
54 | /* Size of the minimum calipso option including | ||
55 | * the two-byte TLV header. | ||
56 | */ | ||
57 | #define CALIPSO_HDR_LEN (2 + 8) | ||
58 | |||
59 | /* Maximium size of the calipso option including | ||
60 | * the two-byte TLV header and upto 3 bytes of | ||
61 | * leading pad and 7 bytes of trailing pad. | ||
62 | */ | ||
63 | #define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7) | ||
64 | |||
47 | 65 | ||
48 | /* List of available DOI definitions */ | 66 | /* List of available DOI definitions */ |
49 | static DEFINE_SPINLOCK(calipso_doi_list_lock); | 67 | static DEFINE_SPINLOCK(calipso_doi_list_lock); |
@@ -297,6 +315,580 @@ doi_walk_return: | |||
297 | return ret_val; | 315 | return ret_val; |
298 | } | 316 | } |
299 | 317 | ||
318 | /** | ||
319 | * calipso_map_cat_hton - Perform a category mapping from host to network | ||
320 | * @doi_def: the DOI definition | ||
321 | * @secattr: the security attributes | ||
322 | * @net_cat: the zero'd out category bitmap in network/CALIPSO format | ||
323 | * @net_cat_len: the length of the CALIPSO bitmap in bytes | ||
324 | * | ||
325 | * Description: | ||
326 | * Perform a label mapping to translate a local MLS category bitmap to the | ||
327 | * correct CALIPSO bitmap using the given DOI definition. Returns the minimum | ||
328 | * size in bytes of the network bitmap on success, negative values otherwise. | ||
329 | * | ||
330 | */ | ||
331 | static int calipso_map_cat_hton(const struct calipso_doi *doi_def, | ||
332 | const struct netlbl_lsm_secattr *secattr, | ||
333 | unsigned char *net_cat, | ||
334 | u32 net_cat_len) | ||
335 | { | ||
336 | int spot = -1; | ||
337 | u32 net_spot_max = 0; | ||
338 | u32 net_clen_bits = net_cat_len * 8; | ||
339 | |||
340 | for (;;) { | ||
341 | spot = netlbl_catmap_walk(secattr->attr.mls.cat, | ||
342 | spot + 1); | ||
343 | if (spot < 0) | ||
344 | break; | ||
345 | if (spot >= net_clen_bits) | ||
346 | return -ENOSPC; | ||
347 | netlbl_bitmap_setbit(net_cat, spot, 1); | ||
348 | |||
349 | if (spot > net_spot_max) | ||
350 | net_spot_max = spot; | ||
351 | } | ||
352 | |||
353 | return (net_spot_max / 32 + 1) * 4; | ||
354 | } | ||
355 | |||
356 | /** | ||
357 | * calipso_map_cat_ntoh - Perform a category mapping from network to host | ||
358 | * @doi_def: the DOI definition | ||
359 | * @net_cat: the category bitmap in network/CALIPSO format | ||
360 | * @net_cat_len: the length of the CALIPSO bitmap in bytes | ||
361 | * @secattr: the security attributes | ||
362 | * | ||
363 | * Description: | ||
364 | * Perform a label mapping to translate a CALIPSO bitmap to the correct local | ||
365 | * MLS category bitmap using the given DOI definition. Returns zero on | ||
366 | * success, negative values on failure. | ||
367 | * | ||
368 | */ | ||
369 | static int calipso_map_cat_ntoh(const struct calipso_doi *doi_def, | ||
370 | const unsigned char *net_cat, | ||
371 | u32 net_cat_len, | ||
372 | struct netlbl_lsm_secattr *secattr) | ||
373 | { | ||
374 | int ret_val; | ||
375 | int spot = -1; | ||
376 | u32 net_clen_bits = net_cat_len * 8; | ||
377 | |||
378 | for (;;) { | ||
379 | spot = netlbl_bitmap_walk(net_cat, | ||
380 | net_clen_bits, | ||
381 | spot + 1, | ||
382 | 1); | ||
383 | if (spot < 0) { | ||
384 | if (spot == -2) | ||
385 | return -EFAULT; | ||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, | ||
390 | spot, | ||
391 | GFP_ATOMIC); | ||
392 | if (ret_val != 0) | ||
393 | return ret_val; | ||
394 | } | ||
395 | |||
396 | return -EINVAL; | ||
397 | } | ||
398 | |||
399 | /** | ||
400 | * calipso_pad_write - Writes pad bytes in TLV format | ||
401 | * @buf: the buffer | ||
402 | * @offset: offset from start of buffer to write padding | ||
403 | * @count: number of pad bytes to write | ||
404 | * | ||
405 | * Description: | ||
406 | * Write @count bytes of TLV padding into @buffer starting at offset @offset. | ||
407 | * @count should be less than 8 - see RFC 4942. | ||
408 | * | ||
409 | */ | ||
410 | static int calipso_pad_write(unsigned char *buf, unsigned int offset, | ||
411 | unsigned int count) | ||
412 | { | ||
413 | if (WARN_ON_ONCE(count >= 8)) | ||
414 | return -EINVAL; | ||
415 | |||
416 | switch (count) { | ||
417 | case 0: | ||
418 | break; | ||
419 | case 1: | ||
420 | buf[offset] = IPV6_TLV_PAD1; | ||
421 | break; | ||
422 | default: | ||
423 | buf[offset] = IPV6_TLV_PADN; | ||
424 | buf[offset + 1] = count - 2; | ||
425 | if (count > 2) | ||
426 | memset(buf + offset + 2, 0, count - 2); | ||
427 | break; | ||
428 | } | ||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | /** | ||
433 | * calipso_genopt - Generate a CALIPSO option | ||
434 | * @buf: the option buffer | ||
435 | * @start: offset from which to write | ||
436 | * @buf_len: the size of opt_buf | ||
437 | * @doi_def: the CALIPSO DOI to use | ||
438 | * @secattr: the security attributes | ||
439 | * | ||
440 | * Description: | ||
441 | * Generate a CALIPSO option using the DOI definition and security attributes | ||
442 | * passed to the function. This also generates upto three bytes of leading | ||
443 | * padding that ensures that the option is 4n + 2 aligned. It returns the | ||
444 | * number of bytes written (including any initial padding). | ||
445 | */ | ||
446 | static int calipso_genopt(unsigned char *buf, u32 start, u32 buf_len, | ||
447 | const struct calipso_doi *doi_def, | ||
448 | const struct netlbl_lsm_secattr *secattr) | ||
449 | { | ||
450 | int ret_val; | ||
451 | u32 len, pad; | ||
452 | u16 crc; | ||
453 | static const unsigned char padding[4] = {2, 1, 0, 3}; | ||
454 | unsigned char *calipso; | ||
455 | |||
456 | /* CALIPSO has 4n + 2 alignment */ | ||
457 | pad = padding[start & 3]; | ||
458 | if (buf_len <= start + pad + CALIPSO_HDR_LEN) | ||
459 | return -ENOSPC; | ||
460 | |||
461 | if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0) | ||
462 | return -EPERM; | ||
463 | |||
464 | len = CALIPSO_HDR_LEN; | ||
465 | |||
466 | if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { | ||
467 | ret_val = calipso_map_cat_hton(doi_def, | ||
468 | secattr, | ||
469 | buf + start + pad + len, | ||
470 | buf_len - start - pad - len); | ||
471 | if (ret_val < 0) | ||
472 | return ret_val; | ||
473 | len += ret_val; | ||
474 | } | ||
475 | |||
476 | calipso_pad_write(buf, start, pad); | ||
477 | calipso = buf + start + pad; | ||
478 | |||
479 | calipso[0] = IPV6_TLV_CALIPSO; | ||
480 | calipso[1] = len - 2; | ||
481 | *(__be32 *)(calipso + 2) = htonl(doi_def->doi); | ||
482 | calipso[6] = (len - CALIPSO_HDR_LEN) / 4; | ||
483 | calipso[7] = secattr->attr.mls.lvl, | ||
484 | crc = ~crc_ccitt(0xffff, calipso, len); | ||
485 | calipso[8] = crc & 0xff; | ||
486 | calipso[9] = (crc >> 8) & 0xff; | ||
487 | return pad + len; | ||
488 | } | ||
489 | |||
490 | /* Hop-by-hop hdr helper functions | ||
491 | */ | ||
492 | |||
493 | /** | ||
494 | * calipso_opt_update - Replaces socket's hop options with a new set | ||
495 | * @sk: the socket | ||
496 | * @hop: new hop options | ||
497 | * | ||
498 | * Description: | ||
499 | * Replaces @sk's hop options with @hop. @hop may be NULL to leave | ||
500 | * the socket with no hop options. | ||
501 | * | ||
502 | */ | ||
503 | static int calipso_opt_update(struct sock *sk, struct ipv6_opt_hdr *hop) | ||
504 | { | ||
505 | struct ipv6_txoptions *old = txopt_get(inet6_sk(sk)), *txopts; | ||
506 | |||
507 | txopts = ipv6_renew_options_kern(sk, old, IPV6_HOPOPTS, | ||
508 | hop, hop ? ipv6_optlen(hop) : 0); | ||
509 | txopt_put(old); | ||
510 | if (IS_ERR(txopts)) | ||
511 | return PTR_ERR(txopts); | ||
512 | |||
513 | txopts = ipv6_update_options(sk, txopts); | ||
514 | if (txopts) { | ||
515 | atomic_sub(txopts->tot_len, &sk->sk_omem_alloc); | ||
516 | txopt_put(txopts); | ||
517 | } | ||
518 | |||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | /** | ||
523 | * calipso_tlv_len - Returns the length of the TLV | ||
524 | * @opt: the option header | ||
525 | * @offset: offset of the TLV within the header | ||
526 | * | ||
527 | * Description: | ||
528 | * Returns the length of the TLV option at offset @offset within | ||
529 | * the option header @opt. Checks that the entire TLV fits inside | ||
530 | * the option header, returns a negative value if this is not the case. | ||
531 | */ | ||
532 | static int calipso_tlv_len(struct ipv6_opt_hdr *opt, unsigned int offset) | ||
533 | { | ||
534 | unsigned char *tlv = (unsigned char *)opt; | ||
535 | unsigned int opt_len = ipv6_optlen(opt), tlv_len; | ||
536 | |||
537 | if (offset < sizeof(*opt) || offset >= opt_len) | ||
538 | return -EINVAL; | ||
539 | if (tlv[offset] == IPV6_TLV_PAD1) | ||
540 | return 1; | ||
541 | if (offset + 1 >= opt_len) | ||
542 | return -EINVAL; | ||
543 | tlv_len = tlv[offset + 1] + 2; | ||
544 | if (offset + tlv_len > opt_len) | ||
545 | return -EINVAL; | ||
546 | return tlv_len; | ||
547 | } | ||
548 | |||
549 | /** | ||
550 | * calipso_opt_find - Finds the CALIPSO option in an IPv6 hop options header | ||
551 | * @hop: the hop options header | ||
552 | * @start: on return holds the offset of any leading padding | ||
553 | * @end: on return holds the offset of the first non-pad TLV after CALIPSO | ||
554 | * | ||
555 | * Description: | ||
556 | * Finds the space occupied by a CALIPSO option (including any leading and | ||
557 | * trailing padding). | ||
558 | * | ||
559 | * If a CALIPSO option exists set @start and @end to the | ||
560 | * offsets within @hop of the start of padding before the first | ||
561 | * CALIPSO option and the end of padding after the first CALIPSO | ||
562 | * option. In this case the function returns 0. | ||
563 | * | ||
564 | * In the absence of a CALIPSO option, @start and @end will be | ||
565 | * set to the start and end of any trailing padding in the header. | ||
566 | * This is useful when appending a new option, as the caller may want | ||
567 | * to overwrite some of this padding. In this case the function will | ||
568 | * return -ENOENT. | ||
569 | */ | ||
570 | static int calipso_opt_find(struct ipv6_opt_hdr *hop, unsigned int *start, | ||
571 | unsigned int *end) | ||
572 | { | ||
573 | int ret_val = -ENOENT, tlv_len; | ||
574 | unsigned int opt_len, offset, offset_s = 0, offset_e = 0; | ||
575 | unsigned char *opt = (unsigned char *)hop; | ||
576 | |||
577 | opt_len = ipv6_optlen(hop); | ||
578 | offset = sizeof(*hop); | ||
579 | |||
580 | while (offset < opt_len) { | ||
581 | tlv_len = calipso_tlv_len(hop, offset); | ||
582 | if (tlv_len < 0) | ||
583 | return tlv_len; | ||
584 | |||
585 | switch (opt[offset]) { | ||
586 | case IPV6_TLV_PAD1: | ||
587 | case IPV6_TLV_PADN: | ||
588 | if (offset_e) | ||
589 | offset_e = offset; | ||
590 | break; | ||
591 | case IPV6_TLV_CALIPSO: | ||
592 | ret_val = 0; | ||
593 | offset_e = offset; | ||
594 | break; | ||
595 | default: | ||
596 | if (offset_e == 0) | ||
597 | offset_s = offset; | ||
598 | else | ||
599 | goto out; | ||
600 | } | ||
601 | offset += tlv_len; | ||
602 | } | ||
603 | |||
604 | out: | ||
605 | if (offset_s) | ||
606 | *start = offset_s + calipso_tlv_len(hop, offset_s); | ||
607 | else | ||
608 | *start = sizeof(*hop); | ||
609 | if (offset_e) | ||
610 | *end = offset_e + calipso_tlv_len(hop, offset_e); | ||
611 | else | ||
612 | *end = opt_len; | ||
613 | |||
614 | return ret_val; | ||
615 | } | ||
616 | |||
617 | /** | ||
618 | * calipso_opt_insert - Inserts a CALIPSO option into an IPv6 hop opt hdr | ||
619 | * @hop: the original hop options header | ||
620 | * @doi_def: the CALIPSO DOI to use | ||
621 | * @secattr: the specific security attributes of the socket | ||
622 | * | ||
623 | * Description: | ||
624 | * Creates a new hop options header based on @hop with a | ||
625 | * CALIPSO option added to it. If @hop already contains a CALIPSO | ||
626 | * option this is overwritten, otherwise the new option is appended | ||
627 | * after any existing options. If @hop is NULL then the new header | ||
628 | * will contain just the CALIPSO option and any needed padding. | ||
629 | * | ||
630 | */ | ||
631 | static struct ipv6_opt_hdr * | ||
632 | calipso_opt_insert(struct ipv6_opt_hdr *hop, | ||
633 | const struct calipso_doi *doi_def, | ||
634 | const struct netlbl_lsm_secattr *secattr) | ||
635 | { | ||
636 | unsigned int start, end, buf_len, pad, hop_len; | ||
637 | struct ipv6_opt_hdr *new; | ||
638 | int ret_val; | ||
639 | |||
640 | if (hop) { | ||
641 | hop_len = ipv6_optlen(hop); | ||
642 | ret_val = calipso_opt_find(hop, &start, &end); | ||
643 | if (ret_val && ret_val != -ENOENT) | ||
644 | return ERR_PTR(ret_val); | ||
645 | } else { | ||
646 | hop_len = 0; | ||
647 | start = sizeof(*hop); | ||
648 | end = 0; | ||
649 | } | ||
650 | |||
651 | buf_len = hop_len + start - end + CALIPSO_OPT_LEN_MAX_WITH_PAD; | ||
652 | new = kzalloc(buf_len, GFP_ATOMIC); | ||
653 | if (!new) | ||
654 | return ERR_PTR(-ENOMEM); | ||
655 | |||
656 | if (start > sizeof(*hop)) | ||
657 | memcpy(new, hop, start); | ||
658 | ret_val = calipso_genopt((unsigned char *)new, start, buf_len, doi_def, | ||
659 | secattr); | ||
660 | if (ret_val < 0) | ||
661 | return ERR_PTR(ret_val); | ||
662 | |||
663 | buf_len = start + ret_val; | ||
664 | /* At this point buf_len aligns to 4n, so (buf_len & 4) pads to 8n */ | ||
665 | pad = ((buf_len & 4) + (end & 7)) & 7; | ||
666 | calipso_pad_write((unsigned char *)new, buf_len, pad); | ||
667 | buf_len += pad; | ||
668 | |||
669 | if (end != hop_len) { | ||
670 | memcpy((char *)new + buf_len, (char *)hop + end, hop_len - end); | ||
671 | buf_len += hop_len - end; | ||
672 | } | ||
673 | new->nexthdr = 0; | ||
674 | new->hdrlen = buf_len / 8 - 1; | ||
675 | |||
676 | return new; | ||
677 | } | ||
678 | |||
679 | /** | ||
680 | * calipso_opt_del - Removes the CALIPSO option from an option header | ||
681 | * @hop: the original header | ||
682 | * @new: the new header | ||
683 | * | ||
684 | * Description: | ||
685 | * Creates a new header based on @hop without any CALIPSO option. If @hop | ||
686 | * doesn't contain a CALIPSO option it returns -ENOENT. If @hop contains | ||
687 | * no other non-padding options, it returns zero with @new set to NULL. | ||
688 | * Otherwise it returns zero, creates a new header without the CALIPSO | ||
689 | * option (and removing as much padding as possible) and returns with | ||
690 | * @new set to that header. | ||
691 | * | ||
692 | */ | ||
693 | static int calipso_opt_del(struct ipv6_opt_hdr *hop, | ||
694 | struct ipv6_opt_hdr **new) | ||
695 | { | ||
696 | int ret_val; | ||
697 | unsigned int start, end, delta, pad, hop_len; | ||
698 | |||
699 | ret_val = calipso_opt_find(hop, &start, &end); | ||
700 | if (ret_val) | ||
701 | return ret_val; | ||
702 | |||
703 | hop_len = ipv6_optlen(hop); | ||
704 | if (start == sizeof(*hop) && end == hop_len) { | ||
705 | /* There's no other option in the header so return NULL */ | ||
706 | *new = NULL; | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | delta = (end - start) & ~7; | ||
711 | *new = kzalloc(hop_len - delta, GFP_ATOMIC); | ||
712 | if (!*new) | ||
713 | return -ENOMEM; | ||
714 | |||
715 | memcpy(*new, hop, start); | ||
716 | (*new)->hdrlen -= delta / 8; | ||
717 | pad = (end - start) & 7; | ||
718 | calipso_pad_write((unsigned char *)*new, start, pad); | ||
719 | if (end != hop_len) | ||
720 | memcpy((char *)*new + start + pad, (char *)hop + end, | ||
721 | hop_len - end); | ||
722 | |||
723 | return 0; | ||
724 | } | ||
725 | |||
726 | /** | ||
727 | * calipso_opt_getattr - Get the security attributes from a memory block | ||
728 | * @calipso: the CALIPSO option | ||
729 | * @secattr: the security attributes | ||
730 | * | ||
731 | * Description: | ||
732 | * Inspect @calipso and return the security attributes in @secattr. | ||
733 | * Returns zero on success and negative values on failure. | ||
734 | * | ||
735 | */ | ||
736 | static int calipso_opt_getattr(const unsigned char *calipso, | ||
737 | struct netlbl_lsm_secattr *secattr) | ||
738 | { | ||
739 | int ret_val = -ENOMSG; | ||
740 | u32 doi, len = calipso[1], cat_len = calipso[6] * 4; | ||
741 | struct calipso_doi *doi_def; | ||
742 | |||
743 | if (cat_len + 8 > len) | ||
744 | return -EINVAL; | ||
745 | |||
746 | doi = get_unaligned_be32(calipso + 2); | ||
747 | rcu_read_lock(); | ||
748 | doi_def = calipso_doi_search(doi); | ||
749 | if (!doi_def) | ||
750 | goto getattr_return; | ||
751 | |||
752 | secattr->attr.mls.lvl = calipso[7]; | ||
753 | secattr->flags |= NETLBL_SECATTR_MLS_LVL; | ||
754 | |||
755 | if (cat_len) { | ||
756 | ret_val = calipso_map_cat_ntoh(doi_def, | ||
757 | calipso + 10, | ||
758 | cat_len, | ||
759 | secattr); | ||
760 | if (ret_val != 0) { | ||
761 | netlbl_catmap_free(secattr->attr.mls.cat); | ||
762 | goto getattr_return; | ||
763 | } | ||
764 | |||
765 | secattr->flags |= NETLBL_SECATTR_MLS_CAT; | ||
766 | } | ||
767 | |||
768 | secattr->type = NETLBL_NLTYPE_CALIPSO; | ||
769 | |||
770 | getattr_return: | ||
771 | rcu_read_unlock(); | ||
772 | return ret_val; | ||
773 | } | ||
774 | |||
775 | /* sock functions. | ||
776 | */ | ||
777 | |||
778 | /** | ||
779 | * calipso_sock_getattr - Get the security attributes from a sock | ||
780 | * @sk: the sock | ||
781 | * @secattr: the security attributes | ||
782 | * | ||
783 | * Description: | ||
784 | * Query @sk to see if there is a CALIPSO option attached to the sock and if | ||
785 | * there is return the CALIPSO security attributes in @secattr. This function | ||
786 | * requires that @sk be locked, or privately held, but it does not do any | ||
787 | * locking itself. Returns zero on success and negative values on failure. | ||
788 | * | ||
789 | */ | ||
790 | static int calipso_sock_getattr(struct sock *sk, | ||
791 | struct netlbl_lsm_secattr *secattr) | ||
792 | { | ||
793 | struct ipv6_opt_hdr *hop; | ||
794 | int opt_len, len, ret_val = -ENOMSG, offset; | ||
795 | unsigned char *opt; | ||
796 | struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); | ||
797 | |||
798 | if (!txopts || !txopts->hopopt) | ||
799 | goto done; | ||
800 | |||
801 | hop = txopts->hopopt; | ||
802 | opt = (unsigned char *)hop; | ||
803 | opt_len = ipv6_optlen(hop); | ||
804 | offset = sizeof(*hop); | ||
805 | while (offset < opt_len) { | ||
806 | len = calipso_tlv_len(hop, offset); | ||
807 | if (len < 0) { | ||
808 | ret_val = len; | ||
809 | goto done; | ||
810 | } | ||
811 | switch (opt[offset]) { | ||
812 | case IPV6_TLV_CALIPSO: | ||
813 | if (len < CALIPSO_HDR_LEN) | ||
814 | ret_val = -EINVAL; | ||
815 | else | ||
816 | ret_val = calipso_opt_getattr(&opt[offset], | ||
817 | secattr); | ||
818 | goto done; | ||
819 | default: | ||
820 | offset += len; | ||
821 | break; | ||
822 | } | ||
823 | } | ||
824 | done: | ||
825 | txopt_put(txopts); | ||
826 | return ret_val; | ||
827 | } | ||
828 | |||
829 | /** | ||
830 | * calipso_sock_setattr - Add a CALIPSO option to a socket | ||
831 | * @sk: the socket | ||
832 | * @doi_def: the CALIPSO DOI to use | ||
833 | * @secattr: the specific security attributes of the socket | ||
834 | * | ||
835 | * Description: | ||
836 | * Set the CALIPSO option on the given socket using the DOI definition and | ||
837 | * security attributes passed to the function. This function requires | ||
838 | * exclusive access to @sk, which means it either needs to be in the | ||
839 | * process of being created or locked. Returns zero on success and negative | ||
840 | * values on failure. | ||
841 | * | ||
842 | */ | ||
843 | static int calipso_sock_setattr(struct sock *sk, | ||
844 | const struct calipso_doi *doi_def, | ||
845 | const struct netlbl_lsm_secattr *secattr) | ||
846 | { | ||
847 | int ret_val; | ||
848 | struct ipv6_opt_hdr *old, *new; | ||
849 | struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); | ||
850 | |||
851 | old = NULL; | ||
852 | if (txopts) | ||
853 | old = txopts->hopopt; | ||
854 | |||
855 | new = calipso_opt_insert(old, doi_def, secattr); | ||
856 | txopt_put(txopts); | ||
857 | if (IS_ERR(new)) | ||
858 | return PTR_ERR(new); | ||
859 | |||
860 | ret_val = calipso_opt_update(sk, new); | ||
861 | |||
862 | kfree(new); | ||
863 | return ret_val; | ||
864 | } | ||
865 | |||
866 | /** | ||
867 | * calipso_sock_delattr - Delete the CALIPSO option from a socket | ||
868 | * @sk: the socket | ||
869 | * | ||
870 | * Description: | ||
871 | * Removes the CALIPSO option from a socket, if present. | ||
872 | * | ||
873 | */ | ||
874 | static void calipso_sock_delattr(struct sock *sk) | ||
875 | { | ||
876 | struct ipv6_opt_hdr *new_hop; | ||
877 | struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); | ||
878 | |||
879 | if (!txopts || !txopts->hopopt) | ||
880 | goto done; | ||
881 | |||
882 | if (calipso_opt_del(txopts->hopopt, &new_hop)) | ||
883 | goto done; | ||
884 | |||
885 | calipso_opt_update(sk, new_hop); | ||
886 | kfree(new_hop); | ||
887 | |||
888 | done: | ||
889 | txopt_put(txopts); | ||
890 | } | ||
891 | |||
300 | static const struct netlbl_calipso_ops ops = { | 892 | static const struct netlbl_calipso_ops ops = { |
301 | .doi_add = calipso_doi_add, | 893 | .doi_add = calipso_doi_add, |
302 | .doi_free = calipso_doi_free, | 894 | .doi_free = calipso_doi_free, |
@@ -304,6 +896,9 @@ static const struct netlbl_calipso_ops ops = { | |||
304 | .doi_getdef = calipso_doi_getdef, | 896 | .doi_getdef = calipso_doi_getdef, |
305 | .doi_putdef = calipso_doi_putdef, | 897 | .doi_putdef = calipso_doi_putdef, |
306 | .doi_walk = calipso_doi_walk, | 898 | .doi_walk = calipso_doi_walk, |
899 | .sock_getattr = calipso_sock_getattr, | ||
900 | .sock_setattr = calipso_sock_setattr, | ||
901 | .sock_delattr = calipso_sock_delattr, | ||
307 | }; | 902 | }; |
308 | 903 | ||
309 | /** | 904 | /** |
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 4449ad1f8114..8a80d59bed34 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -98,7 +98,6 @@ int ip6_ra_control(struct sock *sk, int sel) | |||
98 | return 0; | 98 | return 0; |
99 | } | 99 | } |
100 | 100 | ||
101 | static | ||
102 | struct ipv6_txoptions *ipv6_update_options(struct sock *sk, | 101 | struct ipv6_txoptions *ipv6_update_options(struct sock *sk, |
103 | struct ipv6_txoptions *opt) | 102 | struct ipv6_txoptions *opt) |
104 | { | 103 | { |
diff --git a/net/netlabel/Kconfig b/net/netlabel/Kconfig index 56958c85f2b4..d9eaa30ffe3f 100644 --- a/net/netlabel/Kconfig +++ b/net/netlabel/Kconfig | |||
@@ -5,6 +5,7 @@ | |||
5 | config NETLABEL | 5 | config NETLABEL |
6 | bool "NetLabel subsystem support" | 6 | bool "NetLabel subsystem support" |
7 | depends on SECURITY | 7 | depends on SECURITY |
8 | select CRC_CCITT if IPV6 | ||
8 | default n | 9 | default n |
9 | ---help--- | 10 | ---help--- |
10 | NetLabel provides support for explicit network packet labeling | 11 | NetLabel provides support for explicit network packet labeling |
diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 2857673e8802..6f9c6589a022 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c | |||
@@ -514,3 +514,67 @@ int calipso_doi_walk(u32 *skip_cnt, | |||
514 | ret_val = ops->doi_walk(skip_cnt, callback, cb_arg); | 514 | ret_val = ops->doi_walk(skip_cnt, callback, cb_arg); |
515 | return ret_val; | 515 | return ret_val; |
516 | } | 516 | } |
517 | |||
518 | /** | ||
519 | * calipso_sock_getattr - Get the security attributes from a sock | ||
520 | * @sk: the sock | ||
521 | * @secattr: the security attributes | ||
522 | * | ||
523 | * Description: | ||
524 | * Query @sk to see if there is a CALIPSO option attached to the sock and if | ||
525 | * there is return the CALIPSO security attributes in @secattr. This function | ||
526 | * requires that @sk be locked, or privately held, but it does not do any | ||
527 | * locking itself. Returns zero on success and negative values on failure. | ||
528 | * | ||
529 | */ | ||
530 | int calipso_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) | ||
531 | { | ||
532 | int ret_val = -ENOMSG; | ||
533 | const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); | ||
534 | |||
535 | if (ops) | ||
536 | ret_val = ops->sock_getattr(sk, secattr); | ||
537 | return ret_val; | ||
538 | } | ||
539 | |||
540 | /** | ||
541 | * calipso_sock_setattr - Add a CALIPSO option to a socket | ||
542 | * @sk: the socket | ||
543 | * @doi_def: the CALIPSO DOI to use | ||
544 | * @secattr: the specific security attributes of the socket | ||
545 | * | ||
546 | * Description: | ||
547 | * Set the CALIPSO option on the given socket using the DOI definition and | ||
548 | * security attributes passed to the function. This function requires | ||
549 | * exclusive access to @sk, which means it either needs to be in the | ||
550 | * process of being created or locked. Returns zero on success and negative | ||
551 | * values on failure. | ||
552 | * | ||
553 | */ | ||
554 | int calipso_sock_setattr(struct sock *sk, | ||
555 | const struct calipso_doi *doi_def, | ||
556 | const struct netlbl_lsm_secattr *secattr) | ||
557 | { | ||
558 | int ret_val = -ENOMSG; | ||
559 | const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); | ||
560 | |||
561 | if (ops) | ||
562 | ret_val = ops->sock_setattr(sk, doi_def, secattr); | ||
563 | return ret_val; | ||
564 | } | ||
565 | |||
566 | /** | ||
567 | * calipso_sock_delattr - Delete the CALIPSO option from a socket | ||
568 | * @sk: the socket | ||
569 | * | ||
570 | * Description: | ||
571 | * Removes the CALIPSO option from a socket, if present. | ||
572 | * | ||
573 | */ | ||
574 | void calipso_sock_delattr(struct sock *sk) | ||
575 | { | ||
576 | const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); | ||
577 | |||
578 | if (ops) | ||
579 | ops->sock_delattr(sk); | ||
580 | } | ||
diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h index ed78554ba472..49bc116705c4 100644 --- a/net/netlabel/netlabel_calipso.h +++ b/net/netlabel/netlabel_calipso.h | |||
@@ -128,5 +128,10 @@ void calipso_doi_putdef(struct calipso_doi *doi_def); | |||
128 | int calipso_doi_walk(u32 *skip_cnt, | 128 | int calipso_doi_walk(u32 *skip_cnt, |
129 | int (*callback)(struct calipso_doi *doi_def, void *arg), | 129 | int (*callback)(struct calipso_doi *doi_def, void *arg), |
130 | void *cb_arg); | 130 | void *cb_arg); |
131 | int calipso_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr); | ||
132 | int calipso_sock_setattr(struct sock *sk, | ||
133 | const struct calipso_doi *doi_def, | ||
134 | const struct netlbl_lsm_secattr *secattr); | ||
135 | void calipso_sock_delattr(struct sock *sk); | ||
131 | 136 | ||
132 | #endif | 137 | #endif |
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 54f13a33b52c..00bab51c291e 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c | |||
@@ -37,12 +37,14 @@ | |||
37 | #include <net/ipv6.h> | 37 | #include <net/ipv6.h> |
38 | #include <net/netlabel.h> | 38 | #include <net/netlabel.h> |
39 | #include <net/cipso_ipv4.h> | 39 | #include <net/cipso_ipv4.h> |
40 | #include <net/calipso.h> | ||
40 | #include <asm/bug.h> | 41 | #include <asm/bug.h> |
41 | #include <linux/atomic.h> | 42 | #include <linux/atomic.h> |
42 | 43 | ||
43 | #include "netlabel_domainhash.h" | 44 | #include "netlabel_domainhash.h" |
44 | #include "netlabel_unlabeled.h" | 45 | #include "netlabel_unlabeled.h" |
45 | #include "netlabel_cipso_v4.h" | 46 | #include "netlabel_cipso_v4.h" |
47 | #include "netlabel_calipso.h" | ||
46 | #include "netlabel_user.h" | 48 | #include "netlabel_user.h" |
47 | #include "netlabel_mgmt.h" | 49 | #include "netlabel_mgmt.h" |
48 | #include "netlabel_addrlist.h" | 50 | #include "netlabel_addrlist.h" |
@@ -521,6 +523,7 @@ int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset) | |||
521 | 523 | ||
522 | return -ENOENT; | 524 | return -ENOENT; |
523 | } | 525 | } |
526 | EXPORT_SYMBOL(netlbl_catmap_walk); | ||
524 | 527 | ||
525 | /** | 528 | /** |
526 | * netlbl_catmap_walkrng - Find the end of a string of set bits | 529 | * netlbl_catmap_walkrng - Find the end of a string of set bits |
@@ -656,6 +659,7 @@ int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, | |||
656 | 659 | ||
657 | return 0; | 660 | return 0; |
658 | } | 661 | } |
662 | EXPORT_SYMBOL(netlbl_catmap_setbit); | ||
659 | 663 | ||
660 | /** | 664 | /** |
661 | * netlbl_catmap_setrng - Set a range of bits in a LSM secattr catmap | 665 | * netlbl_catmap_setrng - Set a range of bits in a LSM secattr catmap |
@@ -870,9 +874,21 @@ int netlbl_sock_setattr(struct sock *sk, | |||
870 | break; | 874 | break; |
871 | #if IS_ENABLED(CONFIG_IPV6) | 875 | #if IS_ENABLED(CONFIG_IPV6) |
872 | case AF_INET6: | 876 | case AF_INET6: |
873 | /* since we don't support any IPv6 labeling protocols right | 877 | switch (dom_entry->def.type) { |
874 | * now we can optimize everything away until we do */ | 878 | case NETLBL_NLTYPE_ADDRSELECT: |
875 | ret_val = 0; | 879 | ret_val = -EDESTADDRREQ; |
880 | break; | ||
881 | case NETLBL_NLTYPE_CALIPSO: | ||
882 | ret_val = calipso_sock_setattr(sk, | ||
883 | dom_entry->def.calipso, | ||
884 | secattr); | ||
885 | break; | ||
886 | case NETLBL_NLTYPE_UNLABELED: | ||
887 | ret_val = 0; | ||
888 | break; | ||
889 | default: | ||
890 | ret_val = -ENOENT; | ||
891 | } | ||
876 | break; | 892 | break; |
877 | #endif /* IPv6 */ | 893 | #endif /* IPv6 */ |
878 | default: | 894 | default: |
@@ -899,6 +915,11 @@ void netlbl_sock_delattr(struct sock *sk) | |||
899 | case AF_INET: | 915 | case AF_INET: |
900 | cipso_v4_sock_delattr(sk); | 916 | cipso_v4_sock_delattr(sk); |
901 | break; | 917 | break; |
918 | #if IS_ENABLED(CONFIG_IPV6) | ||
919 | case AF_INET6: | ||
920 | calipso_sock_delattr(sk); | ||
921 | break; | ||
922 | #endif /* IPv6 */ | ||
902 | } | 923 | } |
903 | } | 924 | } |
904 | 925 | ||
@@ -925,7 +946,7 @@ int netlbl_sock_getattr(struct sock *sk, | |||
925 | break; | 946 | break; |
926 | #if IS_ENABLED(CONFIG_IPV6) | 947 | #if IS_ENABLED(CONFIG_IPV6) |
927 | case AF_INET6: | 948 | case AF_INET6: |
928 | ret_val = -ENOMSG; | 949 | ret_val = calipso_sock_getattr(sk, secattr); |
929 | break; | 950 | break; |
930 | #endif /* IPv6 */ | 951 | #endif /* IPv6 */ |
931 | default: | 952 | default: |
@@ -953,6 +974,9 @@ int netlbl_conn_setattr(struct sock *sk, | |||
953 | { | 974 | { |
954 | int ret_val; | 975 | int ret_val; |
955 | struct sockaddr_in *addr4; | 976 | struct sockaddr_in *addr4; |
977 | #if IS_ENABLED(CONFIG_IPV6) | ||
978 | struct sockaddr_in6 *addr6; | ||
979 | #endif | ||
956 | struct netlbl_dommap_def *entry; | 980 | struct netlbl_dommap_def *entry; |
957 | 981 | ||
958 | rcu_read_lock(); | 982 | rcu_read_lock(); |
@@ -973,7 +997,7 @@ int netlbl_conn_setattr(struct sock *sk, | |||
973 | case NETLBL_NLTYPE_UNLABELED: | 997 | case NETLBL_NLTYPE_UNLABELED: |
974 | /* just delete the protocols we support for right now | 998 | /* just delete the protocols we support for right now |
975 | * but we could remove other protocols if needed */ | 999 | * but we could remove other protocols if needed */ |
976 | cipso_v4_sock_delattr(sk); | 1000 | netlbl_sock_delattr(sk); |
977 | ret_val = 0; | 1001 | ret_val = 0; |
978 | break; | 1002 | break; |
979 | default: | 1003 | default: |
@@ -982,9 +1006,27 @@ int netlbl_conn_setattr(struct sock *sk, | |||
982 | break; | 1006 | break; |
983 | #if IS_ENABLED(CONFIG_IPV6) | 1007 | #if IS_ENABLED(CONFIG_IPV6) |
984 | case AF_INET6: | 1008 | case AF_INET6: |
985 | /* since we don't support any IPv6 labeling protocols right | 1009 | addr6 = (struct sockaddr_in6 *)addr; |
986 | * now we can optimize everything away until we do */ | 1010 | entry = netlbl_domhsh_getentry_af6(secattr->domain, |
987 | ret_val = 0; | 1011 | &addr6->sin6_addr); |
1012 | if (entry == NULL) { | ||
1013 | ret_val = -ENOENT; | ||
1014 | goto conn_setattr_return; | ||
1015 | } | ||
1016 | switch (entry->type) { | ||
1017 | case NETLBL_NLTYPE_CALIPSO: | ||
1018 | ret_val = calipso_sock_setattr(sk, | ||
1019 | entry->calipso, secattr); | ||
1020 | break; | ||
1021 | case NETLBL_NLTYPE_UNLABELED: | ||
1022 | /* just delete the protocols we support for right now | ||
1023 | * but we could remove other protocols if needed */ | ||
1024 | netlbl_sock_delattr(sk); | ||
1025 | ret_val = 0; | ||
1026 | break; | ||
1027 | default: | ||
1028 | ret_val = -ENOENT; | ||
1029 | } | ||
988 | break; | 1030 | break; |
989 | #endif /* IPv6 */ | 1031 | #endif /* IPv6 */ |
990 | default: | 1032 | default: |
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 1f989a539fd4..5470f32eca54 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c | |||
@@ -333,7 +333,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family) | |||
333 | struct sk_security_struct *sksec = sk->sk_security; | 333 | struct sk_security_struct *sksec = sk->sk_security; |
334 | struct netlbl_lsm_secattr *secattr; | 334 | struct netlbl_lsm_secattr *secattr; |
335 | 335 | ||
336 | if (family != PF_INET) | 336 | if (family != PF_INET && family != PF_INET6) |
337 | return 0; | 337 | return 0; |
338 | 338 | ||
339 | secattr = selinux_netlbl_sock_genattr(sk); | 339 | secattr = selinux_netlbl_sock_genattr(sk); |