aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv4/inet_diag.c154
-rw-r--r--net/ipv4/ip_fragment.c19
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
49static DEFINE_MUTEX(inet_diag_table_mutex); 53static 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. */
523static 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. */
561static 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
512static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) 571static 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 */
671static 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
599static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, 698static 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
708struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) 708struct 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));