summaryrefslogtreecommitdiffhomepage
path: root/talpid-core/src
diff options
context:
space:
mode:
authorOdd Stranne <odd@mullvad.net>2018-10-23 12:57:27 +0200
committerOdd Stranne <odd@mullvad.net>2018-10-30 14:57:46 +0100
commit7921dcd9d1f888dc8afbac1aea943af20ef27dba (patch)
treefb389be65ebc1e6be172f90170b7dfd9274aa0bc /talpid-core/src
parent313a6657a0c3d35e6e83836d6edf99a87d423fc3 (diff)
downloadmullvadvpn-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.rs47
-rw-r--r--talpid-core/src/security/linux/mod.rs8
-rw-r--r--talpid-core/src/security/macos/mod.rs8
-rw-r--r--talpid-core/src/security/mod.rs20
-rw-r--r--talpid-core/src/security/windows/mod.rs8
-rw-r--r--talpid-core/src/tunnel/mod.rs60
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs16
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs33
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 {