summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-07-03 12:49:15 +0200
committerLinus Färnstrand <linus@mullvad.net>2017-07-03 12:49:15 +0200
commite0ba08fb3ac8198b7184bd73fb82d35fae704e09 (patch)
tree384c09ff013f72dfd2958f354357b44558c84663
parent39e704f76cd74f1700db2efe7622110d52f82951 (diff)
parentadb58cdfbd60c9779db9e42aee018595f7eff9da (diff)
downloadmullvadvpn-e0ba08fb3ac8198b7184bd73fb82d35fae704e09.tar.xz
mullvadvpn-e0ba08fb3ac8198b7184bd73fb82d35fae704e09.zip
Merge branch 'support-user-pass-auth'
-rw-r--r--Cargo.lock26
-rw-r--r--mullvad_daemon/src/main.rs23
-rw-r--r--mullvad_daemon/src/management_interface.rs29
-rw-r--r--talpid_core/Cargo.toml1
-rw-r--r--talpid_core/src/lib.rs1
-rw-r--r--talpid_core/src/process/openvpn.rs19
-rw-r--r--talpid_core/src/tunnel/mod.rs50
7 files changed, 136 insertions, 13 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 65c4aec22b..862906d789 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -312,6 +312,14 @@ dependencies = [
]
[[package]]
+name = "mktemp"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "mullvad_daemon"
version = "0.1.0"
dependencies = [
@@ -447,6 +455,11 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "rustc-serialize"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "scoped-tls"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -555,6 +568,7 @@ dependencies = [
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mktemp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"openvpn_ffi 0.1.0",
"talpid_ipc 0.1.0",
]
@@ -661,6 +675,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "uuid"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "uuid"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -746,6 +769,7 @@ dependencies = [
"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
"checksum mio 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f27d38f824a0d267d55b29b171e9e99269a53812e385fa75c1fe700ae254a6a4"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+"checksum mktemp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77001ceb9eed65439f3dc2a2543f9ba1417d912686bf224a7738d0966e6dcd69"
"checksum net2 0.2.27 (registry+https://github.com/rust-lang/crates.io-index)" = "18b9642ad6222faf5ce46f6966f59b71b9775ad5758c9e09fcf0a6c8061972b4"
"checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487"
"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99"
@@ -758,6 +782,7 @@ dependencies = [
"checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01"
"checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457"
"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95"
+"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
"checksum serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3b46a59dd63931010fdb1d88538513f3279090d88b5c22ef4fe8440cfffcc6e3"
"checksum serde_derive 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c06b68790963518008b8ae0152d48be4bbbe77015d2c717f6282eea1824be9a"
@@ -781,6 +806,7 @@ dependencies = [
"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
"checksum url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5ba8a749fb4479b043733416c244fa9d1d3af3d7c23804944651c8a448cb87e"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
+"checksum uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "78c590b5bd79ed10aad8fb75f078a59d8db445af6c743e55c4a53227fc01c13f"
"checksum uuid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d0f5103675a280a926ec2f9b7bcc2ef49367df54e8c570c3311fec919f9a8b"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
diff --git a/mullvad_daemon/src/main.rs b/mullvad_daemon/src/main.rs
index 62c09608a5..682d6b1290 100644
--- a/mullvad_daemon/src/main.rs
+++ b/mullvad_daemon/src/main.rs
@@ -50,6 +50,10 @@ error_chain!{
description("Error in the management interface")
display("Management interface error: {}", msg)
}
+ InvalidSettings(msg: &'static str) {
+ description("Invalid settings")
+ display("Invalid Settings: {}", msg)
+ }
}
}
@@ -118,6 +122,8 @@ struct Daemon {
// Just for testing. A cyclic iterator iterating over the hardcoded remotes,
// picking a new one for each retry.
remote_iter: std::iter::Cycle<std::iter::Cloned<std::slice::Iter<'static, Endpoint>>>,
+ // The current account token for now. Should be moved into the settings later.
+ account_token: Option<String>,
}
impl Daemon {
@@ -135,6 +141,7 @@ impl Daemon {
tunnel_close_handle: None,
management_interface_broadcaster,
remote_iter: REMOTES.iter().cloned().cycle(),
+ account_token: None,
},
)
}
@@ -226,6 +233,12 @@ impl Daemon {
warn!("Unable to send current state to management interface client",);
}
}
+ TunnelCommand::SetAccount(account_token) => self.account_token = account_token,
+ TunnelCommand::GetAccount(tx) => {
+ if let Err(_) = tx.send(self.account_token.clone()) {
+ warn!("Unable to send current account to management interface client");
+ }
+ }
}
Ok(())
}
@@ -306,7 +319,11 @@ impl Daemon {
ErrorKind::InvalidState
);
let remote = self.remote_iter.next().unwrap();
- let tunnel_monitor = self.spawn_tunnel_monitor(remote)?;
+ let account_token = self.account_token
+ .as_ref()
+ .ok_or(ErrorKind::InvalidSettings("No account token"))?
+ .clone();
+ let tunnel_monitor = self.spawn_tunnel_monitor(remote, &account_token)?;
self.tunnel_close_handle = Some(tunnel_monitor.close_handle());
self.spawn_tunnel_monitor_wait_thread(tunnel_monitor);
@@ -314,13 +331,13 @@ impl Daemon {
Ok(())
}
- fn spawn_tunnel_monitor(&self, remote: Endpoint) -> Result<TunnelMonitor> {
+ fn spawn_tunnel_monitor(&self, remote: Endpoint, account_token: &str) -> Result<TunnelMonitor> {
// Must wrap the channel in a Mutex because TunnelMonitor forces the closure to be Sync
let event_tx = Arc::new(Mutex::new(self.tx.clone()));
let on_tunnel_event = move |event| {
let _ = event_tx.lock().unwrap().send(DaemonEvent::TunnelEvent(event));
};
- TunnelMonitor::new(remote, on_tunnel_event)
+ TunnelMonitor::new(remote, account_token, on_tunnel_event)
.chain_err(|| ErrorKind::TunnelError("Unable to start tunnel monitor"))
}
diff --git a/mullvad_daemon/src/management_interface.rs b/mullvad_daemon/src/management_interface.rs
index fccdae2414..4b5a104d5e 100644
--- a/mullvad_daemon/src/management_interface.rs
+++ b/mullvad_daemon/src/management_interface.rs
@@ -48,9 +48,13 @@ build_rpc_trait! {
#[rpc(name = "get_countries")]
fn get_countries(&self) -> Result<HashMap<CountryCode, String>, Error>;
- /// Set which account to connect with
+ /// Set which account to connect with.
#[rpc(name = "set_account")]
- fn set_account(&self, AccountToken) -> Result<(), Error>;
+ fn set_account(&self, Option<AccountToken>) -> Result<(), Error>;
+
+ /// Get which account is configured.
+ #[rpc(async, name = "get_account")]
+ fn get_account(&self) -> BoxFuture<Option<AccountToken>, Error>;
/// Set which country to connect to
#[rpc(name = "set_country")]
@@ -113,6 +117,10 @@ pub enum TunnelCommand {
SetTargetState(TargetState),
/// Request the current state.
GetState(sync::oneshot::Sender<SecurityState>),
+ /// Set which account token to use for subsequent connection attempts.
+ SetAccount(Option<AccountToken>),
+ /// Request the current account token being used.
+ GetAccount(sync::oneshot::Sender<Option<AccountToken>>),
}
#[derive(Default)]
@@ -252,9 +260,22 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem
Ok(HashMap::new())
}
- fn set_account(&self, _account_token: AccountToken) -> Result<(), Error> {
+ fn set_account(&self, account_token: Option<AccountToken>) -> Result<(), Error> {
trace!("set_account");
- Ok(())
+ self.tx
+ .lock()
+ .unwrap()
+ .send(TunnelCommand::SetAccount(account_token))
+ .map_err(|_| Error::internal_error())
+ }
+
+ fn get_account(&self) -> BoxFuture<Option<AccountToken>, Error> {
+ trace!("get_account");
+ let (tx, rx) = sync::oneshot::channel();
+ match self.tx.lock().unwrap().send(TunnelCommand::GetAccount(tx)) {
+ Ok(()) => rx.map_err(|_| Error::internal_error()).boxed(),
+ Err(_) => future::err(Error::internal_error()).boxed(),
+ }
}
fn set_country(&self, _country_code: CountryCode) -> Result<(), Error> {
diff --git a/talpid_core/Cargo.toml b/talpid_core/Cargo.toml
index 573cc0b328..4538630f39 100644
--- a/talpid_core/Cargo.toml
+++ b/talpid_core/Cargo.toml
@@ -9,6 +9,7 @@ duct = "0.9.1"
error-chain = "0.10"
log = "0.3"
lazy_static = "0.2"
+mktemp = "0.3"
jsonrpc-core = { git = "https://github.com/faern/jsonrpc", branch = "ws-close-handle" }
jsonrpc-macros = { git = "https://github.com/faern/jsonrpc", branch = "ws-close-handle" }
diff --git a/talpid_core/src/lib.rs b/talpid_core/src/lib.rs
index 52213ebee0..1fc92f8fe4 100644
--- a/talpid_core/src/lib.rs
+++ b/talpid_core/src/lib.rs
@@ -12,6 +12,7 @@ extern crate duct;
extern crate lazy_static;
#[macro_use]
extern crate log;
+extern crate mktemp;
#[macro_use]
extern crate error_chain;
diff --git a/talpid_core/src/process/openvpn.rs b/talpid_core/src/process/openvpn.rs
index ba6688b034..09e845b640 100644
--- a/talpid_core/src/process/openvpn.rs
+++ b/talpid_core/src/process/openvpn.rs
@@ -35,6 +35,7 @@ pub struct OpenVpnCommand {
openvpn_bin: OsString,
config: Option<PathBuf>,
remote: Option<net::Endpoint>,
+ user_pass_path: Option<PathBuf>,
plugin: Option<(PathBuf, Vec<String>)>,
}
@@ -46,6 +47,7 @@ impl OpenVpnCommand {
openvpn_bin: OsString::from(openvpn_bin.as_ref()),
config: None,
remote: None,
+ user_pass_path: None,
plugin: None,
}
}
@@ -62,6 +64,13 @@ impl OpenVpnCommand {
self
}
+ /// Sets the path to the file where the username and password for user-pass authentication is
+ /// stored. See the `--auth-user-pass` OpenVPN documentation for details.
+ pub fn user_pass<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+ self.user_pass_path = Some(path.as_ref().to_path_buf());
+ self
+ }
+
/// Sets a plugin and its arguments that OpenVPN will be started with.
pub fn plugin<P: AsRef<Path>>(&mut self, path: P, args: Vec<String>) -> &mut Self {
self.plugin = Some((path.as_ref().to_path_buf(), args));
@@ -84,6 +93,7 @@ impl OpenVpnCommand {
}
args.extend(self.remote_arguments().iter().map(OsString::from));
+ args.extend(self.authentication_arguments());
if let Some((ref path, ref plugin_args)) = self.plugin {
args.push(OsString::from("--plugin"));
@@ -129,6 +139,15 @@ impl OpenVpnCommand {
}
args
}
+
+ fn authentication_arguments(&self) -> Vec<OsString> {
+ let mut args = vec![];
+ if let Some(ref user_pass_path) = self.user_pass_path {
+ args.push(OsString::from("--auth-user-pass"));
+ args.push(OsString::from(user_pass_path));
+ }
+ args
+ }
}
impl fmt::Display for OpenVpnCommand {
diff --git a/talpid_core/src/tunnel/mod.rs b/talpid_core/src/tunnel/mod.rs
index 45b518ad9e..7f4fa7ff5f 100644
--- a/talpid_core/src/tunnel/mod.rs
+++ b/talpid_core/src/tunnel/mod.rs
@@ -1,7 +1,9 @@
+use mktemp;
use net;
use openvpn_ffi::OpenVpnPluginEvent;
use process::openvpn::OpenVpnCommand;
-use std::io;
+use std::fs;
+use std::io::{self, Write};
use std::path::{Path, PathBuf};
/// A module for all OpenVPN related tunnel management.
@@ -20,6 +22,10 @@ mod errors {
PluginNotFound {
description("No OpenVPN plugin found")
}
+ /// There was an error when writing authentication credentials to temporary file.
+ CredentialsWriteError {
+ description("Error while writing credentials to temporary file")
+ }
}
}
}
@@ -51,30 +57,62 @@ impl TunnelEvent {
/// Abstraction for monitoring a generic VPN tunnel.
pub struct TunnelMonitor {
monitor: OpenVpnMonitor,
+ _user_pass_file: mktemp::Temp,
}
impl TunnelMonitor {
/// Creates a new `TunnelMonitor` that connects to the given remote and notifies `on_event`
/// on tunnel state changes.
- pub fn new<L>(remote: net::Endpoint, on_event: L) -> Result<Self>
+ pub fn new<L>(remote: net::Endpoint, account_token: &str, on_event: L) -> Result<Self>
where L: Fn(TunnelEvent) + Send + Sync + 'static
{
let on_openvpn_event = move |event, _env| match TunnelEvent::from_openvpn_event(&event) {
Some(tunnel_event) => on_event(tunnel_event),
None => debug!("Ignoring OpenVpnEvent {:?}", event),
};
- let cmd = Self::create_openvpn_cmd(remote);
+ let user_pass_file = Self::create_user_pass_file(account_token)
+ .chain_err(|| ErrorKind::CredentialsWriteError)?;
+ let cmd = Self::create_openvpn_cmd(remote, user_pass_file.as_ref());
let monitor = openvpn::OpenVpnMonitor::new(cmd, on_openvpn_event, get_plugin_path()?)
.chain_err(|| ErrorKind::TunnelMonitoringError)?;
- Ok(TunnelMonitor { monitor })
+ Ok(
+ TunnelMonitor {
+ monitor,
+ _user_pass_file: user_pass_file,
+ },
+ )
}
- fn create_openvpn_cmd(remote: net::Endpoint) -> OpenVpnCommand {
+ fn create_openvpn_cmd(remote: net::Endpoint, user_pass_file: &Path) -> OpenVpnCommand {
let mut cmd = OpenVpnCommand::new("openvpn");
- cmd.config(get_config_path()).remote(remote);
+ cmd.config(get_config_path()).remote(remote).user_pass(user_pass_file);
cmd
}
+ fn create_user_pass_file(account_token: &str) -> io::Result<mktemp::Temp> {
+ let path = mktemp::Temp::new_file()?;
+ debug!(
+ "Writing user-pass credentials to {}",
+ path.as_ref().to_string_lossy()
+ );
+ let mut file = fs::File::create(&path)?;
+ Self::set_user_pass_file_permissions(&file)?;
+ write!(file, "{}\n-\n", account_token)?;
+ Ok(path)
+ }
+
+ #[cfg(unix)]
+ fn set_user_pass_file_permissions(file: &fs::File) -> io::Result<()> {
+ use std::os::unix::fs::PermissionsExt;
+ file.set_permissions(PermissionsExt::from_mode(0o400))
+ }
+
+ #[cfg(windows)]
+ fn set_user_pass_file_permissions(file: &fs::File) -> io::Result<()> {
+ // TODO(linus): Lock permissions correctly on Windows.
+ Ok(())
+ }
+
/// Creates a handle to this monitor, allowing the tunnel to be closed while some other thread
/// is blocked in `wait`.
pub fn close_handle(&self) -> CloseHandle {