]>
Commit | Line | Data |
---|---|---|
ffc20820 GKH |
1 | From foo@baz Fri 31 May 2019 03:16:39 PM PDT |
2 | From: Eric Dumazet <edumazet@google.com> | |
3 | Date: Wed, 27 Mar 2019 12:40:33 -0700 | |
4 | Subject: inet: switch IP ID generator to siphash | |
5 | ||
6 | From: Eric Dumazet <edumazet@google.com> | |
7 | ||
8 | [ Upstream commit df453700e8d81b1bdafdf684365ee2b9431fb702 ] | |
9 | ||
10 | According to Amit Klein and Benny Pinkas, IP ID generation is too weak | |
11 | and might be used by attackers. | |
12 | ||
13 | Even with recent net_hash_mix() fix (netns: provide pure entropy for net_hash_mix()) | |
14 | having 64bit key and Jenkins hash is risky. | |
15 | ||
16 | It is time to switch to siphash and its 128bit keys. | |
17 | ||
18 | Signed-off-by: Eric Dumazet <edumazet@google.com> | |
19 | Reported-by: Amit Klein <aksecurity@gmail.com> | |
20 | Reported-by: Benny Pinkas <benny@pinkas.net> | |
21 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
22 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
23 | --- | |
24 | include/linux/siphash.h | 5 +++++ | |
25 | include/net/netns/ipv4.h | 2 ++ | |
26 | net/ipv4/route.c | 12 +++++++----- | |
27 | net/ipv6/output_core.c | 30 ++++++++++++++++-------------- | |
28 | 4 files changed, 30 insertions(+), 19 deletions(-) | |
29 | ||
30 | --- a/include/linux/siphash.h | |
31 | +++ b/include/linux/siphash.h | |
32 | @@ -21,6 +21,11 @@ typedef struct { | |
33 | u64 key[2]; | |
34 | } siphash_key_t; | |
35 | ||
36 | +static inline bool siphash_key_is_zero(const siphash_key_t *key) | |
37 | +{ | |
38 | + return !(key->key[0] | key->key[1]); | |
39 | +} | |
40 | + | |
41 | u64 __siphash_aligned(const void *data, size_t len, const siphash_key_t *key); | |
42 | #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS | |
43 | u64 __siphash_unaligned(const void *data, size_t len, const siphash_key_t *key); | |
44 | --- a/include/net/netns/ipv4.h | |
45 | +++ b/include/net/netns/ipv4.h | |
46 | @@ -9,6 +9,7 @@ | |
47 | #include <linux/uidgid.h> | |
48 | #include <net/inet_frag.h> | |
49 | #include <linux/rcupdate.h> | |
50 | +#include <linux/siphash.h> | |
51 | ||
52 | struct tcpm_hash_bucket; | |
53 | struct ctl_table_header; | |
54 | @@ -217,5 +218,6 @@ struct netns_ipv4 { | |
55 | unsigned int ipmr_seq; /* protected by rtnl_mutex */ | |
56 | ||
57 | atomic_t rt_genid; | |
58 | + siphash_key_t ip_id_key; | |
59 | }; | |
60 | #endif | |
61 | --- a/net/ipv4/route.c | |
62 | +++ b/net/ipv4/route.c | |
63 | @@ -500,15 +500,17 @@ EXPORT_SYMBOL(ip_idents_reserve); | |
64 | ||
65 | void __ip_select_ident(struct net *net, struct iphdr *iph, int segs) | |
66 | { | |
67 | - static u32 ip_idents_hashrnd __read_mostly; | |
68 | u32 hash, id; | |
69 | ||
70 | - net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd)); | |
71 | + /* Note the following code is not safe, but this is okay. */ | |
72 | + if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key))) | |
73 | + get_random_bytes(&net->ipv4.ip_id_key, | |
74 | + sizeof(net->ipv4.ip_id_key)); | |
75 | ||
76 | - hash = jhash_3words((__force u32)iph->daddr, | |
77 | + hash = siphash_3u32((__force u32)iph->daddr, | |
78 | (__force u32)iph->saddr, | |
79 | - iph->protocol ^ net_hash_mix(net), | |
80 | - ip_idents_hashrnd); | |
81 | + iph->protocol, | |
82 | + &net->ipv4.ip_id_key); | |
83 | id = ip_idents_reserve(hash, segs); | |
84 | iph->id = htons(id); | |
85 | } | |
86 | --- a/net/ipv6/output_core.c | |
87 | +++ b/net/ipv6/output_core.c | |
88 | @@ -10,15 +10,25 @@ | |
89 | #include <net/secure_seq.h> | |
90 | #include <linux/netfilter.h> | |
91 | ||
92 | -static u32 __ipv6_select_ident(struct net *net, u32 hashrnd, | |
93 | +static u32 __ipv6_select_ident(struct net *net, | |
94 | const struct in6_addr *dst, | |
95 | const struct in6_addr *src) | |
96 | { | |
97 | + const struct { | |
98 | + struct in6_addr dst; | |
99 | + struct in6_addr src; | |
100 | + } __aligned(SIPHASH_ALIGNMENT) combined = { | |
101 | + .dst = *dst, | |
102 | + .src = *src, | |
103 | + }; | |
104 | u32 hash, id; | |
105 | ||
106 | - hash = __ipv6_addr_jhash(dst, hashrnd); | |
107 | - hash = __ipv6_addr_jhash(src, hash); | |
108 | - hash ^= net_hash_mix(net); | |
109 | + /* Note the following code is not safe, but this is okay. */ | |
110 | + if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key))) | |
111 | + get_random_bytes(&net->ipv4.ip_id_key, | |
112 | + sizeof(net->ipv4.ip_id_key)); | |
113 | + | |
114 | + hash = siphash(&combined, sizeof(combined), &net->ipv4.ip_id_key); | |
115 | ||
116 | /* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve, | |
117 | * set the hight order instead thus minimizing possible future | |
118 | @@ -41,7 +51,6 @@ static u32 __ipv6_select_ident(struct ne | |
119 | */ | |
120 | __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb) | |
121 | { | |
122 | - static u32 ip6_proxy_idents_hashrnd __read_mostly; | |
123 | struct in6_addr buf[2]; | |
124 | struct in6_addr *addrs; | |
125 | u32 id; | |
126 | @@ -53,11 +62,7 @@ __be32 ipv6_proxy_select_ident(struct ne | |
127 | if (!addrs) | |
128 | return 0; | |
129 | ||
130 | - net_get_random_once(&ip6_proxy_idents_hashrnd, | |
131 | - sizeof(ip6_proxy_idents_hashrnd)); | |
132 | - | |
133 | - id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd, | |
134 | - &addrs[1], &addrs[0]); | |
135 | + id = __ipv6_select_ident(net, &addrs[1], &addrs[0]); | |
136 | return htonl(id); | |
137 | } | |
138 | EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); | |
139 | @@ -66,12 +71,9 @@ __be32 ipv6_select_ident(struct net *net | |
140 | const struct in6_addr *daddr, | |
141 | const struct in6_addr *saddr) | |
142 | { | |
143 | - static u32 ip6_idents_hashrnd __read_mostly; | |
144 | u32 id; | |
145 | ||
146 | - net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); | |
147 | - | |
148 | - id = __ipv6_select_ident(net, ip6_idents_hashrnd, daddr, saddr); | |
149 | + id = __ipv6_select_ident(net, daddr, saddr); | |
150 | return htonl(id); | |
151 | } | |
152 | EXPORT_SYMBOL(ipv6_select_ident); |