aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp.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.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.c')
-rw-r--r--net/ipv4/tcp.c127
1 files changed, 57 insertions, 70 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)