Skip to content

Commit 49a72df

Browse files
agldavem330
authored andcommitted
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 <[email protected]> Acked-by: Stephen Hemminger <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 845525a commit 49a72df

File tree

5 files changed

+242
-195
lines changed

5 files changed

+242
-195
lines changed

include/net/tcp.h

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,20 +1112,12 @@ struct tcp_md5sig_pool {
11121112
#define TCP_MD5SIG_MAXKEYS (~(u32)0) /* really?! */
11131113

11141114
/* - functions */
1115-
extern int tcp_calc_md5_hash(char *md5_hash,
1116-
struct tcp_md5sig_key *key,
1117-
int bplen,
1118-
struct tcphdr *th,
1119-
unsigned int tcplen,
1120-
struct tcp_md5sig_pool *hp);
1121-
1122-
extern int tcp_v4_calc_md5_hash(char *md5_hash,
1123-
struct tcp_md5sig_key *key,
1124-
struct sock *sk,
1125-
struct dst_entry *dst,
1126-
struct request_sock *req,
1127-
struct tcphdr *th,
1128-
unsigned int tcplen);
1115+
extern int tcp_v4_md5_hash_skb(char *md5_hash,
1116+
struct tcp_md5sig_key *key,
1117+
struct sock *sk,
1118+
struct request_sock *req,
1119+
struct sk_buff *skb);
1120+
11291121
extern struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
11301122
struct sock *addr_sk);
11311123

@@ -1152,6 +1144,11 @@ extern void tcp_free_md5sig_pool(void);
11521144

11531145
extern struct tcp_md5sig_pool *__tcp_get_md5sig_pool(int cpu);
11541146
extern void __tcp_put_md5sig_pool(void);
1147+
extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, struct tcphdr *);
1148+
extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, struct sk_buff *,
1149+
unsigned header_len);
1150+
extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
1151+
struct tcp_md5sig_key *key);
11551152

11561153
static inline
11571154
struct tcp_md5sig_pool *tcp_get_md5sig_pool(void)
@@ -1381,10 +1378,8 @@ struct tcp_sock_af_ops {
13811378
int (*calc_md5_hash) (char *location,
13821379
struct tcp_md5sig_key *md5,
13831380
struct sock *sk,
1384-
struct dst_entry *dst,
13851381
struct request_sock *req,
1386-
struct tcphdr *th,
1387-
unsigned int len);
1382+
struct sk_buff *skb);
13881383
int (*md5_add) (struct sock *sk,
13891384
struct sock *addr_sk,
13901385
u8 *newkey,

net/ipv4/tcp.c

Lines changed: 57 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2465,76 +2465,6 @@ static unsigned long tcp_md5sig_users;
24652465
static struct tcp_md5sig_pool **tcp_md5sig_pool;
24662466
static DEFINE_SPINLOCK(tcp_md5sig_pool_lock);
24672467

2468-
int 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-
}
2536-
EXPORT_SYMBOL(tcp_calc_md5_hash);
2537-
25382468
static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool **pool)
25392469
{
25402470
int cpu;
@@ -2658,6 +2588,63 @@ void __tcp_put_md5sig_pool(void)
26582588
}
26592589

26602590
EXPORT_SYMBOL(__tcp_put_md5sig_pool);
2591+
2592+
int 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+
2607+
EXPORT_SYMBOL(tcp_md5_hash_header);
2608+
2609+
int 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+
2636+
EXPORT_SYMBOL(tcp_md5_hash_skb_data);
2637+
2638+
int 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+
2646+
EXPORT_SYMBOL(tcp_md5_hash_key);
2647+
26612648
#endif
26622649

26632650
void tcp_done(struct sock *sk)

0 commit comments

Comments
 (0)