aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_ipv4.c
diff options
context:
space:
mode:
authorAdam Langley <agl@imperialviolet.org>2008-07-19 03:01:42 -0400
committerDavid S. Miller <davem@davemloft.net>2008-07-19 03:01:42 -0400
commit49a72dfb8814c2d65bd9f8c9c6daf6395a1ec58d (patch)
tree38804d609f21503573bbdd8bb9af38df99275ff5 /net/ipv4/tcp_ipv4.c
parent845525a642c1c9e1335c33a274d4273906ee58eb (diff)
tcp: 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 Signed-off-by: Adam Langley <agl@imperialviolet.org> Acked-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
-rw-r--r--net/ipv4/tcp_ipv4.c140
1 files changed, 85 insertions, 55 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 29adc668ad51..5400d75ff17a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -87,9 +87,8 @@ int sysctl_tcp_low_latency __read_mostly;
87#ifdef CONFIG_TCP_MD5SIG 87#ifdef CONFIG_TCP_MD5SIG
88static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, 88static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk,
89 __be32 addr); 89 __be32 addr);
90static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, 90static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
91 __be32 saddr, __be32 daddr, 91 __be32 daddr, __be32 saddr, struct tcphdr *th);
92 struct tcphdr *th, unsigned int tcplen);
93#else 92#else
94static inline 93static inline
95struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr) 94struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
@@ -583,11 +582,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
583 arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED; 582 arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;
584 rep.th.doff = arg.iov[0].iov_len / 4; 583 rep.th.doff = arg.iov[0].iov_len / 4;
585 584
586 tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[1], 585 tcp_v4_md5_hash_hdr((__u8 *) &rep.opt[1],
587 key, 586 key, ip_hdr(skb)->daddr,
588 ip_hdr(skb)->daddr, 587 ip_hdr(skb)->saddr, &rep.th);
589 ip_hdr(skb)->saddr,
590 &rep.th, arg.iov[0].iov_len);
591 } 588 }
592#endif 589#endif
593 arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, 590 arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
@@ -657,11 +654,9 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
657 arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED; 654 arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;
658 rep.th.doff = arg.iov[0].iov_len/4; 655 rep.th.doff = arg.iov[0].iov_len/4;
659 656
660 tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[offset], 657 tcp_v4_md5_hash_hdr((__u8 *) &rep.opt[offset],
661 key, 658 key, ip_hdr(skb)->daddr,
662 ip_hdr(skb)->daddr, 659 ip_hdr(skb)->saddr, &rep.th);
663 ip_hdr(skb)->saddr,
664 &rep.th, arg.iov[0].iov_len);
665 } 660 }
666#endif 661#endif
667 arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, 662 arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
@@ -989,28 +984,16 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
989 newkey, cmd.tcpm_keylen); 984 newkey, cmd.tcpm_keylen);
990} 985}
991 986
992static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, 987static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
993 __be32 saddr, __be32 daddr, 988 __be32 daddr, __be32 saddr, int nbytes)
994 struct tcphdr *th,
995 unsigned int tcplen)
996{ 989{
997 struct tcp_md5sig_pool *hp;
998 struct tcp4_pseudohdr *bp; 990 struct tcp4_pseudohdr *bp;
999 int err; 991 struct scatterlist sg;
1000
1001 /*
1002 * Okay, so RFC2385 is turned on for this connection,
1003 * so we need to generate the MD5 hash for the packet now.
1004 */
1005
1006 hp = tcp_get_md5sig_pool();
1007 if (!hp)
1008 goto clear_hash_noput;
1009 992
1010 bp = &hp->md5_blk.ip4; 993 bp = &hp->md5_blk.ip4;
1011 994
1012 /* 995 /*
1013 * The TCP pseudo-header (in the order: source IP address, 996 * 1. the TCP pseudo-header (in the order: source IP address,
1014 * destination IP address, zero-padded protocol number, and 997 * destination IP address, zero-padded protocol number, and
1015 * segment length) 998 * segment length)
1016 */ 999 */
@@ -1018,48 +1001,95 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
1018 bp->daddr = daddr; 1001 bp->daddr = daddr;
1019 bp->pad = 0; 1002 bp->pad = 0;
1020 bp->protocol = IPPROTO_TCP; 1003 bp->protocol = IPPROTO_TCP;
1021 bp->len = htons(tcplen); 1004 bp->len = cpu_to_be16(nbytes);
1022 1005
1023 err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp), 1006 sg_init_one(&sg, bp, sizeof(*bp));
1024 th, tcplen, hp); 1007 return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp));
1025 if (err) 1008}
1009
1010static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
1011 __be32 daddr, __be32 saddr, struct tcphdr *th)
1012{
1013 struct tcp_md5sig_pool *hp;
1014 struct hash_desc *desc;
1015
1016 hp = tcp_get_md5sig_pool();
1017 if (!hp)
1018 goto clear_hash_noput;
1019 desc = &hp->md5_desc;
1020
1021 if (crypto_hash_init(desc))
1022 goto clear_hash;
1023 if (tcp_v4_md5_hash_pseudoheader(hp, daddr, saddr, th->doff << 2))
1024 goto clear_hash;
1025 if (tcp_md5_hash_header(hp, th))
1026 goto clear_hash;
1027 if (tcp_md5_hash_key(hp, key))
1028 goto clear_hash;
1029 if (crypto_hash_final(desc, md5_hash))
1026 goto clear_hash; 1030 goto clear_hash;
1027 1031
1028 /* Free up the crypto pool */
1029 tcp_put_md5sig_pool(); 1032 tcp_put_md5sig_pool();
1030out:
1031 return 0; 1033 return 0;
1034
1032clear_hash: 1035clear_hash:
1033 tcp_put_md5sig_pool(); 1036 tcp_put_md5sig_pool();
1034clear_hash_noput: 1037clear_hash_noput:
1035 memset(md5_hash, 0, 16); 1038 memset(md5_hash, 0, 16);
1036 goto out; 1039 return 1;
1037} 1040}
1038 1041
1039int tcp_v4_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, 1042int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
1040 struct sock *sk, 1043 struct sock *sk, struct request_sock *req,
1041 struct dst_entry *dst, 1044 struct sk_buff *skb)
1042 struct request_sock *req,
1043 struct tcphdr *th,
1044 unsigned int tcplen)
1045{ 1045{
1046 struct tcp_md5sig_pool *hp;
1047 struct hash_desc *desc;
1048 struct tcphdr *th = tcp_hdr(skb);
1046 __be32 saddr, daddr; 1049 __be32 saddr, daddr;
1047 1050
1048 if (sk) { 1051 if (sk) {
1049 saddr = inet_sk(sk)->saddr; 1052 saddr = inet_sk(sk)->saddr;
1050 daddr = inet_sk(sk)->daddr; 1053 daddr = inet_sk(sk)->daddr;
1054 } else if (req) {
1055 saddr = inet_rsk(req)->loc_addr;
1056 daddr = inet_rsk(req)->rmt_addr;
1051 } else { 1057 } else {
1052 struct rtable *rt = (struct rtable *)dst; 1058 const struct iphdr *iph = ip_hdr(skb);
1053 BUG_ON(!rt); 1059 saddr = iph->saddr;
1054 saddr = rt->rt_src; 1060 daddr = iph->daddr;
1055 daddr = rt->rt_dst;
1056 } 1061 }
1057 return tcp_v4_do_calc_md5_hash(md5_hash, key, 1062
1058 saddr, daddr, 1063 hp = tcp_get_md5sig_pool();
1059 th, tcplen); 1064 if (!hp)
1065 goto clear_hash_noput;
1066 desc = &hp->md5_desc;
1067
1068 if (crypto_hash_init(desc))
1069 goto clear_hash;
1070
1071 if (tcp_v4_md5_hash_pseudoheader(hp, daddr, saddr, skb->len))
1072 goto clear_hash;
1073 if (tcp_md5_hash_header(hp, th))
1074 goto clear_hash;
1075 if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2))
1076 goto clear_hash;
1077 if (tcp_md5_hash_key(hp, key))
1078 goto clear_hash;
1079 if (crypto_hash_final(desc, md5_hash))
1080 goto clear_hash;
1081
1082 tcp_put_md5sig_pool();
1083 return 0;
1084
1085clear_hash:
1086 tcp_put_md5sig_pool();
1087clear_hash_noput:
1088 memset(md5_hash, 0, 16);
1089 return 1;
1060} 1090}
1061 1091
1062EXPORT_SYMBOL(tcp_v4_calc_md5_hash); 1092EXPORT_SYMBOL(tcp_v4_md5_hash_skb);
1063 1093
1064static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb) 1094static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
1065{ 1095{
@@ -1104,10 +1134,9 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
1104 /* Okay, so this is hash_expected and hash_location - 1134 /* Okay, so this is hash_expected and hash_location -
1105 * so we need to calculate the checksum. 1135 * so we need to calculate the checksum.
1106 */ 1136 */
1107 genhash = tcp_v4_do_calc_md5_hash(newhash, 1137 genhash = tcp_v4_md5_hash_skb(newhash,
1108 hash_expected, 1138 hash_expected,
1109 iph->saddr, iph->daddr, 1139 NULL, NULL, skb);
1110 th, skb->len);
1111 1140
1112 if (genhash || memcmp(hash_location, newhash, 16) != 0) { 1141 if (genhash || memcmp(hash_location, newhash, 16) != 0) {
1113 if (net_ratelimit()) { 1142 if (net_ratelimit()) {
@@ -1356,6 +1385,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
1356 if (newkey != NULL) 1385 if (newkey != NULL)
1357 tcp_v4_md5_do_add(newsk, inet_sk(sk)->daddr, 1386 tcp_v4_md5_do_add(newsk, inet_sk(sk)->daddr,
1358 newkey, key->keylen); 1387 newkey, key->keylen);
1388 newsk->sk_route_caps &= ~NETIF_F_GSO_MASK;
1359 } 1389 }
1360#endif 1390#endif
1361 1391
@@ -1719,7 +1749,7 @@ struct inet_connection_sock_af_ops ipv4_specific = {
1719#ifdef CONFIG_TCP_MD5SIG 1749#ifdef CONFIG_TCP_MD5SIG
1720static struct tcp_sock_af_ops tcp_sock_ipv4_specific = { 1750static struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
1721 .md5_lookup = tcp_v4_md5_lookup, 1751 .md5_lookup = tcp_v4_md5_lookup,
1722 .calc_md5_hash = tcp_v4_calc_md5_hash, 1752 .calc_md5_hash = tcp_v4_md5_hash_skb,
1723 .md5_add = tcp_v4_md5_add_func, 1753 .md5_add = tcp_v4_md5_add_func,
1724 .md5_parse = tcp_v4_parse_md5_keys, 1754 .md5_parse = tcp_v4_parse_md5_keys,
1725}; 1755};