diff options
| -rw-r--r-- | CHANGELOG.md | 6 | ||||
| -rw-r--r-- | talpid-core/src/firewall/linux.rs | 38 | ||||
| -rw-r--r-- | talpid-core/src/firewall/macos.rs | 46 |
3 files changed, 63 insertions, 27 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ea79cf321..cff66c23d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,12 @@ Line wrap the file at 100 chars. Th - Fix crash that happened in certain situations when retrieving the relay list. - Fix crash caused by initialization race condition. +### Security +- Stop DNS leak that could happen on Linux and macOS if local network sharing was enabled + and the user's default DNS was on the local private network. The leak could happen during these + states: While connecting, when blocked due to an error happening and when disconnected if the + "block when disconnected" setting was enabled. + ## [2020.1] - 2020-02-10 This release is identical to 2020.1-beta1 diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs index e2f6f4f736..e291cc9050 100644 --- a/talpid-core/src/firewall/linux.rs +++ b/talpid-core/src/firewall/linux.rs @@ -351,6 +351,9 @@ impl<'a> PolicyBatch<'a> { } => { self.add_allow_icmp_pingable_hosts(&pingable_hosts); self.add_allow_endpoint_rules(peer_endpoint); + // Important to block DNS after allow relay rule (so the relay can operate + // over port 53) but before allow LAN (so DNS does not leak to the LAN) + self.add_drop_dns_rule(); *allow_lan } FirewallPolicy::Connected { @@ -359,15 +362,22 @@ impl<'a> PolicyBatch<'a> { allow_lan, } => { self.add_allow_endpoint_rules(peer_endpoint); - self.add_dns_rule(tunnel, TransportProtocol::Udp)?; - self.add_dns_rule(tunnel, TransportProtocol::Tcp)?; + self.add_allow_dns_rules(tunnel, TransportProtocol::Udp)?; + self.add_allow_dns_rules(tunnel, TransportProtocol::Tcp)?; + // Important to block DNS *before* we allow the tunnel and allow LAN. So DNS + // can't leak to the wrong IPs in the tunnel or on the LAN. + self.add_drop_dns_rule(); self.add_allow_tunnel_rules(tunnel)?; if *allow_lan { self.add_block_cve_2019_14899(tunnel); } *allow_lan } - FirewallPolicy::Blocked { allow_lan } => *allow_lan, + FirewallPolicy::Blocked { allow_lan } => { + // Important to drop DNS before allowing LAN (to stop DNS leaking to the LAN) + self.add_drop_dns_rule(); + *allow_lan + } }; if allow_lan { @@ -419,21 +429,16 @@ impl<'a> PolicyBatch<'a> { } } - fn add_dns_rule( + fn add_allow_dns_rules( &mut self, tunnel: &tunnel::TunnelMetadata, protocol: TransportProtocol, ) -> Result<()> { - // allow DNS traffic to the tunnel gateway + // allow DNS traffic to the tunnel gateway(s) self.add_allow_dns_rule(&tunnel.interface, protocol, tunnel.ipv4_gateway.into())?; if let Some(ipv6_gateway) = tunnel.ipv6_gateway { self.add_allow_dns_rule(&tunnel.interface, protocol, ipv6_gateway.into())?; }; - let mut block_rule = Rule::new(&self.out_chain); - check_port(&mut block_rule, protocol, End::Dst, 53); - add_verdict(&mut block_rule, &Verdict::Drop); - self.batch.add(&block_rule, nftnl::MsgType::Add); - Ok(()) } @@ -461,6 +466,19 @@ impl<'a> PolicyBatch<'a> { Ok(()) } + /// Blocks all outgoing DNS (port 53) on both TCP and UDP + fn add_drop_dns_rule(&mut self) { + let mut block_udp_rule = Rule::new(&self.out_chain); + check_port(&mut block_udp_rule, TransportProtocol::Udp, End::Dst, 53); + add_verdict(&mut block_udp_rule, &Verdict::Drop); + self.batch.add(&block_udp_rule, nftnl::MsgType::Add); + + let mut block_tcp_rule = Rule::new(&self.out_chain); + check_port(&mut block_tcp_rule, TransportProtocol::Tcp, End::Dst, 53); + add_verdict(&mut block_tcp_rule, &Verdict::Drop); + self.batch.add(&block_tcp_rule, nftnl::MsgType::Add); + } + fn add_allow_tunnel_rules(&mut self, tunnel: &tunnel::TunnelMetadata) -> Result<()> { self.batch.add( &allow_interface_rule(&self.out_chain, Direction::Out, &tunnel.interface[..])?, diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs index acf9b68cc6..41898f36cd 100644 --- a/talpid-core/src/firewall/macos.rs +++ b/talpid-core/src/firewall/macos.rs @@ -94,6 +94,9 @@ impl Firewall { let mut rules = vec![self.get_allow_relay_rule(peer_endpoint)?]; rules.extend(self.get_allow_pingable_hosts(&pingable_hosts)?); if allow_lan { + // Important to block DNS after allow relay rule (so the relay can operate + // over port 53) but before allow LAN (so DNS does not leak to the LAN) + rules.append(&mut self.get_block_dns_rules()?); rules.append(&mut self.get_allow_lan_rules()?); } Ok(rules) @@ -144,24 +147,12 @@ impl Firewall { rules.push(v6_dns_rule_udp); } - let block_tcp_dns_rule = self - .create_rule_builder(FilterRuleAction::Drop) - .direction(pfctl::Direction::Out) - .quick(true) - .proto(pfctl::Proto::Tcp) - .to(pfctl::Port::from(53)) - .build()?; - rules.push(block_tcp_dns_rule); - let block_udp_dns_rule = self - .create_rule_builder(FilterRuleAction::Drop) - .direction(pfctl::Direction::Out) - .quick(true) - .proto(pfctl::Proto::Udp) - .to(pfctl::Port::from(53)) - .build()?; - - rules.push(block_udp_dns_rule); rules.push(self.get_allow_relay_rule(peer_endpoint)?); + + // Important to block DNS *before* we allow the tunnel and allow LAN. So DNS + // can't leak to the wrong IPs in the tunnel or on the LAN. + rules.append(&mut self.get_block_dns_rules()?); + rules.push(self.get_allow_tunnel_rule(tunnel.interface.as_str())?); if allow_lan { @@ -173,6 +164,8 @@ impl Firewall { FirewallPolicy::Blocked { allow_lan } => { let mut rules = Vec::new(); if allow_lan { + // Important to block DNS before allow LAN (so DNS does not leak to the LAN) + rules.append(&mut self.get_block_dns_rules()?); rules.append(&mut self.get_allow_lan_rules()?); } Ok(rules) @@ -194,6 +187,25 @@ impl Firewall { .build()?) } + fn get_block_dns_rules(&self) -> Result<Vec<pfctl::FilterRule>> { + let block_tcp_dns_rule = self + .create_rule_builder(FilterRuleAction::Drop) + .direction(pfctl::Direction::Out) + .quick(true) + .proto(pfctl::Proto::Tcp) + .to(pfctl::Port::from(53)) + .build()?; + let block_udp_dns_rule = self + .create_rule_builder(FilterRuleAction::Drop) + .direction(pfctl::Direction::Out) + .quick(true) + .proto(pfctl::Proto::Udp) + .to(pfctl::Port::from(53)) + .build()?; + + Ok(vec![block_tcp_dns_rule, block_udp_dns_rule]) + } + fn get_allow_pingable_hosts( &self, pingable_hosts: &[IpAddr], |
