lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Tue, 1 Jul 2008 15:38:14 -0700
From:	"Adam Langley" <agl@...erialviolet.org>
To:	Stephen Hemminger <shemminger@...tta.com>
Cc:	netdev@...r.kernel.org
Subject: Re: MD5 SG fix

Cut three,
  * I should use sg_set_page, not sg_set_buf with page_address
  * Make the version that tcp_output etc use take an SKB
  * Replicate fix to IPv6

Cheers,


AGL

 include/net/tcp.h     |    6 +--
 net/ipv4/tcp_ipv4.c   |  112 +++++++++++++++++++++++++++++++++++++------------
 net/ipv4/tcp_output.c |   11 ++---
 net/ipv6/tcp_ipv6.c   |   65 ++++++++++++++++++----------
 4 files changed, 133 insertions(+), 61 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 633147c..a9130a1 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1120,9 +1120,8 @@ extern int			tcp_v4_calc_md5_hash(char *md5_hash,
 						     struct sock *sk,
 						     struct dst_entry *dst,
 						     struct request_sock *req,
-						     struct tcphdr *th,
 						     int protocol,
-						     unsigned int tcplen);
+						     struct sk_buff *skb);
 extern struct tcp_md5sig_key	*tcp_v4_md5_lookup(struct sock *sk,
 						   struct sock *addr_sk);
 
@@ -1370,9 +1369,8 @@ struct tcp_sock_af_ops {
 						  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,
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index cd601a8..90a5f41 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -95,8 +95,9 @@ static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk,
 						   __be32 addr);
 static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
 				   __be32 saddr, __be32 daddr,
-				   struct tcphdr *th, int protocol,
-				   unsigned int tcplen);
+				   int protocol,
+				   struct tcphdr *th, int data_off, int tcplen,
+				   struct skb_shared_info *frags);
 #endif
 
 struct inet_hashinfo __cacheline_aligned tcp_hashinfo = {
@@ -586,8 +587,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
 					key,
 					ip_hdr(skb)->daddr,
 					ip_hdr(skb)->saddr,
-					&rep.th, IPPROTO_TCP,
-					arg.iov[0].iov_len);
+					IPPROTO_TCP, &rep.th,
+					arg.iov[0].iov_len, arg.iov[0].iov_len,
+					NULL);
 	}
 #endif
 	arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
@@ -680,8 +682,9 @@ static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk,
 					key,
 					ip_hdr(skb)->daddr,
 					ip_hdr(skb)->saddr,
-					&rep.th, IPPROTO_TCP,
-					arg.iov[0].iov_len);
+					IPPROTO_TCP, &rep.th,
+					arg.iov[0].iov_len, arg.iov[0].iov_len,
+					NULL);
 	}
 #endif
 	arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
@@ -1004,20 +1007,56 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
 				 newkey, cmd.tcpm_keylen);
 }
 
+/*
+static void
+md5_dump(struct scatterlist *sglist, int nsg, unsigned nbytes, int data_off, int tcplen, char frags) {
+	struct scatterlist *sg;
+	unsigned e;
+
+	printk(KERN_INFO "MD5: %d %d %d %d %d\n", nsg, nbytes, data_off, tcplen, frags);
+	for_each_sg(sglist, sg, nsg, e) {
+		u8 *buf = sg_virt(sg);
+		unsigned i;
+
+		for (i = 0; i < sg->length; ++i)
+			printk(" %02x", buf[i]);
+
+		printk("\n");
+	}
+
+	printk("\n");
+}*/
+
+/**
+ * tcp_v4_do_calc_md5_hash - calculate an MD5 hash (RFC 2385)
+ * @md5_hash: (output) a 16 byte space into which the MD5 sig is written
+ * @key: the key that is appened to the hash input
+ * @saddr: source IP address for the packet
+ * @daddr: destination IP address for the packet
+ * @protocol: the protocol number in the IP header (see the RFC)
+ * @th: the TCP header, followed by options and (optional) data
+ * @data_off: the offset of the optional data (in bytes) from @th
+ * @tcplen: the length of the buffer (in bytes) pointed to by @th. If
+ *   @tcplen == @data_off then there is no data following the header
+ * @frags: (maybe NULL) a list of additional fragments of data
+ *
+ * We don't always have the SKB when this function is called, thus the pointer
+ * to the TCP header and all the length arguments.
+ */
 static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
-				   __be32 saddr, __be32 daddr,
-				   struct tcphdr *th, int protocol,
-				   unsigned int tcplen)
+				   __be32 saddr, __be32 daddr, int protocol,
+				   struct tcphdr *th, int data_off, int tcplen,
+				   struct skb_shared_info *frags)
 {
-	struct scatterlist sg[4];
-	__u16 data_len;
+	struct scatterlist sg[MAX_SKB_FRAGS + 3];
+	const int head_data_len = tcplen - data_off;
 	int block = 0;
 	__sum16 old_checksum;
 	struct tcp_md5sig_pool *hp;
 	struct tcp4_pseudohdr *bp;
 	struct hash_desc *desc;
-	int err;
-	unsigned int nbytes = 0;
+	int err, i;
+	unsigned int nbytes = tcplen;
 
 	/*
 	 * Okay, so RFC2385 is turned on for this connection,
@@ -1040,9 +1079,14 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
 	bp->daddr = daddr;
 	bp->pad = 0;
 	bp->protocol = protocol;
-	bp->len = htons(tcplen);
+	if (frags)
+		for (i = 0; i < frags->nr_frags; ++i)
+			nbytes += frags->frags[i].size;
+	bp->len = htons(nbytes);
+	nbytes = 0;
 
-	sg_init_table(sg, 4);
+	sg_init_table(sg, 3 + (head_data_len > 0) +
+		      (frags ? frags->nr_frags : 0));
 
 	sg_set_buf(&sg[block++], bp, sizeof(*bp));
 	nbytes += sizeof(*bp);
@@ -1056,11 +1100,19 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
 	nbytes += sizeof(struct tcphdr);
 
 	/* 3. the TCP segment data (if any) */
-	data_len = tcplen - (th->doff << 2);
-	if (data_len > 0) {
-		unsigned char *data = (unsigned char *)th + (th->doff << 2);
-		sg_set_buf(&sg[block++], data, data_len);
-		nbytes += data_len;
+	if (head_data_len > 0) {
+		unsigned char *data = (unsigned char *)th + data_off;
+		sg_set_buf(&sg[block++], data, head_data_len);
+		nbytes += head_data_len;
+	}
+
+	if (frags) {
+		for (i = 0; i < frags->nr_frags; ++i) {
+			const struct skb_frag_struct *f = &frags->frags[i];
+			sg_set_page(&sg[block++], f->page, f->size,
+				    f->page_offset);
+			nbytes += f->size;
+		}
 	}
 
 	/* 4. an independently-specified key or password, known to both
@@ -1069,7 +1121,7 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
 	sg_set_buf(&sg[block++], key->key, key->keylen);
 	nbytes += key->keylen;
 
-	sg_mark_end(&sg[block - 1]);
+	/*md5_dump(sg, block, nbytes, data_off, tcplen, frags != NULL);*/
 
 	/* Now store the Hash into the packet */
 	err = crypto_hash_init(desc);
@@ -1099,10 +1151,10 @@ 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 protocol, struct sk_buff *skb)
 {
 	__be32 saddr, daddr;
+	struct tcphdr *th = tcp_hdr(skb);
 
 	if (sk) {
 		saddr = inet_sk(sk)->saddr;
@@ -1113,9 +1165,10 @@ int tcp_v4_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
 		saddr = rt->rt_src;
 		daddr = rt->rt_dst;
 	}
-	return tcp_v4_do_calc_md5_hash(md5_hash, key,
-				       saddr, daddr,
-				       th, protocol, tcplen);
+	return tcp_v4_do_calc_md5_hash(md5_hash, key, saddr, daddr, protocol,
+				       th, th->doff << 2, skb_headlen(skb),
+				       skb_is_nonlinear(skb) ?
+					 skb_shinfo(skb) : NULL);
 }
 
 EXPORT_SYMBOL(tcp_v4_calc_md5_hash);
@@ -1206,8 +1259,10 @@ done_opts:
 	genhash = tcp_v4_do_calc_md5_hash(newhash,
 					  hash_expected,
 					  iph->saddr, iph->daddr,
-					  th, sk->sk_protocol,
-					  skb->len);
+					  sk->sk_protocol, th, th->doff << 2,
+					  skb_headlen(skb),
+					  skb_is_nonlinear(skb) ?
+						skb_shinfo(skb) : NULL);
 
 	if (genhash || memcmp(hash_location, newhash, 16) != 0) {
 		if (net_ratelimit()) {
@@ -1456,6 +1511,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 		if (newkey != NULL)
 			tcp_v4_md5_do_add(newsk, inet_sk(sk)->daddr,
 					  newkey, key->keylen);
+		newsk->sk_route_caps &= ~NETIF_F_GSO_MASK;
 	}
 #endif
 
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index e399bde..07269e9 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -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);
@@ -606,9 +608,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 		tp->af_specific->calc_md5_hash(md5_hash_location,
 					       md5,
 					       sk, NULL, NULL,
-					       tcp_hdr(skb),
-					       sk->sk_protocol,
-					       skb->len);
+					       sk->sk_protocol, skb);
 	}
 #endif
 
@@ -2264,8 +2264,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 		tp->af_specific->calc_md5_hash(md5_hash_location,
 					       md5,
 					       NULL, dst, req,
-					       tcp_hdr(skb), sk->sk_protocol,
-					       skb->len);
+					       sk->sk_protocol, skb);
 	}
 #endif
 
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 715965f..b547a08 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -736,18 +736,19 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
 static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
 				   struct in6_addr *saddr,
 				   struct in6_addr *daddr,
-				   struct tcphdr *th, int protocol,
-				   unsigned int tcplen)
+				   int protocol,
+				   struct tcphdr *th, int data_off, int tcplen,
+				   struct skb_shared_info *frags)
 {
-	struct scatterlist sg[4];
-	__u16 data_len;
+	struct scatterlist sg[MAX_SKB_FRAGS + 3];
+	const int head_data_len = tcplen - data_off;
 	int block = 0;
 	__sum16 cksum;
 	struct tcp_md5sig_pool *hp;
 	struct tcp6_pseudohdr *bp;
 	struct hash_desc *desc;
-	int err;
-	unsigned int nbytes = 0;
+	int err, i;
+	unsigned int nbytes = tcplen;
 
 	hp = tcp_get_md5sig_pool();
 	if (!hp) {
@@ -760,10 +761,15 @@ static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
 	/* 1. TCP pseudo-header (RFC2460) */
 	ipv6_addr_copy(&bp->saddr, saddr);
 	ipv6_addr_copy(&bp->daddr, daddr);
-	bp->len = htonl(tcplen);
 	bp->protocol = htonl(protocol);
+	if (frags)
+		for (i = 0; i < frags->nr_frags; ++i)
+			nbytes += frags->frags[i].size;
+	bp->len = htonl(nbytes);
+	nbytes = 0;
 
-	sg_init_table(sg, 4);
+	sg_init_table(sg, 3 + (head_data_len > 0) +
+		      (frags ? frags->nr_frags : 0));
 
 	sg_set_buf(&sg[block++], bp, sizeof(*bp));
 	nbytes += sizeof(*bp);
@@ -775,19 +781,25 @@ static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
 	nbytes += sizeof(*th);
 
 	/* 3. TCP segment data (if any) */
-	data_len = tcplen - (th->doff << 2);
-	if (data_len > 0) {
-		u8 *data = (u8 *)th + (th->doff << 2);
-		sg_set_buf(&sg[block++], data, data_len);
-		nbytes += data_len;
+	if (head_data_len > 0) {
+		u8 *data = (u8 *)th + data_off;
+		sg_set_buf(&sg[block++], data, head_data_len);
+		nbytes += head_data_len;
+	}
+
+	if (frags) {
+		for (i = 0; i < frags->nr_frags; ++i) {
+			const struct skb_frag_struct *f = &frags->frags[i];
+			sg_set_page(&sg[block++], f->page, f->size,
+				    f->page_offset);
+			nbytes += f->size;
+		}
 	}
 
 	/* 4. shared key */
 	sg_set_buf(&sg[block++], key->key, key->keylen);
 	nbytes += key->keylen;
 
-	sg_mark_end(&sg[block - 1]);
-
 	/* Now store the hash into the packet */
 	err = crypto_hash_init(desc);
 	if (err) {
@@ -821,10 +833,11 @@ static int tcp_v6_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 protocol,
+				struct sk_buff *skb)
 {
 	struct in6_addr *saddr, *daddr;
+	struct tcphdr *th = tcp_hdr(skb);
 
 	if (sk) {
 		saddr = &inet6_sk(sk)->saddr;
@@ -834,8 +847,10 @@ static int tcp_v6_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
 		daddr = &inet6_rsk(req)->rmt_addr;
 	}
 	return tcp_v6_do_calc_md5_hash(md5_hash, key,
-				       saddr, daddr,
-				       th, protocol, tcplen);
+				       saddr, daddr, protocol,
+				       th, th->doff << 2, skb_headlen(skb),
+				       skb_is_nonlinear(skb) ?
+					 skb_shinfo(skb) : NULL);
 }
 
 static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
@@ -910,8 +925,10 @@ done_opts:
 	genhash = tcp_v6_do_calc_md5_hash(newhash,
 					  hash_expected,
 					  &ip6h->saddr, &ip6h->daddr,
-					  th, sk->sk_protocol,
-					  skb->len);
+					  sk->sk_protocol, th,
+					  th->doff << 2, skb_headlen(skb),
+					  skb_is_nonlinear(skb) ?
+					    skb_shinfo(skb) : NULL);
 	if (genhash || memcmp(hash_location, newhash, 16) != 0) {
 		if (net_ratelimit()) {
 			printk(KERN_INFO "MD5 Hash %s for "
@@ -1051,7 +1068,8 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
 		tcp_v6_do_calc_md5_hash((__u8 *)&opt[1], key,
 					&ipv6_hdr(skb)->daddr,
 					&ipv6_hdr(skb)->saddr,
-					t1, IPPROTO_TCP, tot_len);
+					IPPROTO_TCP, t1, tot_len,
+					tot_len, NULL);
 	}
 #endif
 
@@ -1157,7 +1175,8 @@ static void tcp_v6_send_ack(struct tcp_timewait_sock *tw,
 		tcp_v6_do_calc_md5_hash((__u8 *)topt, key,
 					&ipv6_hdr(skb)->daddr,
 					&ipv6_hdr(skb)->saddr,
-					t1, IPPROTO_TCP, tot_len);
+					IPPROTO_TCP, t1, tot_len,
+					tot_len, NULL);
 	}
 #endif
 
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ