aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv4/tcp_ipv4.c45
-rw-r--r--net/ipv6/tcp_ipv6.c43
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
689release_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
978release_sk1:
979 if (sk1) {
980 rcu_read_unlock();
981 sock_put(sk1);
982 }
983#endif
945} 984}
946 985
947static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, 986static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts,