inet: switch IP ID generator to siphash

commit df453700e8d81b1bdafdf684365ee2b9431fb702 upstream.

According to Amit Klein and Benny Pinkas, IP ID generation is too weak
and might be used by attackers.

Even with recent net_hash_mix() fix (netns: provide pure entropy for net_hash_mix())
having 64bit key and Jenkins hash is risky.

It is time to switch to siphash and its 128bit keys.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Amit Klein <aksecurity@gmail.com>
Reported-by: Benny Pinkas <benny@pinkas.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
[bwh: Backported to 3.16: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
CVE-2019-10638
Signed-off-by: Kevin F. Haggerty <haggertk@lineageos.org>
Change-Id: I607618745f8725e7318ec60e470a77bf0e53df8b
This commit is contained in:
Eric Dumazet 2019-03-27 12:40:33 -07:00 committed by matteo0026
parent 8d0da84afe
commit e6e21b054b
5 changed files with 45 additions and 32 deletions

View File

@ -19,6 +19,11 @@ typedef struct {
u64 key[2];
} siphash_key_t;
static inline bool siphash_key_is_zero(const siphash_key_t *key)
{
return !(key->key[0] | key->key[1]);
}
u64 __siphash_aligned(const void *data, size_t len, const siphash_key_t *key);
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
u64 __siphash_unaligned(const void *data, size_t len, const siphash_key_t *key);

View File

@ -6,6 +6,7 @@
#define __NETNS_IPV4_H__
#include <net/inet_frag.h>
#include <linux/siphash.h>
struct ctl_table_header;
struct ipv4_devconf;
@ -68,5 +69,6 @@ struct netns_ipv4 {
struct fib_rules_ops *mr_rules_ops;
#endif
#endif
siphash_key_t ip_id_key;
};
#endif

View File

@ -1400,19 +1400,17 @@ EXPORT_SYMBOL(ip_idents_reserve);
void __ip_select_ident(struct net *net, struct iphdr *iph, int segs)
{
static u32 ip_idents_hashrnd __read_mostly;
static bool hashrnd_initialized = false;
u32 hash, id;
if (unlikely(!hashrnd_initialized)) {
hashrnd_initialized = true;
get_random_bytes(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd));
}
/* Note the following code is not safe, but this is okay. */
if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key)))
get_random_bytes(&net->ipv4.ip_id_key,
sizeof(net->ipv4.ip_id_key));
hash = jhash_3words((__force u32)iph->daddr,
hash = siphash_3u32((__force u32)iph->daddr,
(__force u32)iph->saddr,
iph->protocol ^ net_hash_mix(net),
ip_idents_hashrnd);
iph->protocol,
&net->ipv4.ip_id_key);
id = ip_idents_reserve(hash, segs);
iph->id = htons(id);
}

View File

@ -604,17 +604,21 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
void ipv6_select_ident(struct net *net, struct frag_hdr *fhdr, struct rt6_info *rt)
{
static u32 ip6_idents_hashrnd __read_mostly;
static bool hashrnd_initialized = false;
const struct {
struct in6_addr dst;
struct in6_addr src;
} __aligned(SIPHASH_ALIGNMENT) combined = {
.dst = rt->rt6i_dst.addr,
.src = rt->rt6i_src.addr,
};
u32 hash, id;
if (unlikely(!hashrnd_initialized)) {
hashrnd_initialized = true;
get_random_bytes(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));
}
hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd);
hash = __ipv6_addr_jhash(&rt->rt6i_src.addr, hash);
hash ^= net_hash_mix(net);
/* Note the following code is not safe, but this is okay. */
if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key)))
get_random_bytes(&net->ipv4.ip_id_key,
sizeof(net->ipv4.ip_id_key));
hash = siphash(&combined, sizeof(combined), &net->ipv4.ip_id_key);
id = ip_idents_reserve(hash, 1);
fhdr->identification = htonl(id);

View File

@ -11,8 +11,6 @@
*/
void ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
{
static u32 ip6_proxy_idents_hashrnd __read_mostly;
static bool hashrnd_initialized = false;
struct in6_addr buf[2];
struct in6_addr *addrs;
u32 hash, id;
@ -21,19 +19,25 @@ void ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
skb_network_offset(skb) +
offsetof(struct ipv6hdr, saddr),
sizeof(buf), buf);
if (!addrs)
return;
if (addrs)
{
const struct {
struct in6_addr dst;
struct in6_addr src;
} __aligned(SIPHASH_ALIGNMENT) combined = {
.dst = addrs[1],
.src = addrs[0],
};
if (unlikely(!hashrnd_initialized)) {
hashrnd_initialized = true;
get_random_bytes(&ip6_proxy_idents_hashrnd,
sizeof(ip6_proxy_idents_hashrnd));
/* Note the following code is not safe, but this is okay. */
if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key)))
get_random_bytes(&net->ipv4.ip_id_key,
sizeof(net->ipv4.ip_id_key));
hash = siphash(&combined, sizeof(combined), &net->ipv4.ip_id_key);
id = ip_idents_reserve(hash, 1);
skb_shinfo(skb)->ip6_frag_id = htonl(id);
}
hash = __ipv6_addr_jhash(&addrs[1], ip6_proxy_idents_hashrnd);
hash = __ipv6_addr_jhash(&addrs[0], hash);
hash ^= net_hash_mix(net);
id = ip_idents_reserve(hash, 1);
skb_shinfo(skb)->ip6_frag_id = htonl(id);
}
EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);