diff options
-rw-r--r-- | net/ipv4/inet_diag.c | 154 | ||||
-rw-r--r-- | net/ipv4/ip_fragment.c | 19 |
2 files changed, 131 insertions, 42 deletions
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 0c34bfabc11f..e23e16dc501d 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c | |||
@@ -44,6 +44,10 @@ struct inet_diag_entry { | |||
44 | u16 dport; | 44 | u16 dport; |
45 | u16 family; | 45 | u16 family; |
46 | u16 userlocks; | 46 | u16 userlocks; |
47 | #if IS_ENABLED(CONFIG_IPV6) | ||
48 | struct in6_addr saddr_storage; /* for IPv4-mapped-IPv6 addresses */ | ||
49 | struct in6_addr daddr_storage; /* for IPv4-mapped-IPv6 addresses */ | ||
50 | #endif | ||
47 | }; | 51 | }; |
48 | 52 | ||
49 | static DEFINE_MUTEX(inet_diag_table_mutex); | 53 | static DEFINE_MUTEX(inet_diag_table_mutex); |
@@ -428,25 +432,31 @@ static int inet_diag_bc_run(const struct nlattr *_bc, | |||
428 | break; | 432 | break; |
429 | } | 433 | } |
430 | 434 | ||
431 | if (cond->prefix_len == 0) | ||
432 | break; | ||
433 | |||
434 | if (op->code == INET_DIAG_BC_S_COND) | 435 | if (op->code == INET_DIAG_BC_S_COND) |
435 | addr = entry->saddr; | 436 | addr = entry->saddr; |
436 | else | 437 | else |
437 | addr = entry->daddr; | 438 | addr = entry->daddr; |
438 | 439 | ||
440 | if (cond->family != AF_UNSPEC && | ||
441 | cond->family != entry->family) { | ||
442 | if (entry->family == AF_INET6 && | ||
443 | cond->family == AF_INET) { | ||
444 | if (addr[0] == 0 && addr[1] == 0 && | ||
445 | addr[2] == htonl(0xffff) && | ||
446 | bitstring_match(addr + 3, | ||
447 | cond->addr, | ||
448 | cond->prefix_len)) | ||
449 | break; | ||
450 | } | ||
451 | yes = 0; | ||
452 | break; | ||
453 | } | ||
454 | |||
455 | if (cond->prefix_len == 0) | ||
456 | break; | ||
439 | if (bitstring_match(addr, cond->addr, | 457 | if (bitstring_match(addr, cond->addr, |
440 | cond->prefix_len)) | 458 | cond->prefix_len)) |
441 | break; | 459 | break; |
442 | if (entry->family == AF_INET6 && | ||
443 | cond->family == AF_INET) { | ||
444 | if (addr[0] == 0 && addr[1] == 0 && | ||
445 | addr[2] == htonl(0xffff) && | ||
446 | bitstring_match(addr + 3, cond->addr, | ||
447 | cond->prefix_len)) | ||
448 | break; | ||
449 | } | ||
450 | yes = 0; | 460 | yes = 0; |
451 | break; | 461 | break; |
452 | } | 462 | } |
@@ -509,6 +519,55 @@ static int valid_cc(const void *bc, int len, int cc) | |||
509 | return 0; | 519 | return 0; |
510 | } | 520 | } |
511 | 521 | ||
522 | /* Validate an inet_diag_hostcond. */ | ||
523 | static bool valid_hostcond(const struct inet_diag_bc_op *op, int len, | ||
524 | int *min_len) | ||
525 | { | ||
526 | int addr_len; | ||
527 | struct inet_diag_hostcond *cond; | ||
528 | |||
529 | /* Check hostcond space. */ | ||
530 | *min_len += sizeof(struct inet_diag_hostcond); | ||
531 | if (len < *min_len) | ||
532 | return false; | ||
533 | cond = (struct inet_diag_hostcond *)(op + 1); | ||
534 | |||
535 | /* Check address family and address length. */ | ||
536 | switch (cond->family) { | ||
537 | case AF_UNSPEC: | ||
538 | addr_len = 0; | ||
539 | break; | ||
540 | case AF_INET: | ||
541 | addr_len = sizeof(struct in_addr); | ||
542 | break; | ||
543 | case AF_INET6: | ||
544 | addr_len = sizeof(struct in6_addr); | ||
545 | break; | ||
546 | default: | ||
547 | return false; | ||
548 | } | ||
549 | *min_len += addr_len; | ||
550 | if (len < *min_len) | ||
551 | return false; | ||
552 | |||
553 | /* Check prefix length (in bits) vs address length (in bytes). */ | ||
554 | if (cond->prefix_len > 8 * addr_len) | ||
555 | return false; | ||
556 | |||
557 | return true; | ||
558 | } | ||
559 | |||
560 | /* Validate a port comparison operator. */ | ||
561 | static inline bool valid_port_comparison(const struct inet_diag_bc_op *op, | ||
562 | int len, int *min_len) | ||
563 | { | ||
564 | /* Port comparisons put the port in a follow-on inet_diag_bc_op. */ | ||
565 | *min_len += sizeof(struct inet_diag_bc_op); | ||
566 | if (len < *min_len) | ||
567 | return false; | ||
568 | return true; | ||
569 | } | ||
570 | |||
512 | static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) | 571 | static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) |
513 | { | 572 | { |
514 | const void *bc = bytecode; | 573 | const void *bc = bytecode; |
@@ -516,29 +575,39 @@ static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) | |||
516 | 575 | ||
517 | while (len > 0) { | 576 | while (len > 0) { |
518 | const struct inet_diag_bc_op *op = bc; | 577 | const struct inet_diag_bc_op *op = bc; |
578 | int min_len = sizeof(struct inet_diag_bc_op); | ||
519 | 579 | ||
520 | //printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len); | 580 | //printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len); |
521 | switch (op->code) { | 581 | switch (op->code) { |
522 | case INET_DIAG_BC_AUTO: | ||
523 | case INET_DIAG_BC_S_COND: | 582 | case INET_DIAG_BC_S_COND: |
524 | case INET_DIAG_BC_D_COND: | 583 | case INET_DIAG_BC_D_COND: |
584 | if (!valid_hostcond(bc, len, &min_len)) | ||
585 | return -EINVAL; | ||
586 | break; | ||
525 | case INET_DIAG_BC_S_GE: | 587 | case INET_DIAG_BC_S_GE: |
526 | case INET_DIAG_BC_S_LE: | 588 | case INET_DIAG_BC_S_LE: |
527 | case INET_DIAG_BC_D_GE: | 589 | case INET_DIAG_BC_D_GE: |
528 | case INET_DIAG_BC_D_LE: | 590 | case INET_DIAG_BC_D_LE: |
529 | case INET_DIAG_BC_JMP: | 591 | if (!valid_port_comparison(bc, len, &min_len)) |
530 | if (op->no < 4 || op->no > len + 4 || op->no & 3) | ||
531 | return -EINVAL; | ||
532 | if (op->no < len && | ||
533 | !valid_cc(bytecode, bytecode_len, len - op->no)) | ||
534 | return -EINVAL; | 592 | return -EINVAL; |
535 | break; | 593 | break; |
594 | case INET_DIAG_BC_AUTO: | ||
595 | case INET_DIAG_BC_JMP: | ||
536 | case INET_DIAG_BC_NOP: | 596 | case INET_DIAG_BC_NOP: |
537 | break; | 597 | break; |
538 | default: | 598 | default: |
539 | return -EINVAL; | 599 | return -EINVAL; |
540 | } | 600 | } |
541 | if (op->yes < 4 || op->yes > len + 4 || op->yes & 3) | 601 | |
602 | if (op->code != INET_DIAG_BC_NOP) { | ||
603 | if (op->no < min_len || op->no > len + 4 || op->no & 3) | ||
604 | return -EINVAL; | ||
605 | if (op->no < len && | ||
606 | !valid_cc(bytecode, bytecode_len, len - op->no)) | ||
607 | return -EINVAL; | ||
608 | } | ||
609 | |||
610 | if (op->yes < min_len || op->yes > len + 4 || op->yes & 3) | ||
542 | return -EINVAL; | 611 | return -EINVAL; |
543 | bc += op->yes; | 612 | bc += op->yes; |
544 | len -= op->yes; | 613 | len -= op->yes; |
@@ -596,6 +665,36 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, | |||
596 | cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); | 665 | cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); |
597 | } | 666 | } |
598 | 667 | ||
668 | /* Get the IPv4, IPv6, or IPv4-mapped-IPv6 local and remote addresses | ||
669 | * from a request_sock. For IPv4-mapped-IPv6 we must map IPv4 to IPv6. | ||
670 | */ | ||
671 | static inline void inet_diag_req_addrs(const struct sock *sk, | ||
672 | const struct request_sock *req, | ||
673 | struct inet_diag_entry *entry) | ||
674 | { | ||
675 | struct inet_request_sock *ireq = inet_rsk(req); | ||
676 | |||
677 | #if IS_ENABLED(CONFIG_IPV6) | ||
678 | if (sk->sk_family == AF_INET6) { | ||
679 | if (req->rsk_ops->family == AF_INET6) { | ||
680 | entry->saddr = inet6_rsk(req)->loc_addr.s6_addr32; | ||
681 | entry->daddr = inet6_rsk(req)->rmt_addr.s6_addr32; | ||
682 | } else if (req->rsk_ops->family == AF_INET) { | ||
683 | ipv6_addr_set_v4mapped(ireq->loc_addr, | ||
684 | &entry->saddr_storage); | ||
685 | ipv6_addr_set_v4mapped(ireq->rmt_addr, | ||
686 | &entry->daddr_storage); | ||
687 | entry->saddr = entry->saddr_storage.s6_addr32; | ||
688 | entry->daddr = entry->daddr_storage.s6_addr32; | ||
689 | } | ||
690 | } else | ||
691 | #endif | ||
692 | { | ||
693 | entry->saddr = &ireq->loc_addr; | ||
694 | entry->daddr = &ireq->rmt_addr; | ||
695 | } | ||
696 | } | ||
697 | |||
599 | static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, | 698 | static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, |
600 | struct request_sock *req, | 699 | struct request_sock *req, |
601 | struct user_namespace *user_ns, | 700 | struct user_namespace *user_ns, |
@@ -637,8 +736,10 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, | |||
637 | r->idiag_inode = 0; | 736 | r->idiag_inode = 0; |
638 | #if IS_ENABLED(CONFIG_IPV6) | 737 | #if IS_ENABLED(CONFIG_IPV6) |
639 | if (r->idiag_family == AF_INET6) { | 738 | if (r->idiag_family == AF_INET6) { |
640 | *(struct in6_addr *)r->id.idiag_src = inet6_rsk(req)->loc_addr; | 739 | struct inet_diag_entry entry; |
641 | *(struct in6_addr *)r->id.idiag_dst = inet6_rsk(req)->rmt_addr; | 740 | inet_diag_req_addrs(sk, req, &entry); |
741 | memcpy(r->id.idiag_src, entry.saddr, sizeof(struct in6_addr)); | ||
742 | memcpy(r->id.idiag_dst, entry.daddr, sizeof(struct in6_addr)); | ||
642 | } | 743 | } |
643 | #endif | 744 | #endif |
644 | 745 | ||
@@ -691,18 +792,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, | |||
691 | continue; | 792 | continue; |
692 | 793 | ||
693 | if (bc) { | 794 | if (bc) { |
694 | entry.saddr = | 795 | inet_diag_req_addrs(sk, req, &entry); |
695 | #if IS_ENABLED(CONFIG_IPV6) | ||
696 | (entry.family == AF_INET6) ? | ||
697 | inet6_rsk(req)->loc_addr.s6_addr32 : | ||
698 | #endif | ||
699 | &ireq->loc_addr; | ||
700 | entry.daddr = | ||
701 | #if IS_ENABLED(CONFIG_IPV6) | ||
702 | (entry.family == AF_INET6) ? | ||
703 | inet6_rsk(req)->rmt_addr.s6_addr32 : | ||
704 | #endif | ||
705 | &ireq->rmt_addr; | ||
706 | entry.dport = ntohs(ireq->rmt_port); | 796 | entry.dport = ntohs(ireq->rmt_port); |
707 | 797 | ||
708 | if (!inet_diag_bc_run(bc, &entry)) | 798 | if (!inet_diag_bc_run(bc, &entry)) |
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 448e68546827..8d5cc75dac88 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c | |||
@@ -707,28 +707,27 @@ EXPORT_SYMBOL(ip_defrag); | |||
707 | 707 | ||
708 | struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) | 708 | struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) |
709 | { | 709 | { |
710 | const struct iphdr *iph; | 710 | struct iphdr iph; |
711 | u32 len; | 711 | u32 len; |
712 | 712 | ||
713 | if (skb->protocol != htons(ETH_P_IP)) | 713 | if (skb->protocol != htons(ETH_P_IP)) |
714 | return skb; | 714 | return skb; |
715 | 715 | ||
716 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | 716 | if (!skb_copy_bits(skb, 0, &iph, sizeof(iph))) |
717 | return skb; | 717 | return skb; |
718 | 718 | ||
719 | iph = ip_hdr(skb); | 719 | if (iph.ihl < 5 || iph.version != 4) |
720 | if (iph->ihl < 5 || iph->version != 4) | ||
721 | return skb; | 720 | return skb; |
722 | if (!pskb_may_pull(skb, iph->ihl*4)) | 721 | |
723 | return skb; | 722 | len = ntohs(iph.tot_len); |
724 | iph = ip_hdr(skb); | 723 | if (skb->len < len || len < (iph.ihl * 4)) |
725 | len = ntohs(iph->tot_len); | ||
726 | if (skb->len < len || len < (iph->ihl * 4)) | ||
727 | return skb; | 724 | return skb; |
728 | 725 | ||
729 | if (ip_is_fragment(ip_hdr(skb))) { | 726 | if (ip_is_fragment(&iph)) { |
730 | skb = skb_share_check(skb, GFP_ATOMIC); | 727 | skb = skb_share_check(skb, GFP_ATOMIC); |
731 | if (skb) { | 728 | if (skb) { |
729 | if (!pskb_may_pull(skb, iph.ihl*4)) | ||
730 | return skb; | ||
732 | if (pskb_trim_rcsum(skb, len)) | 731 | if (pskb_trim_rcsum(skb, len)) |
733 | return skb; | 732 | return skb; |
734 | memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); | 733 | memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); |