| From 190cc82489f46f9d88e73c81a47e14f80a791e1a Mon Sep 17 00:00:00 2001 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Tue, 9 Feb 2021 11:20:27 -0800 |
| Subject: tcp: change source port randomizarion at connect() time |
| |
| From: Eric Dumazet <edumazet@google.com> |
| |
| commit 190cc82489f46f9d88e73c81a47e14f80a791e1a upstream. |
| |
| RFC 6056 (Recommendations for Transport-Protocol Port Randomization) |
| provides good summary of why source selection needs extra care. |
| |
| David Dworken reminded us that linux implements Algorithm 3 |
| as described in RFC 6056 3.3.3 |
| |
| Quoting David : |
| In the context of the web, this creates an interesting info leak where |
| websites can count how many TCP connections a user's computer is |
| establishing over time. For example, this allows a website to count |
| exactly how many subresources a third party website loaded. |
| This also allows: |
| - Distinguishing between different users behind a VPN based on |
| distinct source port ranges. |
| - Tracking users over time across multiple networks. |
| - Covert communication channels between different browsers/browser |
| profiles running on the same computer |
| - Tracking what applications are running on a computer based on |
| the pattern of how fast source ports are getting incremented. |
| |
| Section 3.3.4 describes an enhancement, that reduces |
| attackers ability to use the basic information currently |
| stored into the shared 'u32 hint'. |
| |
| This change also decreases collision rate when |
| multiple applications need to connect() to |
| different destinations. |
| |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Reported-by: David Dworken <ddworken@google.com> |
| Cc: Willem de Bruijn <willemb@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| [SG: Adjusted context] |
| Signed-off-by: Stefan Ghinea <stefan.ghinea@windriver.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/inet_hashtables.c | 20 +++++++++++++++++--- |
| 1 file changed, 17 insertions(+), 3 deletions(-) |
| |
| --- a/net/ipv4/inet_hashtables.c |
| +++ b/net/ipv4/inet_hashtables.c |
| @@ -714,6 +714,17 @@ unlock: |
| } |
| EXPORT_SYMBOL_GPL(inet_unhash); |
| |
| +/* RFC 6056 3.3.4. Algorithm 4: Double-Hash Port Selection Algorithm |
| + * Note that we use 32bit integers (vs RFC 'short integers') |
| + * because 2^16 is not a multiple of num_ephemeral and this |
| + * property might be used by clever attacker. |
| + * RFC claims using TABLE_LENGTH=10 buckets gives an improvement, |
| + * we use 256 instead to really give more isolation and |
| + * privacy, this only consumes 1 KB of kernel memory. |
| + */ |
| +#define INET_TABLE_PERTURB_SHIFT 8 |
| +static u32 table_perturb[1 << INET_TABLE_PERTURB_SHIFT]; |
| + |
| int __inet_hash_connect(struct inet_timewait_death_row *death_row, |
| struct sock *sk, u32 port_offset, |
| int (*check_established)(struct inet_timewait_death_row *, |
| @@ -727,7 +738,7 @@ int __inet_hash_connect(struct inet_time |
| struct inet_bind_bucket *tb; |
| u32 remaining, offset; |
| int ret, i, low, high; |
| - static u32 hint; |
| + u32 index; |
| |
| if (port) { |
| head = &hinfo->bhash[inet_bhashfn(net, port, |
| @@ -752,7 +763,10 @@ int __inet_hash_connect(struct inet_time |
| if (likely(remaining > 1)) |
| remaining &= ~1U; |
| |
| - offset = (hint + port_offset) % remaining; |
| + net_get_random_once(table_perturb, sizeof(table_perturb)); |
| + index = hash_32(port_offset, INET_TABLE_PERTURB_SHIFT); |
| + |
| + offset = (READ_ONCE(table_perturb[index]) + port_offset) % remaining; |
| /* In first pass we try ports of @low parity. |
| * inet_csk_get_port() does the opposite choice. |
| */ |
| @@ -805,7 +819,7 @@ next_port: |
| return -EADDRNOTAVAIL; |
| |
| ok: |
| - hint += i + 2; |
| + WRITE_ONCE(table_perturb[index], READ_ONCE(table_perturb[index]) + i + 2); |
| |
| /* Head lock still held and bh's disabled */ |
| inet_bind_hash(sk, tb, port); |