[PATCH] tcp: md5: fix md5 RST when both sides have listener

Subsystems: networking [general], networking [tcp], the rest

STALE5311d

6 messages, 2 authors, 2012-02-01 · open the first message on its own page

[PATCH] tcp: md5: fix md5 RST when both sides have listener

From: Shawn Lu <hidden>
Date: 2012-01-31 23:54:05

TCP RST mechanism is broken in TCP md5(RFC2385). When
connection is gone, md5 key is lost, sending RST
without md5 hash is deem to ignored by peer. This can
be a problem since RST help protocal like bgp to fast
recove from peer crash.

In most case, users of tcp md5, such as bgp and ldp,
have listeners on both sides. md5 keys for peers are
saved in listening socket. When passive side connection
is gone, we can still get md5 key from listening socket.
When active side of connection is gone, we can try to
find listening socket through source port, and then md5
key.
we are not loosing sercuriy here:
packet is valified checked with md5 hash. No RST is generated
if md5 hash doesn't match or no md5 key can be found.

Signed-off-by: Shawn Lu <redacted>
---
 net/ipv4/tcp_ipv4.c |   43 ++++++++++++++++++++++++++++++++++++++++---
 net/ipv6/tcp_ipv6.c |   40 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 78 insertions(+), 5 deletions(-)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index da5d322..adafee8 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)
 	struct ip_reply_arg arg;
 #ifdef CONFIG_TCP_MD5SIG
 	struct tcp_md5sig_key *key;
+	const __u8 *hash_location = NULL;
+	unsigned char newhash[16];
+	int genhash;
+	struct sock *sk1 = NULL;
 #endif
 	struct net *net;
 
@@ -623,9 +627,36 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
 	arg.iov[0].iov_len  = sizeof(rep.th);
 
 #ifdef CONFIG_TCP_MD5SIG
-	key = sk ? tcp_md5_do_lookup(sk,
-				     (union tcp_md5_addr *)&ip_hdr(skb)->saddr,
-				     AF_INET) : NULL;
+	hash_location = tcp_parse_md5sig_option(th);
+	if (!sk && hash_location) {
+		/*
+		 * active side is lost. Try to find listening socket through
+		 * source port, and then find md5 key through listening socket.
+		 * we are not loose security here:
+		 * Incoming packet is checked with md5 hash with finding key,
+		 * no RST generated if md5 hash doesn't match.
+		 */
+		sk1 = __inet_lookup_listener(dev_net(skb_dst(skb)->dev),
+					     &tcp_hashinfo, ip_hdr(skb)->daddr,
+					     ntohs(th->source), inet_iif(skb));
+		/* don't send rst if it can't find key */
+		if (!sk1)
+			return;
+
+		key = tcp_md5_do_lookup(sk1, (union tcp_md5_addr *)
+					&ip_hdr(skb)->saddr, AF_INET);
+		if (!key)
+			goto release_sk1;
+
+		genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, NULL, skb);
+		if (genhash || memcmp(hash_location, newhash, 16) != 0)
+			goto release_sk1;
+	} else {
+		key = sk ? tcp_md5_do_lookup(sk, (union tcp_md5_addr *)
+					     &ip_hdr(skb)->saddr,
+					     AF_INET) : NULL;
+	}
+
 	if (key) {
 		rep.opt[0] = htonl((TCPOPT_NOP << 24) |
 				   (TCPOPT_NOP << 16) |
@@ -653,6 +684,12 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
 
 	TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
 	TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS);
+
+#ifdef CONFIG_TCP_MD5SIG
+release_sk1:
+	if (sk1)
+		sock_put(sk1);
+#endif
 }
 
 /* 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 bec41f9..00da65c 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -925,6 +925,13 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
 	const struct tcphdr *th = tcp_hdr(skb);
 	u32 seq = 0, ack_seq = 0;
 	struct tcp_md5sig_key *key = NULL;
+#ifdef CONFIG_TCP_MD5SIG
+	const __u8 *hash_location = NULL;
+	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+	unsigned char newhash[16];
+	int genhash;
+	struct sock *sk1 = NULL;
+#endif
 
 	if (th->rst)
 		return;
@@ -933,8 +940,31 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
 		return;
 
 #ifdef CONFIG_TCP_MD5SIG
-	if (sk)
-		key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr);
+	hash_location = tcp_parse_md5sig_option(th);
+	if (!sk && hash_location) {
+		/*
+		 * active side is lost. Try to find listening socket through
+		 * source port, and then find md5 key through listening socket.
+		 * we are not loose security here:
+		 * Incoming packet is checked with md5 hash with finding key,
+		 * no RST generated if md5 hash doesn't match.
+		 */
+		sk1 = inet6_lookup_listener(dev_net(skb_dst(skb)->dev),
+					   &tcp_hashinfo, &ipv6h->daddr,
+					   ntohs(th->source), inet6_iif(skb));
+		if (!sk1)
+			return;
+
+		key = tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr);
+		if (!key)
+			goto release_sk1;
+
+		genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, NULL, skb);
+		if (genhash || memcmp(hash_location, newhash, 16) != 0)
+			goto release_sk1;
+	} else {
+		key = sk ? tcp_v6_md5_do_lookup(sk, &ipv6h->saddr) : NULL;
+	}
 #endif
 
 	if (th->ack)
@@ -944,6 +974,12 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
 			  (th->doff << 2);
 
 	tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1, 0);
+
+#ifdef CONFIG_TCP_MD5SIG
+release_sk1:
+	if (sk1)
+		sock_put(sk1);
+#endif
 }
 
 static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts,
-- 
1.7.0.4

Re: [PATCH] tcp: md5: fix md5 RST when both sides have listener

From: Eric Dumazet <hidden>
Date: 2012-02-01 05:09:24

Le mardi 31 janvier 2012 à 15:53 -0800, Shawn Lu a écrit :
TCP RST mechanism is broken in TCP md5(RFC2385). When
connection is gone, md5 key is lost, sending RST
without md5 hash is deem to ignored by peer. This can
be a problem since RST help protocal like bgp to fast
recove from peer crash.

In most case, users of tcp md5, such as bgp and ldp,
have listeners on both sides. md5 keys for peers are
saved in listening socket. When passive side connection
is gone, we can still get md5 key from listening socket.
When active side of connection is gone, we can try to
find listening socket through source port, and then md5
key.
we are not loosing sercuriy here:
packet is valified checked with md5 hash. No RST is generated
if md5 hash doesn't match or no md5 key can be found.

Signed-off-by: Shawn Lu <redacted>
---
Small notes : 

1) Always add [PATCH Vx] on your submission if it was a new version of a
previous patch. (V2, V3, V4, ...)

If possible, add after the "---" separator an explanation of what
changed in your new submission :

V3: added some rcu_read_lock()/rcu_read_unlock() sections

2) Also patch title and changelog are a bit confusing.

Only the machine receiving the TCP message and answering by RST message
needs a listener, in case tcp frame cannot be associated to an existing
tcp socket (ESTABLISHED or TIMEWAIT).

The other side doesnt need a listener, only a client using TCP MD5
option in its socket.

Other than that, your patch seems fine to me.

Acked-by: Eric Dumazet <redacted>

Thanks !

RE: [PATCH] tcp: md5: fix md5 RST when both sides have listener

From: Shawn Lu <hidden>
Date: 2012-02-01 07:48:31

Hi, Eric:

How about change the title and log to following:

  tcp: md5: RST: getting md5 key from listener

    TCP RST mechanism is broken in TCP md5(RFC2385). When
    connection is gone, md5 key is lost, sending RST
    without md5 hash is deem to ignored by peer. This can
    be a problem since RST help protocal like bgp to fast
    recove from peer crash.

    In most case, users of tcp md5, such as bgp and ldp,
    have listener on both side to accept connection from peer.
    md5 keys for peers are saved in listening socket.

    There are two cases in finding md5 key when connection is
    lost:
    1.Passive receive RST: The message is send to well known port,
    tcp will associate packet with listener. md5 key can be gotten
    from listener.

    2.Active receive RST (no sock): The message is send to ative
    side, there is no socket associated with message. In this case,
    finding listener from source port, then find md5 key from
    listener.

    we are not loosing sercuriy here:
    packet is checked with md5 hash. No RST is generated
    if md5 hash doesn't match or no md5 key can be found.

Note:
Will send out a new version that is on top of your new patch
-- "tcp: md5: protects md5sig_info with RCU"


-----Original Message-----
From: Eric Dumazet [mailto:eric.dumazet@gmail.com] 
Sent: Tuesday, January 31, 2012 9:09 PM
To: Shawn Lu
Cc: davem@davemloft.net; netdev@vger.kernel.org; xiaoclu@gmail.com
Subject: Re: [PATCH] tcp: md5: fix md5 RST when both sides have listener

Le mardi 31 janvier 2012 à 15:53 -0800, Shawn Lu a écrit :
TCP RST mechanism is broken in TCP md5(RFC2385). When connection is 
gone, md5 key is lost, sending RST without md5 hash is deem to ignored 
by peer. This can be a problem since RST help protocal like bgp to 
fast recove from peer crash.

In most case, users of tcp md5, such as bgp and ldp, have listeners on 
both sides. md5 keys for peers are saved in listening socket. When 
passive side connection is gone, we can still get md5 key from 
listening socket.
When active side of connection is gone, we can try to find listening 
socket through source port, and then md5 key.
we are not loosing sercuriy here:
packet is valified checked with md5 hash. No RST is generated if md5 
hash doesn't match or no md5 key can be found.

Signed-off-by: Shawn Lu <redacted>
---
Small notes : 

1) Always add [PATCH Vx] on your submission if it was a new version of a previous patch. (V2, V3, V4, ...)

If possible, add after the "---" separator an explanation of what changed in your new submission :

V3: added some rcu_read_lock()/rcu_read_unlock() sections

2) Also patch title and changelog are a bit confusing.

Only the machine receiving the TCP message and answering by RST message needs a listener, in case tcp frame cannot be associated to an existing tcp socket (ESTABLISHED or TIMEWAIT).

The other side doesnt need a listener, only a client using TCP MD5 option in its socket.

Other than that, your patch seems fine to me.

Acked-by: Eric Dumazet <redacted>

Thanks !

RE: [PATCH] tcp: md5: fix md5 RST when both sides have listener

From: Eric Dumazet <hidden>
Date: 2012-02-01 07:53:58

Le mercredi 01 février 2012 à 02:48 -0500, Shawn Lu a écrit :
Hi, Eric:

How about change the title and log to following:

  tcp: md5: RST: getting md5 key from listener

    TCP RST mechanism is broken in TCP md5(RFC2385). When
    connection is gone, md5 key is lost, sending RST
    without md5 hash is deem to ignored by peer. This can
    be a problem since RST help protocal like bgp to fast
    recove from peer crash.

    In most case, users of tcp md5, such as bgp and ldp,
    have listener on both side to accept connection from peer.
    md5 keys for peers are saved in listening socket.

    There are two cases in finding md5 key when connection is
    lost:
    1.Passive receive RST: The message is send to well known port,
    tcp will associate packet with listener. md5 key can be gotten
    from listener.

    2.Active receive RST (no sock): The message is send to ative
    side, there is no socket associated with message. In this case,
    finding listener from source port, then find md5 key from
    listener.

    we are not loosing sercuriy here:
    packet is checked with md5 hash. No RST is generated
    if md5 hash doesn't match or no md5 key can be found.

Note:
Will send out a new version that is on top of your new patch
-- "tcp: md5: protects md5sig_info with RCU"
Seems good to me !

By the way, is the patch going to work if netfilter conntrack is
enabled ?

RE: [PATCH] tcp: md5: fix md5 RST when both sides have listener

From: Shawn Lu <hidden>
Date: 2012-02-01 08:11:21

Why? Do you mean packet is droped by netfilter before getting to tcp and No RST is generated. 

-----Original Message-----
From: Eric Dumazet [mailto:eric.dumazet@gmail.com] 
Sent: Tuesday, January 31, 2012 11:54 PM
To: Shawn Lu
Cc: davem@davemloft.net; netdev@vger.kernel.org; xiaoclu@gmail.com
Subject: RE: [PATCH] tcp: md5: fix md5 RST when both sides have listener

Le mercredi 01 février 2012 à 02:48 -0500, Shawn Lu a écrit :
Hi, Eric:

How about change the title and log to following:

  tcp: md5: RST: getting md5 key from listener

    TCP RST mechanism is broken in TCP md5(RFC2385). When
    connection is gone, md5 key is lost, sending RST
    without md5 hash is deem to ignored by peer. This can
    be a problem since RST help protocal like bgp to fast
    recove from peer crash.

    In most case, users of tcp md5, such as bgp and ldp,
    have listener on both side to accept connection from peer.
    md5 keys for peers are saved in listening socket.

    There are two cases in finding md5 key when connection is
    lost:
    1.Passive receive RST: The message is send to well known port,
    tcp will associate packet with listener. md5 key can be gotten
    from listener.

    2.Active receive RST (no sock): The message is send to ative
    side, there is no socket associated with message. In this case,
    finding listener from source port, then find md5 key from
    listener.

    we are not loosing sercuriy here:
    packet is checked with md5 hash. No RST is generated
    if md5 hash doesn't match or no md5 key can be found.

Note:
Will send out a new version that is on top of your new patch
-- "tcp: md5: protects md5sig_info with RCU"
Seems good to me !

By the way, is the patch going to work if netfilter conntrack is enabled ?

RE: [PATCH] tcp: md5: fix md5 RST when both sides have listener

From: Eric Dumazet <hidden>
Date: 2012-02-01 09:25:49

Le mercredi 01 février 2012 à 03:11 -0500, Shawn Lu a écrit :
Why? Do you mean packet is droped by netfilter before getting to tcp and No RST is generated. 
Hmm, net/ipv4/netfilter/ipt_REJECT.c / net/ipv6/netfilter/ip6t_REJECT.c
are able to sent RST packets. I presume this is not md5 aware.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help