Skip to contentSkip to conversation

ipv[46]: Fix MD5 signatures for non-linear skbs

PATCH In review Adam Langley 5 participants 11 messages Jul 4, 2008 – Jul 9, 2008

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ipv[46]: Fix MD5 signatures for non-linear skbs

Currently, the MD5 code assumes that the SKBs are linear and, in the case that they aren't, happily goes off and hashes off the end of the SKB and into random memory.

Reported by Stephen Hemminger in [1]. Advice thanks to Stephen and Evgeniy Polyakov. Also includes a couple of missed route_caps from Stephen's patch in [2].

[1] http://marc.info/?l=linux-netdev&m=121445989106145&w=2
[2] http://marc.info/?l=linux-netdev&m=121459157816964&w=2

---

 include/net/tcp.h     |   20 ++---
 net/ipv4/tcp.c        |   57 ++++++++++++++++
 net/ipv4/tcp_ipv4.c   |  180 ++++++++++++++++++++++---------------------------
 net/ipv4/tcp_output.c |   17 ++---
 net/ipv6/tcp_ipv6.c   |  168 ++++++++++++++++++++++------------------------
 5 files changed, 237 insertions(+), 205 deletions(-)
include/net/tcp.h +9 -11
index cf54034..baca49e 100644
@@ -1113,14 +1113,9 @@ struct tcp_md5sig_pool {
 #define TCP_MD5SIG_MAXKEYS	(~(u32)0)	/* really?! */
 
 /* - functions */
-extern int			tcp_v4_calc_md5_hash(char *md5_hash,
-						     struct tcp_md5sig_key *key,
-						     struct sock *sk,
-						     struct dst_entry *dst,
-						     struct request_sock *req,
-						     struct tcphdr *th,
-						     int protocol,
-						     unsigned int tcplen);
+int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
+			struct sock *sk, struct request_sock *req,
+			int protocol, struct sk_buff *skb);
 extern struct tcp_md5sig_key	*tcp_v4_md5_lookup(struct sock *sk,
 						   struct sock *addr_sk);
 
@@ -1137,6 +1132,11 @@ extern void			tcp_free_md5sig_pool(void);
 
 extern struct tcp_md5sig_pool	*__tcp_get_md5sig_pool(int cpu);
 extern void			__tcp_put_md5sig_pool(void);
+extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, struct tcphdr *);
+extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, struct sk_buff *,
+				 unsigned header_len);
+extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
+			    struct tcp_md5sig_key *key);
 
 static inline
 struct tcp_md5sig_pool		*tcp_get_md5sig_pool(void)
@@ -1366,11 +1366,9 @@ struct tcp_sock_af_ops {
 	int			(*calc_md5_hash) (char *location,
 						  struct tcp_md5sig_key *md5,
 						  struct sock *sk,
-						  struct dst_entry *dst,
 						  struct request_sock *req,
-						  struct tcphdr *th,
 						  int protocol,
-						  unsigned int len);
+						  struct sk_buff *skb);
 	int			(*md5_add) (struct sock *sk,
 					    struct sock *addr_sk,
 					    u8 *newkey,
net/ipv4/tcp.c +57 -0

63 lines not on this page · raw message

net/ipv4/tcp_ipv4.c +82 -98

259 lines not on this page · raw message

net/ipv4/tcp_output.c +7 -10
index ad993ec..14dfc09 100644
@@ -542,8 +542,10 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 	 * room for it.
 	 */
 	md5 = tp->af_specific->md5_lookup(sk, sk);
-	if (md5)
+	if (md5) {
 		tcp_header_size += TCPOLEN_MD5SIG_ALIGNED;
+		sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+	}
 #endif
 
 	skb_push(skb, tcp_header_size);
@@ -604,11 +606,8 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 	/* Calculate the MD5 hash, as we have all we need now */
 	if (md5) {
 		tp->af_specific->calc_md5_hash(md5_hash_location,
-					       md5,
-					       sk, NULL, NULL,
-					       tcp_hdr(skb),
-					       sk->sk_protocol,
-					       skb->len);
+					       md5, sk, NULL,
+					       sk->sk_protocol, skb);
 	}
 #endif
 
@@ -2264,10 +2263,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 	/* Okay, we have all we need - do the md5 hash if needed */
 	if (md5) {
 		tp->af_specific->calc_md5_hash(md5_hash_location,
-					       md5,
-					       NULL, dst, req,
-					       tcp_hdr(skb), sk->sk_protocol,
-					       skb->len);
+					       md5, NULL, req,
+					       sk->sk_protocol, skb);
 	}
 #endif
 
net/ipv6/tcp_ipv6.c +82 -86

236 lines not on this page · raw message

#1 lore raw
Quoted 114 lines · On Thu, 3 Jul 2008 17:19:41 -0700 "Adam Langley" <[email protected]> wrote:

Currently, the MD5 code assumes that the SKBs are linear and, in the case that they aren't, happily goes off and hashes off the end of the SKB and into random memory.

Reported by Stephen Hemminger in [1]. Advice thanks to Stephen and Evgeniy Polyakov. Also includes a couple of missed route_caps from Stephen's patch in [2].

[1] http://marc.info/?l=linux-netdev&m=121445989106145&w=2
[2] http://marc.info/?l=linux-netdev&m=121459157816964&w=2

---

 include/net/tcp.h     |   20 ++---
 net/ipv4/tcp.c        |   57 ++++++++++++++++
 net/ipv4/tcp_ipv4.c   |  180 ++++++++++++++++++++++---------------------------
 net/ipv4/tcp_output.c |   17 ++---
 net/ipv6/tcp_ipv6.c   |  168 ++++++++++++++++++++++------------------------
 5 files changed, 237 insertions(+), 205 deletions(-)
include/net/tcp.h +9 -11
index cf54034..baca49e 100644
@@ -1113,14 +1113,9 @@ struct tcp_md5sig_pool {
 #define TCP_MD5SIG_MAXKEYS	(~(u32)0)	/* really?! */
 
 /* - functions */
-extern int			tcp_v4_calc_md5_hash(char *md5_hash,
-						     struct tcp_md5sig_key *key,
-						     struct sock *sk,
-						     struct dst_entry *dst,
-						     struct request_sock *req,
-						     struct tcphdr *th,
-						     int protocol,
-						     unsigned int tcplen);
+int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
+			struct sock *sk, struct request_sock *req,
+			int protocol, struct sk_buff *skb);
 extern struct tcp_md5sig_key	*tcp_v4_md5_lookup(struct sock *sk,
 						   struct sock *addr_sk);
 
@@ -1137,6 +1132,11 @@ extern void			tcp_free_md5sig_pool(void);
 
 extern struct tcp_md5sig_pool	*__tcp_get_md5sig_pool(int cpu);
 extern void			__tcp_put_md5sig_pool(void);
+extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, struct tcphdr *);
+extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, struct sk_buff *,
+				 unsigned header_len);
+extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
+			    struct tcp_md5sig_key *key);
 
 static inline
 struct tcp_md5sig_pool		*tcp_get_md5sig_pool(void)
@@ -1366,11 +1366,9 @@ struct tcp_sock_af_ops {
 	int			(*calc_md5_hash) (char *location,
 						  struct tcp_md5sig_key *md5,
 						  struct sock *sk,
-						  struct dst_entry *dst,
 						  struct request_sock *req,
-						  struct tcphdr *th,
 						  int protocol,
-						  unsigned int len);
+						  struct sk_buff *skb);
 	int			(*md5_add) (struct sock *sk,
 					    struct sock *addr_sk,
 					    u8 *newkey,
net/ipv4/tcp.c +38 -0
index 1d723de..33b3860 100644
@@ -2590,6 +2590,63 @@ void __tcp_put_md5sig_pool(void)
 }
 
 EXPORT_SYMBOL(__tcp_put_md5sig_pool);
+
+int tcp_md5_hash_header(struct tcp_md5sig_pool *hp,
+			struct tcphdr *th)
+{
+	struct scatterlist sg;
+	int err;
+
+	__sum16 old_checksum = th->check;
+	th->check = 0;
+	sg_init_table(&sg, 1);
+	/* options aren't included in the hash */
+	sg_set_buf(&sg, th, sizeof(struct tcphdr));
+	err = crypto_hash_update(&hp->md5_desc, &sg, sizeof(struct tcphdr));
+	th->check = old_checksum;
+	return err;
+}
+
+int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp,
+			  struct sk_buff *skb, unsigned header_len)
+{
+	struct scatterlist sg;
+	const struct tcphdr *tp = tcp_hdr(skb);
+	struct hash_desc *desc = &hp->md5_desc;
+
+	sg_init_table(&sg, 1);
+
+	if (skb_headlen(skb) > header_len) {
+		const unsigned head_data_len = skb_headlen(skb) - header_len;
+		sg_set_buf(&sg, ((u8 *) tp) + header_len, head_data_len);
+		if (crypto_hash_update(desc, &sg, head_data_len))
+			return 1;
+	}
+
+	if (skb_is_nonlinear(skb)) {
+		const struct skb_shared_info *shi = skb_shinfo(skb);
+		unsigned i;
+
+		for (i = 0; i < shi->nr_frags; ++i) {

net/ipv4/tcp.c void __tcp_put_md5sig_pool(void) · line 2640

Quoted 10 lines
net/ipv4/tcp.c +9 -0
+			const struct skb_frag_struct *f = &shi->frags[i];
+			sg_set_page(&sg, f->page, f->size, f->page_offset);
+			if (crypto_hash_update(desc, &sg, f->size))
+				return 1;
+		}
+	}
+
+	return 0;
+}
 

Minor nit. I would code both of these as possibly empty loops.
crypto_hash_update(x,&p, 0) should be a nop
and also no need to test for nonlinear, just let the for() loop go for zero times.

#2 lore raw
In reply to Adam Langley

Hi.

net/ipv4/tcp.c void __tcp_put_md5sig_pool(void) · line 2648

Quoted 8 lines · Adam Langley
net/ipv4/tcp.c +8 -0
+int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, struct tcp_md5sig_key *key)
+{
+	struct scatterlist sg;
+
+	sg_init_table(&sg, 1);
+	sg_set_buf(&sg, key->key, key->keylen);
+	return crypto_hash_update(&hp->md5_desc, &sg, key->keylen);
+}

A trivial nit: you can use sg_init_one() to initialize single scatterlist and set data pointer instead of sg_init_table()/sg_set_buf() in all places where you use single sg.

Signature · 1 line
	Evgeniy Polyakov
#3 lore raw
Quoted 3 lines · Evgeniy Polyakov

A trivial nit: you can use sg_init_one() to initialize single scatterlist and set data pointer instead of sg_init_table()/sg_set_buf() in all places where you use single sg.

Thanks both. Respun patch in a second...
Changes: Use sg_init_one where possible and take a couple of ifs out of tcp_md5_hash_skb_data

AGL

Signature · 1 line
Adam Langley [email protected] http://www.imperialviolet.org
#4 lore raw

ipv[46]: Fix MD5 signatures for non-linear skbs

Currently, the MD5 code assumes that the SKBs are linear and, in the case that they aren't, happily goes off and hashes off the end of the SKB and into random memory.

Reported by Stephen Hemminger in [1]. Advice thanks to Stephen and Evgeniy Polyakov. Also includes a couple of missed route_caps from Stephen's patch in [2].

[1] http://marc.info/?l=linux-netdev&m=121445989106145&w=2
[2] http://marc.info/?l=linux-netdev&m=121459157816964&w=2

---

 include/net/tcp.h     |   20 ++---
 net/ipv4/tcp.c        |   51 ++++++++++++++
 net/ipv4/tcp_ipv4.c   |  179 ++++++++++++++++++++++---------------------------
 net/ipv4/tcp_output.c |   17 ++---
 net/ipv6/tcp_ipv6.c   |  167 ++++++++++++++++++++++------------------------
 5 files changed, 229 insertions(+), 205 deletions(-)
include/net/tcp.h +9 -11
index cf54034..baca49e 100644
@@ -1113,14 +1113,9 @@ struct tcp_md5sig_pool {
 #define TCP_MD5SIG_MAXKEYS	(~(u32)0)	/* really?! */
 
 /* - functions */
-extern int			tcp_v4_calc_md5_hash(char *md5_hash,
-						     struct tcp_md5sig_key *key,
-						     struct sock *sk,
-						     struct dst_entry *dst,
-						     struct request_sock *req,
-						     struct tcphdr *th,
-						     int protocol,
-						     unsigned int tcplen);
+int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
+			struct sock *sk, struct request_sock *req,
+			int protocol, struct sk_buff *skb);
 extern struct tcp_md5sig_key	*tcp_v4_md5_lookup(struct sock *sk,
 						   struct sock *addr_sk);
 
@@ -1137,6 +1132,11 @@ extern void			tcp_free_md5sig_pool(void);
 
 extern struct tcp_md5sig_pool	*__tcp_get_md5sig_pool(int cpu);
 extern void			__tcp_put_md5sig_pool(void);
+extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, struct tcphdr *);
+extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, struct sk_buff *,
+				 unsigned header_len);
+extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
+			    struct tcp_md5sig_key *key);
 
 static inline
 struct tcp_md5sig_pool		*tcp_get_md5sig_pool(void)
@@ -1366,11 +1366,9 @@ struct tcp_sock_af_ops {
 	int			(*calc_md5_hash) (char *location,
 						  struct tcp_md5sig_key *md5,
 						  struct sock *sk,
-						  struct dst_entry *dst,
 						  struct request_sock *req,
-						  struct tcphdr *th,
 						  int protocol,
-						  unsigned int len);
+						  struct sk_buff *skb);
 	int			(*md5_add) (struct sock *sk,
 					    struct sock *addr_sk,
 					    u8 *newkey,
net/ipv4/tcp.c +51 -0

57 lines not on this page · raw message

net/ipv4/tcp_ipv4.c +81 -98

258 lines not on this page · raw message

net/ipv4/tcp_output.c +7 -10
index ad993ec..14dfc09 100644
@@ -542,8 +542,10 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 	 * room for it.
 	 */
 	md5 = tp->af_specific->md5_lookup(sk, sk);
-	if (md5)
+	if (md5) {
 		tcp_header_size += TCPOLEN_MD5SIG_ALIGNED;
+		sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+	}
 #endif
 
 	skb_push(skb, tcp_header_size);
@@ -604,11 +606,8 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 	/* Calculate the MD5 hash, as we have all we need now */
 	if (md5) {
 		tp->af_specific->calc_md5_hash(md5_hash_location,
-					       md5,
-					       sk, NULL, NULL,
-					       tcp_hdr(skb),
-					       sk->sk_protocol,
-					       skb->len);
+					       md5, sk, NULL,
+					       sk->sk_protocol, skb);
 	}
 #endif
 
@@ -2264,10 +2263,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 	/* Okay, we have all we need - do the md5 hash if needed */
 	if (md5) {
 		tp->af_specific->calc_md5_hash(md5_hash_location,
-					       md5,
-					       NULL, dst, req,
-					       tcp_hdr(skb), sk->sk_protocol,
-					       skb->len);
+					       md5, NULL, req,
+					       sk->sk_protocol, skb);
 	}
 #endif
 
net/ipv6/tcp_ipv6.c +81 -86

235 lines not on this page · raw message

#5 lore raw
Quoted 6 lines · On Mon, 7 Jul 2008 10:22:43 -0700 "Adam Langley" <[email protected]> wrote:

ipv[46]: Fix MD5 signatures for non-linear skbs

Currently, the MD5 code assumes that the SKBs are linear and, in the case that they aren't, happily goes off and hashes off the end of the SKB and into random memory.

Quoted 8 lines

Reported by Stephen Hemminger in [1]. Advice thanks to Stephen and Evgeniy Polyakov. Also includes a couple of missed route_caps from Stephen's patch in [2].

[1] http://marc.info/?l=linux-netdev&m=121445989106145&w=2
[2] http://marc.info/?l=linux-netdev&m=121459157816964&w=2

Patch doesn't apply cleanly to net-next-2.6 tree because of other changes so to help the merge, please create a version for that as well.

#6 lore raw
Quoted 2 lines · Stephen Hemminger

Patch doesn't apply cleanly to net-next-2.6 tree because of other changes so to help the merge, please create a version for that as well.

Yea, that clashes pretty badly with net-next-2.6. Patch for that coming in a sec.

Signature · 1 line
Adam Langley [email protected] http://www.imperialviolet.org
#7 lore raw

ipv[46]: Fix MD5 signatures for non-linear skbs

Currently, the MD5 code assumes that the SKBs are linear and, in the case that they aren't, happily goes off and hashes off the end of the SKB and into random memory.

Reported by Stephen Hemminger in [1]. Advice thanks to Stephen and Evgeniy Polyakov. Also includes a couple of missed route_caps from Stephen's patch in [2].

[1] http://marc.info/?l=linux-netdev&m=121445989106145&w=2
[2] http://marc.info/?l=linux-netdev&m=121459157816964&w=2

---

This version is against net-next-2.6: 768cea593522d816b374149f3bf35e7c246fc8d0

 include/net/tcp.h     |   16 ++----
 net/ipv4/tcp.c        |  121 ++++++++++++++++++------------------------
 net/ipv4/tcp_ipv4.c   |  140 +++++++++++++++++++++++++++++-------------------
 net/ipv4/tcp_output.c |   14 ++---
 net/ipv6/tcp_ipv6.c   |  127 +++++++++++++++++++++++++++++---------------
 5 files changed, 229 insertions(+), 189 deletions(-)
include/net/tcp.h +6 -10
index 6b827cc..f7dcd52 100644
@@ -1122,13 +1122,6 @@ extern int			tcp_calc_md5_hash(char *md5_hash,
 						  unsigned int tcplen,
 						  struct tcp_md5sig_pool *hp);
 
-extern int			tcp_v4_calc_md5_hash(char *md5_hash,
-						     struct tcp_md5sig_key *key,
-						     struct sock *sk,
-						     struct dst_entry *dst,
-						     struct request_sock *req,
-						     struct tcphdr *th,
-						     unsigned int tcplen);
 extern struct tcp_md5sig_key	*tcp_v4_md5_lookup(struct sock *sk,
 						   struct sock *addr_sk);
 
@@ -1155,6 +1148,11 @@ extern void			tcp_free_md5sig_pool(void);
 
 extern struct tcp_md5sig_pool	*__tcp_get_md5sig_pool(int cpu);
 extern void			__tcp_put_md5sig_pool(void);
+extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, struct tcphdr *);
+extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, struct sk_buff *,
+				 unsigned header_len);
+extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
+			    struct tcp_md5sig_key *key);
 
 static inline
 struct tcp_md5sig_pool		*tcp_get_md5sig_pool(void)
@@ -1384,10 +1382,8 @@ struct tcp_sock_af_ops {
 	int			(*calc_md5_hash) (char *location,
 						  struct tcp_md5sig_key *md5,
 						  struct sock *sk,
-						  struct dst_entry *dst,
 						  struct request_sock *req,
-						  struct tcphdr *th,
-						  unsigned int len);
+						  struct sk_buff *skb);
 	int			(*md5_add) (struct sock *sk,
 					    struct sock *addr_sk,
 					    u8 *newkey,
net/ipv4/tcp.c +51 -70

133 lines not on this page · raw message

net/ipv4/tcp_ipv4.c +84 -56

214 lines not on this page · raw message

net/ipv4/tcp_output.c +5 -9
index edef2af..ac3d89d 100644
@@ -540,8 +540,10 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 	 * room for it.
 	 */
 	md5 = tp->af_specific->md5_lookup(sk, sk);
-	if (md5)
+	if (md5) {
 		tcp_header_size += TCPOLEN_MD5SIG_ALIGNED;
+		sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+	}
 #endif
 
 	skb_push(skb, tcp_header_size);
@@ -602,10 +604,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 	/* Calculate the MD5 hash, as we have all we need now */
 	if (md5) {
 		tp->af_specific->calc_md5_hash(md5_hash_location,
-					       md5,
-					       sk, NULL, NULL,
-					       tcp_hdr(skb),
-					       skb->len);
+					       md5, sk, NULL, skb);
 	}
 #endif
 
@@ -2264,10 +2263,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 	/* Okay, we have all we need - do the md5 hash if needed */
 	if (md5) {
 		tp->af_specific->calc_md5_hash(md5_hash_location,
-					       md5,
-					       NULL, dst, req,
-					       tcp_hdr(skb),
-					       skb->len);
+					       md5, NULL, req, skb);
 	}
 #endif
 
net/ipv6/tcp_ipv6.c +83 -44

191 lines not on this page · raw message

#8 lore raw

RFC 1/1 mark MD5 as broken in older (stable) kernels

In reply to Adam Langley

The following should probably be sent to stable kernel tree to disable MD5 in older kernels. It will avoid problems for all those vendor kernels.

net/ipv4/Kconfig +3 -1
@@ -622,7 +622,7 @@ config DEFAULT_TCP_CONG
 
 config TCP_MD5SIG
 	bool "TCP: MD5 Signature Option support (RFC2385) (EXPERIMENTAL)"
-	depends on EXPERIMENTAL
+	depends on EXPERIMENTAL && BROKEN
 	select CRYPTO
 	select CRYPTO_MD5
 	---help---
@@ -631,6 +631,8 @@ config TCP_MD5SIG
 	  on the Internet.
 
 	  If unsure, say N.
+	  The current version is broken for case of TCP SACK and devices that
+          use TSO or Scatter/Gather.
 
 source "net/ipv4/ipvs/Kconfig"
 
author Acked-by on v1 1/1
#9 lore raw

Re: [RFC] mark MD5 as broken in older (stable) kernels

Quoted 3 lines · Stephen Hemminger

The following should probably be sent to stable kernel tree to disable MD5 in older kernels. It will avoid problems for all those vendor kernels.

Signature · 1 line
Adam Langley [email protected] http://www.imperialviolet.org
maintainer
#10 lore raw

Re: [RFC] mark MD5 as broken in older (stable) kernels

In reply to Stephen Hemminger

From: Stephen Hemminger <[email protected]>
Date: Wed, 9 Jul 2008 09:58:51 -0700

Quoted 3 lines

The following should probably be sent to stable kernel tree to disable MD5 in older kernels. It will avoid problems for all those vendor kernels.

If we're going to take the time to fix this, we should take the time to push a bug fix, not a disable, into -stable.

Part of this is my fault as I haven't had time to review the MD5 fixes being posted since I want to fix the TX multiqueue stuff so I can get it into 2.6.27, first.