diff options
| author | Odd Stranne <odd@mullvad.net> | 2018-10-23 12:57:27 +0200 |
|---|---|---|
| committer | Odd Stranne <odd@mullvad.net> | 2018-10-30 14:57:46 +0100 |
| commit | 7921dcd9d1f888dc8afbac1aea943af20ef27dba (patch) | |
| tree | fb389be65ebc1e6be172f90170b7dfd9274aa0bc /talpid-core/src | |
| parent | 313a6657a0c3d35e6e83836d6edf99a87d423fc3 (diff) | |
| download | mullvadvpn-7921dcd9d1f888dc8afbac1aea943af20ef27dba.tar.xz mullvadvpn-7921dcd9d1f888dc8afbac1aea943af20ef27dba.zip | |
Add OpenVPN proxy support in Daemon
Diffstat (limited to 'talpid-core/src')
| -rw-r--r-- | talpid-core/src/process/openvpn.rs | 47 | ||||
| -rw-r--r-- | talpid-core/src/security/linux/mod.rs | 8 | ||||
| -rw-r--r-- | talpid-core/src/security/macos/mod.rs | 8 | ||||
| -rw-r--r-- | talpid-core/src/security/mod.rs | 20 | ||||
| -rw-r--r-- | talpid-core/src/security/windows/mod.rs | 8 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 60 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connected_state.rs | 16 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 33 |
8 files changed, 162 insertions, 38 deletions
diff --git a/talpid-core/src/process/openvpn.rs b/talpid-core/src/process/openvpn.rs index 761d4e6206..014e475524 100644 --- a/talpid-core/src/process/openvpn.rs +++ b/talpid-core/src/process/openvpn.rs @@ -46,6 +46,7 @@ pub struct OpenVpnCommand { config: Option<PathBuf>, remote: Option<net::Endpoint>, user_pass_path: Option<PathBuf>, + proxy_auth_path: Option<PathBuf>, ca: Option<PathBuf>, crl: Option<PathBuf>, iproute_bin: Option<OsString>, @@ -65,6 +66,7 @@ impl OpenVpnCommand { config: None, remote: None, user_pass_path: None, + proxy_auth_path: None, ca: None, crl: None, iproute_bin: None, @@ -95,6 +97,13 @@ impl OpenVpnCommand { self } + /// Sets the path to the file where the username and password for proxy authentication + /// is stored. + pub fn proxy_auth<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { + self.proxy_auth_path = Some(path.as_ref().to_path_buf()); + self + } + /// Sets the path to the CA certificate file. pub fn ca<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { self.ca = Some(path.as_ref().to_path_buf()); @@ -133,7 +142,7 @@ impl OpenVpnCommand { /// Sets extra options pub fn tunnel_options(&mut self, tunnel_options: &net::OpenVpnTunnelOptions) -> &mut Self { - self.tunnel_options = *tunnel_options; + self.tunnel_options = tunnel_options.clone(); self } @@ -208,6 +217,7 @@ impl OpenVpnCommand { } args.extend(Self::security_arguments().iter().map(OsString::from)); + args.extend(self.proxy_arguments().iter().map(OsString::from)); args } @@ -252,6 +262,41 @@ impl OpenVpnCommand { } args } + + fn proxy_arguments(&self) -> Vec<String> { + let mut args = vec![]; + match self.tunnel_options.proxy { + Some(net::OpenVpnProxySettings::Local(ref local_proxy)) => { + args.push("--socks-proxy".to_owned()); + args.push("127.0.0.1".to_owned()); + args.push(local_proxy.port.to_string()); + args.push("--route".to_owned()); + args.push(local_proxy.peer.ip().to_string()); + args.push("255.255.255.255".to_owned()); + args.push("net_gateway".to_owned()); + } + Some(net::OpenVpnProxySettings::Remote(ref remote_proxy)) => { + args.push("--socks-proxy".to_owned()); + args.push(remote_proxy.address.ip().to_string()); + args.push(remote_proxy.address.port().to_string()); + + if let Some(ref _auth) = remote_proxy.auth { + if let Some(ref auth_file) = self.proxy_auth_path { + args.push(auth_file.to_string_lossy().to_string()); + } else { + log::error!("Proxy credentials present but credentials file missing"); + } + } + + args.push("--route".to_owned()); + args.push(remote_proxy.address.ip().to_string()); + args.push("255.255.255.255".to_owned()); + args.push("net_gateway".to_owned()); + } + None => {} + }; + args + } } impl fmt::Display for OpenVpnCommand { diff --git a/talpid-core/src/security/linux/mod.rs b/talpid-core/src/security/linux/mod.rs index 83b0816988..9641830f2a 100644 --- a/talpid-core/src/security/linux/mod.rs +++ b/talpid-core/src/security/linux/mod.rs @@ -227,18 +227,18 @@ impl<'a> PolicyBatch<'a> { fn add_policy_specific_rules(&mut self, policy: &SecurityPolicy) -> Result<()> { let allow_lan = match policy { SecurityPolicy::Connecting { - relay_endpoint, + peer_endpoint, allow_lan, } => { - self.add_allow_endpoint_rules(relay_endpoint)?; + self.add_allow_endpoint_rules(peer_endpoint)?; *allow_lan } SecurityPolicy::Connected { - relay_endpoint, + peer_endpoint, tunnel, allow_lan, } => { - self.add_allow_endpoint_rules(relay_endpoint)?; + self.add_allow_endpoint_rules(peer_endpoint)?; self.add_dns_rule(tunnel, TransportProtocol::Udp)?; self.add_dns_rule(tunnel, TransportProtocol::Tcp)?; self.add_allow_tunnel_rules(tunnel)?; diff --git a/talpid-core/src/security/macos/mod.rs b/talpid-core/src/security/macos/mod.rs index f83567f91e..d588d21177 100644 --- a/talpid-core/src/security/macos/mod.rs +++ b/talpid-core/src/security/macos/mod.rs @@ -87,17 +87,17 @@ impl NetworkSecurity { ) -> Result<Vec<pfctl::FilterRule>> { match policy { SecurityPolicy::Connecting { - relay_endpoint, + peer_endpoint, allow_lan, } => { - let mut rules = vec![Self::get_allow_relay_rule(relay_endpoint)?]; + let mut rules = vec![Self::get_allow_relay_rule(peer_endpoint)?]; if allow_lan { rules.append(&mut Self::get_allow_lan_rules()?); } Ok(rules) } SecurityPolicy::Connected { - relay_endpoint, + peer_endpoint, tunnel, allow_lan, } => { @@ -139,7 +139,7 @@ impl NetworkSecurity { allow_udp_dns_to_relay_rule, block_tcp_dns_rule, block_udp_dns_rule, - Self::get_allow_relay_rule(relay_endpoint)?, + Self::get_allow_relay_rule(peer_endpoint)?, Self::get_allow_tunnel_rule(tunnel.interface.as_str())?, ]; diff --git a/talpid-core/src/security/mod.rs b/talpid-core/src/security/mod.rs index a18ecc26da..6e8149e87b 100644 --- a/talpid-core/src/security/mod.rs +++ b/talpid-core/src/security/mod.rs @@ -43,18 +43,18 @@ lazy_static! { /// A enum that describes network security strategy #[derive(Debug, Clone, Eq, PartialEq)] pub enum SecurityPolicy { - /// Allow traffic only to relay server + /// Allow traffic only to server Connecting { - /// The relay endpoint that should be allowed. - relay_endpoint: Endpoint, + /// The peer endpoint that should be allowed. + peer_endpoint: Endpoint, /// Flag setting if communication with LAN networks should be possible. allow_lan: bool, }, - /// Allow traffic only to relay server and over tunnel interface + /// Allow traffic only to server and over tunnel interface Connected { - /// The relay endpoint that should be allowed. - relay_endpoint: Endpoint, + /// The peer endpoint that should be allowed. + peer_endpoint: Endpoint, /// Metadata about the tunnel and tunnel interface. tunnel: ::tunnel::TunnelMetadata, /// Flag setting if communication with LAN networks should be possible. @@ -72,22 +72,22 @@ impl fmt::Display for SecurityPolicy { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { SecurityPolicy::Connecting { - relay_endpoint, + peer_endpoint, allow_lan, } => write!( f, "Connecting to {}, {} LAN", - relay_endpoint, + peer_endpoint, if *allow_lan { "Allowing" } else { "Blocking" } ), SecurityPolicy::Connected { - relay_endpoint, + peer_endpoint, tunnel, allow_lan, } => write!( f, "Connected to {} over \"{}\" (ip: {}, gw: {}), {} LAN", - relay_endpoint, + peer_endpoint, tunnel.interface, tunnel.ip, tunnel.gateway, diff --git a/talpid-core/src/security/windows/mod.rs b/talpid-core/src/security/windows/mod.rs index 75961aa69f..65590f6f19 100644 --- a/talpid-core/src/security/windows/mod.rs +++ b/talpid-core/src/security/windows/mod.rs @@ -87,19 +87,19 @@ impl NetworkSecurityT for NetworkSecurity { fn apply_policy(&mut self, policy: SecurityPolicy) -> Result<()> { match policy { SecurityPolicy::Connecting { - relay_endpoint, + peer_endpoint, allow_lan, } => { let cfg = &WinFwSettings::new(allow_lan); - self.set_connecting_state(&relay_endpoint, &cfg) + self.set_connecting_state(&peer_endpoint, &cfg) } SecurityPolicy::Connected { - relay_endpoint, + peer_endpoint, tunnel, allow_lan, } => { let cfg = &WinFwSettings::new(allow_lan); - self.set_connected_state(&relay_endpoint, &cfg, &tunnel) + self.set_connected_state(&peer_endpoint, &cfg, &tunnel) } SecurityPolicy::Blocked { allow_lan } => { let cfg = &WinFwSettings::new(allow_lan); diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 3084341af5..1d8d2e967f 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -8,13 +8,16 @@ use std::fs; use std::io::{self, Write}; use std::net::Ipv4Addr; use std::path::{Path, PathBuf}; +use std::result::Result as StdResult; #[cfg(target_os = "linux")] use failure::ResultExt as FailureResultExt; #[cfg(target_os = "linux")] use which; -use talpid_types::net::{Endpoint, TunnelEndpoint, TunnelEndpointData, TunnelOptions}; +use talpid_types::net::{ + Endpoint, OpenVpnProxySettings, TunnelEndpoint, TunnelEndpointData, TunnelOptions, +}; /// A module for all OpenVPN related tunnel management. pub mod openvpn; @@ -141,6 +144,8 @@ pub struct TunnelMonitor { monitor: OpenVpnMonitor, /// Keep the `TempFile` for the user-pass file in the struct, so it's removed on drop. _user_pass_file: mktemp::TempFile, + /// Keep the 'TempFile' for the proxy user-pass file in the struct, so it's removed on drop. + _proxy_auth_file: Option<mktemp::TempFile>, } impl TunnelMonitor { @@ -161,22 +166,42 @@ impl TunnelMonitor { Self::ensure_endpoint_is_openvpn(&tunnel_endpoint)?; Self::ensure_ipv6_can_be_used_if_enabled(tunnel_options)?; - let user_pass_file = - Self::create_user_pass_file(username).chain_err(|| ErrorKind::CredentialsWriteError)?; + let user_pass_file = Self::create_credentials_file(username, "-") + .chain_err(|| ErrorKind::CredentialsWriteError)?; + + let proxy_auth_file = Self::create_proxy_auth_file(&tunnel_options.openvpn.proxy) + .chain_err(|| ErrorKind::CredentialsWriteError)?; + + // todo: also send proxy file let cmd = Self::create_openvpn_cmd( tunnel_endpoint.to_endpoint(), tunnel_alias, &tunnel_options, user_pass_file.as_ref(), + match proxy_auth_file { + Some(ref file) => Some(file.as_ref()), + _ => None, + }, log, resource_dir, )?; let user_pass_file_path = user_pass_file.to_path_buf(); + + let proxy_auth_file_path = match proxy_auth_file { + Some(ref file) => Some(file.to_path_buf()), + _ => None, + }; + let on_openvpn_event = move |event, env| { if event == OpenVpnPluginEvent::Up { // The user-pass file has been read. Try to delete it early. let _ = fs::remove_file(&user_pass_file_path); + + // The proxy auth file has been read. Try to delete it early. + if let Some(ref file_path) = &proxy_auth_file_path { + let _ = fs::remove_file(file_path); + } } match TunnelEvent::from_openvpn_event(event, &env) { Some(tunnel_event) => on_event(tunnel_event), @@ -193,6 +218,7 @@ impl TunnelMonitor { Ok(TunnelMonitor { monitor, _user_pass_file: user_pass_file, + _proxy_auth_file: proxy_auth_file, }) } @@ -216,6 +242,7 @@ impl TunnelMonitor { tunnel_alias: Option<OsString>, options: &TunnelOptions, user_pass_file: &Path, + proxy_auth_file: Option<&Path>, log: Option<&Path>, resource_dir: &Path, ) -> Result<OpenVpnCommand> { @@ -239,6 +266,10 @@ impl TunnelMonitor { if let Some(log) = log { cmd.log(log); } + if let Some(proxy_auth_file) = proxy_auth_file { + cmd.proxy_auth(proxy_auth_file); + } + Ok(cmd) } @@ -271,18 +302,29 @@ impl TunnelMonitor { } } - fn create_user_pass_file(username: &str) -> io::Result<mktemp::TempFile> { + fn create_credentials_file(username: &str, password: &str) -> io::Result<mktemp::TempFile> { let temp_file = mktemp::TempFile::new(); - log::debug!( - "Writing user-pass credentials to {}", - temp_file.as_ref().display() - ); + log::debug!("Writing credentials to {}", temp_file.as_ref().display()); let mut file = fs::File::create(&temp_file)?; Self::set_user_pass_file_permissions(&file)?; - write!(file, "{}\n-\n", username)?; + write!(file, "{}\n{}\n", username, password)?; Ok(temp_file) } + fn create_proxy_auth_file( + proxy: &Option<OpenVpnProxySettings>, + ) -> StdResult<Option<mktemp::TempFile>, io::Error> { + if let Some(OpenVpnProxySettings::Remote(ref remote_proxy)) = proxy { + if let Some(ref proxy_auth) = remote_proxy.auth { + return Ok(Some(Self::create_credentials_file( + &proxy_auth.username, + &proxy_auth.password, + )?)); + } + } + Ok(None) + } + #[cfg(unix)] fn set_user_pass_file_permissions(file: &fs::File) -> io::Result<()> { use std::os::unix::fs::PermissionsExt; diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index c9004556c5..abb5c3cad5 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -1,6 +1,7 @@ use error_chain::ChainedError; use futures::sync::{mpsc, oneshot}; use futures::{Async, Future, Stream}; +use talpid_types::net::{Endpoint, OpenVpnProxySettings, TransportProtocol}; use talpid_types::tunnel::BlockReason; use super::{ @@ -40,8 +41,21 @@ impl ConnectedState { } fn set_security_policy(&self, shared_values: &mut SharedTunnelStateValues) -> Result<()> { + // If a proxy is specified we need to pass it on as the peer endpoint. + let peer_endpoint = match self.tunnel_parameters.options.openvpn.proxy { + Some(OpenVpnProxySettings::Local(ref local_proxy)) => Endpoint { + address: local_proxy.peer, + protocol: TransportProtocol::Tcp, + }, + Some(OpenVpnProxySettings::Remote(ref remote_proxy)) => Endpoint { + address: remote_proxy.address, + protocol: TransportProtocol::Tcp, + }, + _ => self.tunnel_parameters.endpoint.to_endpoint(), + }; + let policy = SecurityPolicy::Connected { - relay_endpoint: self.tunnel_parameters.endpoint.to_endpoint(), + peer_endpoint, tunnel: self.metadata.clone(), allow_lan: shared_values.allow_lan, }; diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index cc30d88ac9..873eaa9933 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -7,7 +7,9 @@ use error_chain::ChainedError; use futures::sync::{mpsc, oneshot}; use futures::{Async, Future, Stream}; use log::{debug, error, info, trace, warn}; -use talpid_types::net::{TunnelEndpoint, TunnelEndpointData}; +use talpid_types::net::{ + Endpoint, OpenVpnProxySettings, TransportProtocol, TunnelEndpoint, TunnelEndpointData, +}; use talpid_types::tunnel::BlockReason; use super::{ @@ -53,10 +55,24 @@ pub struct ConnectingState { impl ConnectingState { fn set_security_policy( shared_values: &mut SharedTunnelStateValues, + proxy: &Option<OpenVpnProxySettings>, endpoint: TunnelEndpoint, ) -> Result<()> { + // If a proxy is specified we need to pass it on as the peer endpoint. + let peer_endpoint = match proxy { + Some(OpenVpnProxySettings::Local(ref local_proxy)) => Endpoint { + address: local_proxy.peer, + protocol: TransportProtocol::Tcp, + }, + Some(OpenVpnProxySettings::Remote(ref remote_proxy)) => Endpoint { + address: remote_proxy.address, + protocol: TransportProtocol::Tcp, + }, + _ => endpoint.to_endpoint(), + }; + let policy = SecurityPolicy::Connecting { - relay_endpoint: endpoint.to_endpoint(), + peer_endpoint, allow_lan: shared_values.allow_lan, }; shared_values @@ -172,7 +188,11 @@ impl ConnectingState { match try_handle_event!(self, commands.poll()) { Ok(TunnelCommand::AllowLan(allow_lan)) => { shared_values.allow_lan = allow_lan; - match Self::set_security_policy(shared_values, self.tunnel_parameters.endpoint) { + match Self::set_security_policy( + shared_values, + &self.tunnel_parameters.options.openvpn.proxy, + self.tunnel_parameters.endpoint, + ) { Ok(()) => SameState(self), Err(error) => { error!("{}", error.display_chain()); @@ -284,8 +304,11 @@ impl TunnelState for ConnectingState { None => BlockedState::enter(shared_values, BlockReason::NoMatchingRelay), Some(tunnel_parameters) => { let tunnel_endpoint = tunnel_parameters.endpoint; - - if let Err(error) = Self::set_security_policy(shared_values, tunnel_endpoint) { + if let Err(error) = Self::set_security_policy( + shared_values, + &tunnel_parameters.options.openvpn.proxy, + tunnel_endpoint, + ) { error!("{}", error.display_chain()); BlockedState::enter(shared_values, BlockReason::StartTunnelError) } else { |
