diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-09-11 06:31:56 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-09-11 06:31:56 -0300 |
| commit | ff18ea9d2c9e3d99ea58946cbdd5e1ed87829f8f (patch) | |
| tree | 2688a051ce9153b5ce9bc23c932ef2a2f5531cd6 | |
| parent | 235506bc6ec9b453716b2f85e4abdb57dd54380d (diff) | |
| parent | 55199132b8d13adc979bfefaa7ea280e761afca8 (diff) | |
| download | mullvadvpn-ff18ea9d2c9e3d99ea58946cbdd5e1ed87829f8f.tar.xz mullvadvpn-ff18ea9d2c9e3d99ea58946cbdd5e1ed87829f8f.zip | |
Merge branch 'simple-android-firewall'
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | mullvad-jni/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-jni/src/vpn_service_tun_provider.rs | 178 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/tun_provider/mod.rs | 14 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/tun_provider/stub.rs | 12 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/tun_provider/unix.rs | 2 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/mod.rs | 2 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/wireguard_go.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/blocked_state.rs | 18 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/disconnected_state.rs | 3 |
12 files changed, 181 insertions, 64 deletions
diff --git a/Cargo.lock b/Cargo.lock index 17330baa87..856ee14fec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1237,6 +1237,7 @@ dependencies = [ "jni 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "mullvad-daemon 2019.7.0", diff --git a/mullvad-jni/Cargo.toml b/mullvad-jni/Cargo.toml index 21b1d15ba3..2f0884f29d 100644 --- a/mullvad-jni/Cargo.toml +++ b/mullvad-jni/Cargo.toml @@ -16,6 +16,7 @@ ipnetwork = "0.14" jni = "0.12" jsonrpc-client-core = "0.5" lazy_static = "1" +libc = "0.2" log = "0.4" log-panics = "2" parking_lot = "0.8" diff --git a/mullvad-jni/src/vpn_service_tun_provider.rs b/mullvad-jni/src/vpn_service_tun_provider.rs index 3d422d83e0..bcc52c53bf 100644 --- a/mullvad-jni/src/vpn_service_tun_provider.rs +++ b/mullvad-jni/src/vpn_service_tun_provider.rs @@ -1,10 +1,16 @@ use crate::{get_class, into_java::IntoJava}; +use ipnetwork::IpNetwork; use jni::{ objects::{GlobalRef, JObject, JValue}, signature::{JavaType, Primitive}, JNIEnv, JavaVM, }; -use std::os::unix::io::{AsRawFd, RawFd}; +use std::{ + fs::File, + io, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + os::unix::io::{AsRawFd, FromRawFd, RawFd}, +}; use talpid_core::tunnel::tun_provider::{Tun, TunConfig, TunProvider}; use talpid_types::BoxedError; @@ -18,19 +24,29 @@ pub enum Error { #[error(display = "Failed to allow socket to bypass tunnel")] Bypass, - #[error(display = "Failed to call Java method {}", _0)] + #[error(display = "Failed to call Java method MullvadVpnService.{}", _0)] CallMethod(&'static str, #[error(cause)] jni::errors::Error), + #[error(display = "Failed to create Java VM handle clone")] + CloneJavaVm(#[error(cause)] jni::errors::Error), + #[error(display = "Failed to create global reference to MullvadVpnService instance")] CreateGlobalReference(#[error(cause)] jni::errors::Error), - #[error(display = "Failed to find {} method", _0)] + #[error(display = "Failed to duplicate tunnel file descriptor")] + DuplicateTunFd(#[error(cause)] io::Error), + + #[error(display = "Failed to find MullvadVpnService.{} method", _0)] FindMethod(&'static str, #[error(cause)] jni::errors::Error), #[error(display = "Failed to get Java VM instance")] GetJvmInstance(#[error(cause)] jni::errors::Error), - #[error(display = "Received an invalid result from {}: {}", _0, _1)] + #[error( + display = "Received an invalid result from MullvadVpnService.{}: {}", + _0, + _1 + )] InvalidMethodResult(&'static str, String), } @@ -39,6 +55,8 @@ pub struct VpnServiceTunProvider { jvm: JavaVM, class: GlobalRef, object: GlobalRef, + active_tun: Option<File>, + last_tun_config: TunConfig, } impl VpnServiceTunProvider { @@ -50,51 +68,113 @@ impl VpnServiceTunProvider { .new_global_ref(*mullvad_vpn_service) .map_err(Error::CreateGlobalReference)?; - Ok(VpnServiceTunProvider { jvm, class, object }) + // Initial configuration simply intercepts all packets. The only field that matters is + // `routes`, because it determines what must enter the tunnel. All other fields contain + // stub values. + let initial_tun_config = TunConfig { + addresses: vec![IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))], + dns_servers: Vec::new(), + routes: vec![ + IpNetwork::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0) + .expect("Invalid IP network prefix for IPv4 address"), + IpNetwork::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), 0) + .expect("Invalid IP network prefix for IPv6 address"), + ], + mtu: 1380, + }; + + Ok(VpnServiceTunProvider { + jvm, + class, + object, + active_tun: None, + last_tun_config: initial_tun_config, + }) + } + + fn duplicate_tun(tun: &File) -> Result<RawFd, Error> { + let tun_fd = unsafe { libc::dup(tun.as_raw_fd()) }; + + if tun_fd >= 0 { + Ok(tun_fd) + } else { + Err(Error::DuplicateTunFd(io::Error::last_os_error())) + } + } + + fn prepare_tun(&mut self, config: TunConfig) -> Result<&File, Error> { + if self.active_tun.is_none() || self.last_tun_config != config { + let env = self + .jvm + .attach_current_thread() + .map_err(Error::AttachJvmToThread)?; + let create_tun_method = env + .get_method_id( + &self.class, + "createTun", + "(Lnet/mullvad/mullvadvpn/model/TunConfig;)I", + ) + .map_err(|cause| Error::FindMethod("createTun", cause))?; + + let result = env + .call_method_unchecked( + self.object.as_obj(), + create_tun_method, + JavaType::Primitive(Primitive::Int), + &[JValue::Object(config.clone().into_java(&env))], + ) + .map_err(|cause| Error::CallMethod("createTun", cause))?; + + match result { + JValue::Int(fd) => { + let tun = unsafe { File::from_raw_fd(fd) }; + + self.active_tun = Some(tun); + self.last_tun_config = config; + } + value => { + return Err(Error::InvalidMethodResult( + "createTun", + format!("{:?}", value), + )) + } + } + } + + Ok(self + .active_tun + .as_ref() + .expect("Tunnel should be configured")) } } impl TunProvider for VpnServiceTunProvider { - fn create_tun(&self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError> { - let env = self - .jvm - .attach_current_thread() - .map_err(|cause| BoxedError::new(Error::AttachJvmToThread(cause)))?; - let create_tun_method = env - .get_method_id( - &self.class, - "createTun", - "(Lnet/mullvad/mullvadvpn/model/TunConfig;)I", - ) - .map_err(|cause| { - BoxedError::new(Error::FindMethod("MullvadVpnService.createTun", cause)) - })?; + fn get_tun(&mut self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError> { + let tun = self.prepare_tun(config).map_err(BoxedError::new)?; + let tun_fd = Self::duplicate_tun(tun).map_err(BoxedError::new)?; - let result = env - .call_method_unchecked( - self.object.as_obj(), - create_tun_method, - JavaType::Primitive(Primitive::Int), - &[JValue::Object(config.into_java(&env))], - ) - .map_err(|cause| { - BoxedError::new(Error::CallMethod("MullvadVpnService.createTun", cause)) - })?; + let jvm = unsafe { JavaVM::from_raw(self.jvm.get_java_vm_pointer()) } + .map_err(|cause| BoxedError::new(Error::CloneJavaVm(cause)))?; - match result { - JValue::Int(fd) => Ok(Box::new(VpnServiceTun { - tunnel: fd, - jvm: env - .get_java_vm() - .map_err(|cause| BoxedError::new(Error::GetJvmInstance(cause)))?, - class: self.class.clone(), - object: self.object.clone(), - })), - value => Err(BoxedError::new(Error::InvalidMethodResult( - "MullvadVpnService.createTun", - format!("{:?}", value), - ))), + Ok(Box::new(VpnServiceTun { + tunnel: tun_fd, + jvm, + class: self.class.clone(), + object: self.object.clone(), + })) + } + + fn create_tun_if_closed(&mut self) -> Result<(), BoxedError> { + if self.active_tun.is_none() { + self.prepare_tun(self.last_tun_config.clone()) + .map_err(BoxedError::new)?; } + + Ok(()) + } + + fn close_tun(&mut self) { + self.active_tun = None; } } @@ -121,11 +201,9 @@ impl Tun for VpnServiceTun { .jvm .attach_current_thread() .map_err(|cause| BoxedError::new(Error::AttachJvmToThread(cause)))?; - let create_tun_method = - env.get_method_id(&self.class, "bypass", "(I)Z") - .map_err(|cause| { - BoxedError::new(Error::FindMethod("MullvadVpnService.bypass", cause)) - })?; + let create_tun_method = env + .get_method_id(&self.class, "bypass", "(I)Z") + .map_err(|cause| BoxedError::new(Error::FindMethod("bypass", cause)))?; let result = env .call_method_unchecked( @@ -134,15 +212,13 @@ impl Tun for VpnServiceTun { JavaType::Primitive(Primitive::Boolean), &[JValue::Int(socket)], ) - .map_err(|cause| { - BoxedError::new(Error::CallMethod("MullvadVpnService.bypass", cause)) - })?; + .map_err(|cause| BoxedError::new(Error::CallMethod("bypass", cause)))?; match result { JValue::Bool(0) => Err(BoxedError::new(Error::Bypass)), JValue::Bool(_) => Ok(()), value => Err(BoxedError::new(Error::InvalidMethodResult( - "MullvadVpnService.bypass", + "bypass", format!("{:?}", value), ))), } diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 9014ee16ab..8fe2f7eb66 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -145,7 +145,7 @@ impl TunnelMonitor { log_dir: &Option<PathBuf>, resource_dir: &Path, on_event: L, - tun_provider: &dyn TunProvider, + tun_provider: &mut dyn TunProvider, ) -> Result<Self> where L: Fn(TunnelEvent) + Send + Clone + Sync + 'static, @@ -175,7 +175,7 @@ impl TunnelMonitor { params: &wireguard_types::TunnelParameters, log: Option<PathBuf>, on_event: L, - tun_provider: &dyn TunProvider, + tun_provider: &mut dyn TunProvider, ) -> Result<Self> where L: Fn(TunnelEvent) + Send + Sync + Clone + 'static, diff --git a/talpid-core/src/tunnel/tun_provider/mod.rs b/talpid-core/src/tunnel/tun_provider/mod.rs index d9578ebbd1..3cac459c51 100644 --- a/talpid-core/src/tunnel/tun_provider/mod.rs +++ b/talpid-core/src/tunnel/tun_provider/mod.rs @@ -49,12 +49,20 @@ pub trait Tun: Send { /// Factory of tunnel devices. pub trait TunProvider: Send + 'static { - /// Create a tunnel device using the provided configuration. - fn create_tun(&self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError>; + /// Retrieve a tunnel device with the provided configuration. + fn get_tun(&mut self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError>; + + /// Open a tunnel device using the previous or the default configuration. + #[cfg(target_os = "android")] + fn create_tun_if_closed(&mut self) -> Result<(), BoxedError>; + + /// Close currently active tunnel device. + #[cfg(target_os = "android")] + fn close_tun(&mut self); } /// Configuration for creating a tunnel device. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct TunConfig { /// IP addresses for the tunnel interface. pub addresses: Vec<IpAddr>, diff --git a/talpid-core/src/tunnel/tun_provider/stub.rs b/talpid-core/src/tunnel/tun_provider/stub.rs index 0b94b426bf..be985c8045 100644 --- a/talpid-core/src/tunnel/tun_provider/stub.rs +++ b/talpid-core/src/tunnel/tun_provider/stub.rs @@ -11,7 +11,17 @@ impl Default for StubTunProvider { } impl TunProvider for StubTunProvider { - fn create_tun(&self, _: TunConfig) -> Result<Box<dyn Tun>, BoxedError> { + fn get_tun(&mut self, _: TunConfig) -> Result<Box<dyn Tun>, BoxedError> { + unimplemented!(); + } + + #[cfg(target_os = "android")] + fn create_tun_if_closed(&mut self) -> Result<(), BoxedError> { + unimplemented!(); + } + + #[cfg(target_os = "android")] + fn close_tun(&mut self) { unimplemented!(); } } diff --git a/talpid-core/src/tunnel/tun_provider/unix.rs b/talpid-core/src/tunnel/tun_provider/unix.rs index c0715a81bd..461e49a3b6 100644 --- a/talpid-core/src/tunnel/tun_provider/unix.rs +++ b/talpid-core/src/tunnel/tun_provider/unix.rs @@ -29,7 +29,7 @@ impl Default for UnixTunProvider { } impl TunProvider for UnixTunProvider { - fn create_tun(&self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError> { + fn get_tun(&mut self, config: TunConfig) -> Result<Box<dyn Tun>, BoxedError> { let mut tunnel_device = TunnelDevice::new() .map_err(|cause| BoxedError::new(Error::CreateTunnelDevice(cause)))?; diff --git a/talpid-core/src/tunnel/wireguard/mod.rs b/talpid-core/src/tunnel/wireguard/mod.rs index 440bd1ae61..71ef1a83b3 100644 --- a/talpid-core/src/tunnel/wireguard/mod.rs +++ b/talpid-core/src/tunnel/wireguard/mod.rs @@ -82,7 +82,7 @@ impl WireguardMonitor { config: &Config, log_path: Option<&Path>, on_event: F, - tun_provider: &dyn TunProvider, + tun_provider: &mut dyn TunProvider, ) -> Result<WireguardMonitor> { let tunnel = Box::new(WgGoTunnel::start_tunnel( &config, diff --git a/talpid-core/src/tunnel/wireguard/wireguard_go.rs b/talpid-core/src/tunnel/wireguard/wireguard_go.rs index 38a0c83218..a771bfae4a 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_go.rs +++ b/talpid-core/src/tunnel/wireguard/wireguard_go.rs @@ -18,12 +18,12 @@ impl WgGoTunnel { pub fn start_tunnel( config: &Config, log_path: Option<&Path>, - tun_provider: &dyn TunProvider, + tun_provider: &mut dyn TunProvider, routes: impl Iterator<Item = IpNetwork>, ) -> Result<Self> { #[cfg_attr(not(target_os = "android"), allow(unused_mut))] let mut tunnel_device = tun_provider - .create_tun(Self::create_tunnel_config(config, routes)) + .get_tun(Self::create_tunnel_config(config, routes)) .map_err(Error::SetupTunnelDeviceError)?; let interface_name: String = tunnel_device.interface_name().to_string(); diff --git a/talpid-core/src/tunnel_state_machine/blocked_state.rs b/talpid-core/src/tunnel_state_machine/blocked_state.rs index 2387d4e922..bdf054ab2c 100644 --- a/talpid-core/src/tunnel_state_machine/blocked_state.rs +++ b/talpid-core/src/tunnel_state_machine/blocked_state.rs @@ -30,6 +30,22 @@ impl BlockedState { } } } + + #[cfg(target_os = "android")] + fn create_blocking_tun(shared_values: &mut SharedTunnelStateValues) -> Option<BlockReason> { + match shared_values.tun_provider.create_tun_if_closed() { + Ok(()) => None, + Err(error) => { + log::error!( + "{}", + error.display_chain_with_msg( + "Failed to open tunnel adapter to drop packets for blocked state" + ) + ); + Some(BlockReason::SetFirewallPolicyError) + } + } + } } impl TunnelState for BlockedState { @@ -40,6 +56,8 @@ impl TunnelState for BlockedState { block_reason: Self::Bootstrap, ) -> (TunnelStateWrapper, TunnelStateTransition) { let block_reason = Self::set_firewall_policy(shared_values).unwrap_or_else(|| block_reason); + #[cfg(target_os = "android")] + let block_reason = Self::create_blocking_tun(shared_values).unwrap_or_else(|| block_reason); ( TunnelStateWrapper::from(BlockedState { diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 2c8b112537..74494241db 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -15,7 +15,7 @@ use futures::{ }; use log::{debug, error, info, trace, warn}; use std::{ - borrow::Borrow, + borrow::BorrowMut, net::IpAddr, path::{Path, PathBuf}, thread, @@ -64,7 +64,7 @@ impl ConnectingState { parameters: TunnelParameters, log_dir: &Option<PathBuf>, resource_dir: &Path, - tun_provider: &dyn TunProvider, + tun_provider: &mut dyn TunProvider, retry_attempt: u32, ) -> crate::tunnel::Result<Self> { let (event_tx, event_rx) = mpsc::unbounded(); @@ -350,7 +350,7 @@ impl TunnelState for ConnectingState { tunnel_parameters, &shared_values.log_dir, &shared_values.resource_dir, - shared_values.tun_provider.borrow(), + shared_values.tun_provider.borrow_mut(), retry_attempt, ) { Ok(connecting_state) => { diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs index 619efb2c10..6b95548ddd 100644 --- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs @@ -40,6 +40,9 @@ impl TunnelState for DisconnectedState { _: Self::Bootstrap, ) -> (TunnelStateWrapper, TunnelStateTransition) { Self::set_firewall_policy(shared_values); + #[cfg(target_os = "android")] + shared_values.tun_provider.close_tun(); + ( TunnelStateWrapper::from(DisconnectedState), TunnelStateTransition::Disconnected, |
