aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
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
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')
-rw-r--r--net/ipv4/tcp.c127
-rw-r--r--net/ipv4/tcp_ipv4.c140
-rw-r--r--net/ipv4/tcp_output.c14
3 files changed, 147 insertions, 134 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 827e6132af5f..0b491bf03db4 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2465,76 +2465,6 @@ static unsigned long tcp_md5sig_users;
2465static struct tcp_md5sig_pool **tcp_md5sig_pool; 2465static struct tcp_md5sig_pool **tcp_md5sig_pool;
2466static DEFINE_SPINLOCK(tcp_md5sig_pool_lock); 2466static DEFINE_SPINLOCK(tcp_md5sig_pool_lock);
2467 2467
2468int tcp_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
2469 int bplen,
2470 struct tcphdr *th, unsigned int tcplen,
2471 struct tcp_md5sig_pool *hp)
2472{
2473 struct scatterlist sg[4];
2474 __u16 data_len;
2475 int block = 0;
2476 __sum16 cksum;
2477 struct hash_desc *desc = &hp->md5_desc;
2478 int err;
2479 unsigned int nbytes = 0;
2480
2481 sg_init_table(sg, 4);
2482
2483 /* 1. The TCP pseudo-header */
2484 sg_set_buf(&sg[block++], &hp->md5_blk, bplen);
2485 nbytes += bplen;
2486
2487 /* 2. The TCP header, excluding options, and assuming a
2488 * checksum of zero
2489 */
2490 cksum = th->check;
2491 th->check = 0;
2492 sg_set_buf(&sg[block++], th, sizeof(*th));
2493 nbytes += sizeof(*th);
2494
2495 /* 3. The TCP segment data (if any) */
2496 data_len = tcplen - (th->doff << 2);
2497 if (data_len > 0) {
2498 u8 *data = (u8 *)th + (th->doff << 2);
2499 sg_set_buf(&sg[block++], data, data_len);
2500 nbytes += data_len;
2501 }
2502
2503 /* 4. an independently-specified key or password, known to both
2504 * TCPs and presumably connection-specific
2505 */
2506 sg_set_buf(&sg[block++], key->key, key->keylen);
2507 nbytes += key->keylen;
2508
2509 sg_mark_end(&sg[block - 1]);
2510
2511 /* Now store the hash into the packet */
2512 err = crypto_hash_init(desc);
2513 if (err) {
2514 if (net_ratelimit())
2515 printk(KERN_WARNING "%s(): hash_init failed\n", __func__);
2516 return -1;
2517 }
2518 err = crypto_hash_update(desc, sg, nbytes);
2519 if (err) {
2520 if (net_ratelimit())
2521 printk(KERN_WARNING "%s(): hash_update failed\n", __func__);
2522 return -1;
2523 }
2524 err = crypto_hash_final(desc, md5_hash);
2525 if (err) {
2526 if (net_ratelimit())
2527 printk(KERN_WARNING "%s(): hash_final failed\n", __func__);
2528 return -1;
2529 }
2530
2531 /* Reset header */
2532 th->check = cksum;
2533
2534 return 0;
2535}
2536EXPORT_SYMBOL(tcp_calc_md5_hash);
2537
2538static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool **pool) 2468static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool **pool)
2539{ 2469{
2540 int cpu; 2470 int cpu;
@@ -2658,6 +2588,63 @@ void __tcp_put_md5sig_pool(void)
2658} 2588}
2659 2589
2660EXPORT_SYMBOL(__tcp_put_md5sig_pool); 2590EXPORT_SYMBOL(__tcp_put_md5sig_pool);
2591
2592int tcp_md5_hash_header(struct tcp_md5sig_pool *hp,
2593 struct tcphdr *th)
2594{
2595 struct scatterlist sg;
2596 int err;
2597
2598 __sum16 old_checksum = th->check;
2599 th->check = 0;
2600 /* options aren't included in the hash */
2601 sg_init_one(&sg, th, sizeof(struct tcphdr));
2602 err = crypto_hash_update(&hp->md5_desc, &sg, sizeof(struct tcphdr));
2603 th->check = old_checksum;
2604 return err;
2605}
2606
2607EXPORT_SYMBOL(tcp_md5_hash_header);
2608
2609int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp,
2610 struct sk_buff *skb, unsigned header_len)
2611{
2612 struct scatterlist sg;
2613 const struct tcphdr *tp = tcp_hdr(skb);
2614 struct hash_desc *desc = &hp->md5_desc;
2615 unsigned i;
2616 const unsigned head_data_len = skb_headlen(skb) > header_len ?
2617 skb_headlen(skb) - header_len : 0;
2618 const struct skb_shared_info *shi = skb_shinfo(skb);
2619
2620 sg_init_table(&sg, 1);
2621
2622 sg_set_buf(&sg, ((u8 *) tp) + header_len, head_data_len);
2623 if (crypto_hash_update(desc, &sg, head_data_len))
2624 return 1;
2625
2626 for (i = 0; i < shi->nr_frags; ++i) {
2627 const struct skb_frag_struct *f = &shi->frags[i];
2628 sg_set_page(&sg, f->page, f->size, f->page_offset);
2629 if (crypto_hash_update(desc, &sg, f->size))
2630 return 1;
2631 }
2632
2633 return 0;
2634}
2635
2636EXPORT_SYMBOL(tcp_md5_hash_skb_data);
2637
2638int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, struct tcp_md5sig_key *key)
2639{
2640 struct scatterlist sg;
2641
2642 sg_init_one(&sg, key->key, key->keylen);
2643 return crypto_hash_update(&hp->md5_desc, &sg, key->keylen);
2644}
2645
2646EXPORT_SYMBOL(tcp_md5_hash_key);
2647
2661#endif 2648#endif
2662 2649
2663void tcp_done(struct sock *sk) 2650void tcp_done(struct sock *sk)
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};
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 36a19707f67f..958ff486165f 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -540,8 +540,10 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
540 * room for it. 540 * room for it.
541 */ 541 */
542 md5 = tp->af_specific->md5_lookup(sk, sk); 542 md5 = tp->af_specific->md5_lookup(sk, sk);
543 if (md5) 543 if (md5) {
544 tcp_header_size += TCPOLEN_MD5SIG_ALIGNED; 544 tcp_header_size += TCPOLEN_MD5SIG_ALIGNED;
545 sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
546 }
545#endif 547#endif
546 548
547 skb_push(skb, tcp_header_size); 549 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,
602 /* Calculate the MD5 hash, as we have all we need now */ 604 /* Calculate the MD5 hash, as we have all we need now */
603 if (md5) { 605 if (md5) {
604 tp->af_specific->calc_md5_hash(md5_hash_location, 606 tp->af_specific->calc_md5_hash(md5_hash_location,
605 md5, 607 md5, sk, NULL, skb);
606 sk, NULL, NULL,
607 tcp_hdr(skb),
608 skb->len);
609 } 608 }
610#endif 609#endif
611 610
@@ -2264,10 +2263,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
2264 /* Okay, we have all we need - do the md5 hash if needed */ 2263 /* Okay, we have all we need - do the md5 hash if needed */
2265 if (md5) { 2264 if (md5) {
2266 tp->af_specific->calc_md5_hash(md5_hash_location, 2265 tp->af_specific->calc_md5_hash(md5_hash_location,
2267 md5, 2266 md5, NULL, req, skb);
2268 NULL, dst, req,
2269 tcp_hdr(skb),
2270 skb->len);
2271 } 2267 }
2272#endif 2268#endif
2273 2269