diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index e25d01f58d8b..811fc2dcc7f2 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -22,6 +22,9 @@ Line wrap the file at 100 chars. Th * **Security**: in case of vulnerabilities. ## [Unreleased] +### Security +- Fix DNS leaks in blocking states or when no valid DNS has been configured due to an underlying OS + issue. In these cases a dummy DNS will be set to prevent leaks. ## [android/2024.2-beta1] - 2024-04-17 diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt index 94b097fe1310..76abde2a01f1 100644 --- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt +++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt @@ -2,6 +2,7 @@ package net.mullvad.talpid import android.net.VpnService import android.os.ParcelFileDescriptor +import android.util.Log import java.net.Inet4Address import java.net.Inet6Address import java.net.InetAddress @@ -103,6 +104,18 @@ open class TalpidVpnService : VpnService() { } } + // Avoids creating a tunnel with no DNS servers or if all DNS servers was invalid, + // since apps then may leak DNS requests. + // https://issuetracker.google.com/issues/337961996 + if (invalidDnsServerAddresses.size == config.dnsServers.size) { + Log.w( + "mullvad", + "All DNS servers invalid or non set, using fallback DNS server to " + + "minimize leaks, dnsServers.isEmpty(): ${config.dnsServers.isEmpty()}" + ) + addDnsServer(FALLBACK_DUMMY_DNS_SERVER) + } + for (route in config.routes) { addRoute(route.address, route.prefixLength.toInt()) } @@ -148,4 +161,8 @@ open class TalpidVpnService : VpnService() { private external fun defaultTunConfig(): TunConfig private external fun waitForTunnelUp(tunFd: Int, isIpv6Enabled: Boolean) + + companion object { + private const val FALLBACK_DUMMY_DNS_SERVER = "192.0.2.1" + } } diff --git a/talpid-tunnel/src/tun_provider/android/mod.rs b/talpid-tunnel/src/tun_provider/android/mod.rs index 187dd3b4f502..baccd3f3fc3c 100644 --- a/talpid-tunnel/src/tun_provider/android/mod.rs +++ b/talpid-tunnel/src/tun_provider/android/mod.rs @@ -56,6 +56,7 @@ pub struct AndroidTunProvider { object: GlobalRef, last_tun_config: TunConfig, allow_lan: bool, + blocking: bool, custom_dns_servers: Option>, allowed_lan_networks: Vec, } @@ -82,6 +83,7 @@ impl AndroidTunProvider { object: context.vpn_service, last_tun_config: TunConfig::default(), allow_lan, + blocking: false, custom_dns_servers, allowed_lan_networks, } @@ -105,8 +107,15 @@ impl AndroidTunProvider { Ok(()) } + /// Retrieve a tunnel device with the provided configuration. Custom DNS and LAN routes are + /// appended to the provided config. + pub fn get_tun(&mut self, mut config: TunConfig) -> Result { + self.prepare_tun_config(&mut config, false); + self.get_tun_inner(config) + } + /// Retrieve a tunnel device with the provided configuration. - pub fn get_tun(&mut self, config: TunConfig) -> Result { + fn get_tun_inner(&mut self, config: TunConfig) -> Result { let tun_fd = self.get_tun_fd(config.clone())?; self.last_tun_config = config; @@ -122,15 +131,15 @@ impl AndroidTunProvider { }) } - /// Open a tunnel device that routes everything but custom DNS, and - /// (potentially) LAN routes via the tunnel device. + /// Open a tunnel device that routes everything but (potentially) LAN routes via the tunnel + /// device. /// /// Will open a new tunnel if there is already an active tunnel. The previous tunnel will be /// closed. pub fn create_blocking_tun(&mut self) -> Result<(), Error> { let mut config = TunConfig::default(); - self.prepare_tun_config(&mut config); - let _ = self.get_tun(config)?; + self.prepare_tun_config(&mut config, true); + let _ = self.get_tun_inner(config)?; Ok(()) } @@ -176,9 +185,7 @@ impl AndroidTunProvider { } } - fn get_tun_fd(&mut self, mut config: TunConfig) -> Result { - self.prepare_tun_config(&mut config); - + fn get_tun_fd(&mut self, config: TunConfig) -> Result { let env = self.env()?; let java_config = config.into_java(&env); @@ -198,7 +205,7 @@ impl AndroidTunProvider { fn recreate_tun_if_open(&mut self) -> Result<(), Error> { let mut actual_config = self.last_tun_config.clone(); - self.prepare_tun_config(&mut actual_config); + self.prepare_tun_config(&mut actual_config, self.blocking); let env = self.env()?; let java_config = actual_config.into_java(&env); @@ -216,9 +223,13 @@ impl AndroidTunProvider { } } - fn prepare_tun_config(&self, config: &mut TunConfig) { + fn prepare_tun_config(&mut self, config: &mut TunConfig, blocking: bool) { + self.blocking = blocking; self.prepare_tun_config_for_allow_lan(config); - self.prepare_tun_config_for_custom_dns(config); + if !blocking { + self.prepare_tun_config_for_custom_dns(config); + } + maybe_set_dummy_dns_servers(config); } fn prepare_tun_config_for_allow_lan(&self, config: &mut TunConfig) { @@ -324,6 +335,14 @@ impl AndroidTunProvider { } } +/// Add dummy servers if no DNS servers are set. Android may sometimes leak DNS otherwise. +fn maybe_set_dummy_dns_servers(config: &mut TunConfig) { + if !config.dns_servers.is_empty() { + return; + } + config.dns_servers = vec!["192.0.2.1".parse().unwrap()]; +} + /// Handle to a tunnel device on Android. pub struct VpnServiceTun { tunnel: RawFd,