| From 75ff39ccc1bd5d3c455b6822ab09e533c551f758 Mon Sep 17 00:00:00 2001 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Sun, 10 Jul 2016 10:04:02 +0200 |
| Subject: tcp: make challenge acks less predictable |
| |
| commit 75ff39ccc1bd5d3c455b6822ab09e533c551f758 upstream. |
| |
| Yue Cao claims that current host rate limiting of challenge ACKS |
| (RFC 5961) could leak enough information to allow a patient attacker |
| to hijack TCP sessions. He will soon provide details in an academic |
| paper. |
| |
| This patch increases the default limit from 100 to 1000, and adds |
| some randomization so that the attacker can no longer hijack |
| sessions without spending a considerable amount of probes. |
| |
| Based on initial analysis and patch from Linus. |
| |
| Note that we also have per socket rate limiting, so it is tempting |
| to remove the host limit in the future. |
| |
| v2: randomize the count of challenge acks per second, not the period. |
| |
| Fixes: 282f23c6ee34 ("tcp: implement RFC 5961 3.2") |
| Reported-by: Yue Cao <ycao009@ucr.edu> |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Yuchung Cheng <ycheng@google.com> |
| Cc: Neal Cardwell <ncardwell@google.com> |
| Acked-by: Neal Cardwell <ncardwell@google.com> |
| Acked-by: Yuchung Cheng <ycheng@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| [lizf: Backported to 3.4: |
| - adjust context |
| - use ACCESS_ONCE instead WRITE_ONCE/READ_ONCE |
| - open-code prandom_u32_max()] |
| Signed-off-by: Zefan Li <lizefan@huawei.com> |
| --- |
| net/ipv4/tcp_input.c | 13 +++++++++---- |
| 1 file changed, 9 insertions(+), 4 deletions(-) |
| |
| --- a/net/ipv4/tcp_input.c |
| +++ b/net/ipv4/tcp_input.c |
| @@ -89,7 +89,7 @@ int sysctl_tcp_adv_win_scale __read_most |
| EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); |
| |
| /* rfc5961 challenge ack rate limiting */ |
| -int sysctl_tcp_challenge_ack_limit = 100; |
| +int sysctl_tcp_challenge_ack_limit = 1000; |
| |
| int sysctl_tcp_stdurg __read_mostly; |
| int sysctl_tcp_rfc1337 __read_mostly; |
| @@ -3701,13 +3701,18 @@ static void tcp_send_challenge_ack(struc |
| /* unprotected vars, we dont care of overwrites */ |
| static u32 challenge_timestamp; |
| static unsigned int challenge_count; |
| - u32 now = jiffies / HZ; |
| + u32 count, now = jiffies / HZ; |
| |
| if (now != challenge_timestamp) { |
| + u32 half = (sysctl_tcp_challenge_ack_limit + 1) >> 1; |
| + |
| challenge_timestamp = now; |
| - challenge_count = 0; |
| + ACCESS_ONCE(challenge_count) = half + |
| + (u32)(((u64)random32() * sysctl_tcp_challenge_ack_limit) >> 32); |
| } |
| - if (++challenge_count <= sysctl_tcp_challenge_ack_limit) { |
| + count = ACCESS_ONCE(challenge_count); |
| + if (count > 0) { |
| + ACCESS_ONCE(challenge_count) = count - 1; |
| NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK); |
| tcp_send_ack(sk); |
| } |