diff options
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 45 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 43 |
2 files changed, 83 insertions, 5 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 567cca9b30df..90e47931e217 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -593,6 +593,10 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) | |||
593 | struct ip_reply_arg arg; | 593 | struct ip_reply_arg arg; |
594 | #ifdef CONFIG_TCP_MD5SIG | 594 | #ifdef CONFIG_TCP_MD5SIG |
595 | struct tcp_md5sig_key *key; | 595 | struct tcp_md5sig_key *key; |
596 | const __u8 *hash_location = NULL; | ||
597 | unsigned char newhash[16]; | ||
598 | int genhash; | ||
599 | struct sock *sk1 = NULL; | ||
596 | #endif | 600 | #endif |
597 | struct net *net; | 601 | struct net *net; |
598 | 602 | ||
@@ -623,9 +627,36 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) | |||
623 | arg.iov[0].iov_len = sizeof(rep.th); | 627 | arg.iov[0].iov_len = sizeof(rep.th); |
624 | 628 | ||
625 | #ifdef CONFIG_TCP_MD5SIG | 629 | #ifdef CONFIG_TCP_MD5SIG |
626 | key = sk ? tcp_md5_do_lookup(sk, | 630 | hash_location = tcp_parse_md5sig_option(th); |
627 | (union tcp_md5_addr *)&ip_hdr(skb)->saddr, | 631 | if (!sk && hash_location) { |
628 | AF_INET) : NULL; | 632 | /* |
633 | * active side is lost. Try to find listening socket through | ||
634 | * source port, and then find md5 key through listening socket. | ||
635 | * we are not loose security here: | ||
636 | * Incoming packet is checked with md5 hash with finding key, | ||
637 | * no RST generated if md5 hash doesn't match. | ||
638 | */ | ||
639 | sk1 = __inet_lookup_listener(dev_net(skb_dst(skb)->dev), | ||
640 | &tcp_hashinfo, ip_hdr(skb)->daddr, | ||
641 | ntohs(th->source), inet_iif(skb)); | ||
642 | /* don't send rst if it can't find key */ | ||
643 | if (!sk1) | ||
644 | return; | ||
645 | rcu_read_lock(); | ||
646 | key = tcp_md5_do_lookup(sk1, (union tcp_md5_addr *) | ||
647 | &ip_hdr(skb)->saddr, AF_INET); | ||
648 | if (!key) | ||
649 | goto release_sk1; | ||
650 | |||
651 | genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, NULL, skb); | ||
652 | if (genhash || memcmp(hash_location, newhash, 16) != 0) | ||
653 | goto release_sk1; | ||
654 | } else { | ||
655 | key = sk ? tcp_md5_do_lookup(sk, (union tcp_md5_addr *) | ||
656 | &ip_hdr(skb)->saddr, | ||
657 | AF_INET) : NULL; | ||
658 | } | ||
659 | |||
629 | if (key) { | 660 | if (key) { |
630 | rep.opt[0] = htonl((TCPOPT_NOP << 24) | | 661 | rep.opt[0] = htonl((TCPOPT_NOP << 24) | |
631 | (TCPOPT_NOP << 16) | | 662 | (TCPOPT_NOP << 16) | |
@@ -653,6 +684,14 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) | |||
653 | 684 | ||
654 | TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); | 685 | TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); |
655 | TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); | 686 | TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); |
687 | |||
688 | #ifdef CONFIG_TCP_MD5SIG | ||
689 | release_sk1: | ||
690 | if (sk1) { | ||
691 | rcu_read_unlock(); | ||
692 | sock_put(sk1); | ||
693 | } | ||
694 | #endif | ||
656 | } | 695 | } |
657 | 696 | ||
658 | /* The code following below sending ACKs in SYN-RECV and TIME-WAIT states | 697 | /* The code following below sending ACKs in SYN-RECV and TIME-WAIT states |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c25018106ef2..d16414cb3421 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -923,6 +923,13 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) | |||
923 | const struct tcphdr *th = tcp_hdr(skb); | 923 | const struct tcphdr *th = tcp_hdr(skb); |
924 | u32 seq = 0, ack_seq = 0; | 924 | u32 seq = 0, ack_seq = 0; |
925 | struct tcp_md5sig_key *key = NULL; | 925 | struct tcp_md5sig_key *key = NULL; |
926 | #ifdef CONFIG_TCP_MD5SIG | ||
927 | const __u8 *hash_location = NULL; | ||
928 | struct ipv6hdr *ipv6h = ipv6_hdr(skb); | ||
929 | unsigned char newhash[16]; | ||
930 | int genhash; | ||
931 | struct sock *sk1 = NULL; | ||
932 | #endif | ||
926 | 933 | ||
927 | if (th->rst) | 934 | if (th->rst) |
928 | return; | 935 | return; |
@@ -931,8 +938,32 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) | |||
931 | return; | 938 | return; |
932 | 939 | ||
933 | #ifdef CONFIG_TCP_MD5SIG | 940 | #ifdef CONFIG_TCP_MD5SIG |
934 | if (sk) | 941 | hash_location = tcp_parse_md5sig_option(th); |
935 | key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr); | 942 | if (!sk && hash_location) { |
943 | /* | ||
944 | * active side is lost. Try to find listening socket through | ||
945 | * source port, and then find md5 key through listening socket. | ||
946 | * we are not loose security here: | ||
947 | * Incoming packet is checked with md5 hash with finding key, | ||
948 | * no RST generated if md5 hash doesn't match. | ||
949 | */ | ||
950 | sk1 = inet6_lookup_listener(dev_net(skb_dst(skb)->dev), | ||
951 | &tcp_hashinfo, &ipv6h->daddr, | ||
952 | ntohs(th->source), inet6_iif(skb)); | ||
953 | if (!sk1) | ||
954 | return; | ||
955 | |||
956 | rcu_read_lock(); | ||
957 | key = tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr); | ||
958 | if (!key) | ||
959 | goto release_sk1; | ||
960 | |||
961 | genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, NULL, skb); | ||
962 | if (genhash || memcmp(hash_location, newhash, 16) != 0) | ||
963 | goto release_sk1; | ||
964 | } else { | ||
965 | key = sk ? tcp_v6_md5_do_lookup(sk, &ipv6h->saddr) : NULL; | ||
966 | } | ||
936 | #endif | 967 | #endif |
937 | 968 | ||
938 | if (th->ack) | 969 | if (th->ack) |
@@ -942,6 +973,14 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) | |||
942 | (th->doff << 2); | 973 | (th->doff << 2); |
943 | 974 | ||
944 | tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1, 0); | 975 | tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1, 0); |
976 | |||
977 | #ifdef CONFIG_TCP_MD5SIG | ||
978 | release_sk1: | ||
979 | if (sk1) { | ||
980 | rcu_read_unlock(); | ||
981 | sock_put(sk1); | ||
982 | } | ||
983 | #endif | ||
945 | } | 984 | } |
946 | 985 | ||
947 | static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, | 986 | static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, |