CVE-2019-10638 (C, published 2019-07-05T23:15Z)
Description: value: In the Linux kernel before 5.1.7, a device can be tracked by an attacker using the IP ID values the kernel produces for connection-less protocols (e.g., UDP and ICMP). When such traffic is sent to multiple destination IP addresses, it is possible to obtain hash collisions (of indices to the counter array) and thereby obtain the hashing key (via enumeration). An attack may be conducted by hosting a crafted web page that uses WebRTC or gQUIC to force UDP traffic to attacker-controlled IP addresses.
Severity: MEDIUM (CVSS v3 base score: 6.5)
Weakness: CWE-326 \u2014 Inadequate Encryption Strength
The product stores or transmits sensitive data using an encryption scheme that is theoretically sound, but is not strong enough for the level of protection required.
Repository: https://github.com/torvalds/linux
Files changed:
include/linux/siphash.h
include/net/netns/ipv4.h
net/ipv4/route.c
net/ipv6/output_core.c
Commit message: inet: switch IP ID generator to siphash

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>


Vulnerable code:
	static u32 ip_idents_hashrnd __read_mostly;
	net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd));
	hash = jhash_3words((__force u32)iph->daddr,
			    iph->protocol ^ net_hash_mix(net),
			    ip_idents_hashrnd);
static u32 __ipv6_select_ident(struct net *net, u32 hashrnd,
	hash = __ipv6_addr_jhash(dst, hashrnd);
	hash = __ipv6_addr_jhash(src, hash);
	hash ^= net_hash_mix(net);
	static u32 ip6_proxy_idents_hashrnd __read_mostly;
	net_get_random_once(&ip6_proxy_idents_hashrnd,
			    sizeof(ip6_proxy_idents_hashrnd));

	id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd,
				 &addrs[1], &addrs[0]);
	static u32 ip6_idents_hashrnd __read_mostly;
	net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));

	id = __ipv6_select_ident(net, ip6_idents_hashrnd, daddr, saddr);

Fixed code:
static inline bool siphash_key_is_zero(const siphash_key_t *key)
{
	return !(key->key[0] | key->key[1]);
}

#include <linux/siphash.h>
	siphash_key_t	ip_id_key;
	/* 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_3u32((__force u32)iph->daddr,
			    iph->protocol,
			    &net->ipv4.ip_id_key);
static u32 __ipv6_select_ident(struct net *net,
	const struct {
		struct in6_addr dst;
		struct in6_addr src;
	} __aligned(SIPHASH_ALIGNMENT) combined = {
		.dst = *dst,
		.src = *src,
	};
	/* 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 = __ipv6_select_ident(net, &addrs[1], &addrs[0]);
	id = __ipv6_select_ident(net, daddr, saddr);