]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/5.1.7/inet-switch-ip-id-generator-to-siphash.patch
Linux 5.1.7
[thirdparty/kernel/stable-queue.git] / releases / 5.1.7 / inet-switch-ip-id-generator-to-siphash.patch
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);