summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-06-26 11:49:30 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-06-26 11:49:30 -0300
commitfcb82716f1827d565ebee7b883581d82f9f257da (patch)
treed8ed0a4e81145d00e2d4c2305cfff596f1d3c9a6
parentf23235725532f22ef14d6d93869d6e1b3c6785e5 (diff)
parent3a60d226753a6169640c8b5499b94afc0cda172a (diff)
downloadmullvadvpn-fcb82716f1827d565ebee7b883581d82f9f257da.tar.xz
mullvadvpn-fcb82716f1827d565ebee7b883581d82f9f257da.zip
Merge branch 'talpid-plugin-auth-test'
-rw-r--r--.travis.yml2
-rw-r--r--Cargo.lock2
-rwxr-xr-xintegration-tests.sh6
-rw-r--r--mullvad-tests/Cargo.toml2
-rw-r--r--mullvad-tests/src/lib.rs60
-rw-r--r--mullvad-tests/tests/connection.rs162
6 files changed, 222 insertions, 12 deletions
diff --git a/.travis.yml b/.travis.yml
index 80554d2727..be815c7db0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -52,7 +52,6 @@ matrix:
- rustup component add rustfmt-preview
- rustfmt --version
- cargo fmt -- --check --unstable-features
- - ./integration-tests.sh
- language: rust
rust: beta
@@ -62,7 +61,6 @@ matrix:
script: &rust_linux_script
- cargo build --verbose
- cargo test --verbose
- - ./integration-tests.sh
- language: rust
rust: stable
diff --git a/Cargo.lock b/Cargo.lock
index 3c02c48546..737173d112 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -848,7 +848,9 @@ dependencies = [
"mullvad-paths 0.1.0",
"mullvad-types 0.1.0",
"notify 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openvpn-plugin 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"os_pipe 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "talpid-ipc 0.1.0",
"tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
diff --git a/integration-tests.sh b/integration-tests.sh
index cfb5cdfdd8..f69614109f 100755
--- a/integration-tests.sh
+++ b/integration-tests.sh
@@ -1,12 +1,16 @@
#!/usr/bin/env bash
+if [ "$UID" -ne 0 ]; then
+ echo "WARNING: Not running as root, some tests may fail" >&2
+fi
+
MULLVAD_DIR="$(cd "$(dirname "$0")"; pwd -P)"
pushd "$MULLVAD_DIR"
cargo build \
&& cd mullvad-tests \
- && cargo test --features "integration-tests"
+ && cargo test --features "integration-tests" -- --test-threads=1
RESULT="$?"
popd
diff --git a/mullvad-tests/Cargo.toml b/mullvad-tests/Cargo.toml
index b114245c32..5760be7d49 100644
--- a/mullvad-tests/Cargo.toml
+++ b/mullvad-tests/Cargo.toml
@@ -14,7 +14,9 @@ mullvad-ipc-client = { path = "../mullvad-ipc-client" }
mullvad-paths = { path = "../mullvad-paths" }
mullvad-types = { path = "../mullvad-types" }
notify = "4.0"
+openvpn-plugin = { version = "0.3", features = ["serde"] }
os_pipe = "0.6"
+talpid-ipc = { path = "../talpid-ipc" }
tempfile = "3.0"
[target.'cfg(unix)'.dependencies]
diff --git a/mullvad-tests/src/lib.rs b/mullvad-tests/src/lib.rs
index 35d50e814c..54aea065c8 100644
--- a/mullvad-tests/src/lib.rs
+++ b/mullvad-tests/src/lib.rs
@@ -7,9 +7,12 @@ extern crate libc;
extern crate mullvad_ipc_client;
extern crate mullvad_paths;
extern crate notify;
+extern crate openvpn_plugin;
extern crate os_pipe;
+extern crate talpid_ipc;
extern crate tempfile;
+use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
@@ -17,10 +20,12 @@ use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
-use self::mullvad_ipc_client::DaemonRpcClient;
-use self::notify::{op, RawEvent, RecursiveMode, Watcher};
-use self::os_pipe::{pipe, PipeReader};
-use self::tempfile::TempDir;
+use mullvad_ipc_client::DaemonRpcClient;
+use notify::{op, RawEvent, RecursiveMode, Watcher};
+use openvpn_plugin::types::OpenVpnPluginEvent;
+use os_pipe::{pipe, PipeReader};
+use talpid_ipc::WsIpcClient;
+use tempfile::TempDir;
use self::platform_specific::*;
@@ -271,3 +276,50 @@ impl Drop for DaemonRunner {
let _ = fs::remove_file(&self.rpc_address_file);
}
}
+
+pub struct MockOpenVpnPluginRpcClient {
+ credentials: String,
+ rpc: WsIpcClient,
+}
+
+impl MockOpenVpnPluginRpcClient {
+ pub fn new(address: String, credentials: String) -> Result<Self, String> {
+ let rpc = WsIpcClient::connect(&address).map_err(|error| {
+ format!("Failed to create Mock OpenVPN plugin RPC client: {}", error)
+ })?;
+
+ Ok(MockOpenVpnPluginRpcClient { rpc, credentials })
+ }
+
+ pub fn authenticate(&mut self) -> Result<bool, String> {
+ self.rpc
+ .call("authenticate", &[&self.credentials])
+ .map_err(|error| format!("Failed to authenticate mock OpenVPN IPC client: {}", error))
+ }
+
+ pub fn authenticate_with(&mut self, credentials: &str) -> Result<bool, String> {
+ self.rpc
+ .call("authenticate", &[credentials])
+ .map_err(|error| format!("Failed to authenticate mock OpenVPN IPC client: {}", error))
+ }
+
+ pub fn up(&mut self) -> Result<(), String> {
+ let mut env: HashMap<String, String> = HashMap::new();
+
+ env.insert("dev".to_owned(), "dummy".to_owned());
+ env.insert("ifconfig_local".to_owned(), "10.0.0.10".to_owned());
+ env.insert("route_vpn_gateway".to_owned(), "10.0.0.1".to_owned());
+
+ self.send_event(OpenVpnPluginEvent::Up, env)
+ }
+
+ fn send_event(
+ &mut self,
+ event: OpenVpnPluginEvent,
+ env: HashMap<String, String>,
+ ) -> Result<(), String> {
+ self.rpc
+ .call("openvpn_event", &(event, env))
+ .map_err(|error| format!("Failed to send mock OpenVPN event {:?}: {}", event, error))
+ }
+}
diff --git a/mullvad-tests/tests/connection.rs b/mullvad-tests/tests/connection.rs
index 38dba38c2d..84306eeb14 100644
--- a/mullvad-tests/tests/connection.rs
+++ b/mullvad-tests/tests/connection.rs
@@ -4,19 +4,31 @@ extern crate mullvad_ipc_client;
extern crate mullvad_tests;
extern crate mullvad_types;
-use std::fs;
+use std::fs::{self, File};
+use std::io::{BufRead, BufReader};
+use std::path::Path;
use std::sync::mpsc;
use std::time::Duration;
-use mullvad_ipc_client::DaemonRpcClient;
-use mullvad_tests::{wait_for_file_write_finish, DaemonRunner};
+use mullvad_tests::{wait_for_file_write_finish, DaemonRunner, MockOpenVpnPluginRpcClient};
use mullvad_types::states::{DaemonState, SecurityState, TargetState};
+#[cfg(target_os = "linux")]
+const OPENVPN_PLUGIN_NAME: &str = "libtalpid_openvpn_plugin.so";
+
+#[cfg(windows)]
+const OPENVPN_PLUGIN_NAME: &str = "talpid_openvpn_plugin.dll";
+
const CONNECTING_STATE: DaemonState = DaemonState {
state: SecurityState::Unsecured,
target_state: TargetState::Secured,
};
+const CONNECTED_STATE: DaemonState = DaemonState {
+ state: SecurityState::Secured,
+ target_state: TargetState::Secured,
+};
+
#[test]
fn spawns_openvpn() {
let mut daemon = DaemonRunner::spawn();
@@ -63,14 +75,154 @@ fn changes_to_connecting_state() {
rpc_client.set_account(Some("123456".to_owned())).unwrap();
rpc_client.connect().unwrap();
- assert_state_event(state_events, CONNECTING_STATE);
+ assert_state_event(&state_events, CONNECTING_STATE);
+ assert_eq!(rpc_client.get_state().unwrap(), CONNECTING_STATE);
+}
+
+#[test]
+fn ignores_event_from_unauthorized_connection_from_openvpn_plugin() {
+ let mut daemon = DaemonRunner::spawn();
+ let mut rpc_client = daemon.rpc_client().unwrap();
+ let openvpn_args_file = daemon.mock_openvpn_args_file();
+ let state_events = rpc_client.new_state_subscribe().unwrap();
+
+ rpc_client.set_account(Some("123456".to_owned())).unwrap();
+ rpc_client.connect().unwrap();
+
+ assert_state_event(&state_events, CONNECTING_STATE);
+
+ let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file);
+ let call_result = mock_plugin_client.up();
+
+ assert!(call_result.is_err());
+ assert_no_state_event(&state_events);
assert_eq!(rpc_client.get_state().unwrap(), CONNECTING_STATE);
}
-fn assert_state_event(receiver: mpsc::Receiver<DaemonState>, expected_state: DaemonState) {
+#[test]
+fn authentication_credentials() {
+ let mut daemon = DaemonRunner::spawn();
+ let mut rpc_client = daemon.rpc_client().unwrap();
+ let openvpn_args_file = daemon.mock_openvpn_args_file();
+ let state_events = rpc_client.new_state_subscribe().unwrap();
+
+ rpc_client.set_account(Some("123456".to_owned())).unwrap();
+ rpc_client.connect().unwrap();
+
+ assert_state_event(&state_events, CONNECTING_STATE);
+
+ let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file);
+
+ assert_eq!(
+ mock_plugin_client.authenticate_with(&String::new()),
+ Ok(false)
+ );
+ assert_eq!(
+ mock_plugin_client.authenticate_with(&"fake-secret".to_owned()),
+ Ok(false)
+ );
+ assert_eq!(mock_plugin_client.authenticate(), Ok(true));
+ // Ensure it doesn't accept additional incorrect credentials
+ assert_eq!(
+ mock_plugin_client.authenticate_with(&"different-secret".to_owned()),
+ Ok(false)
+ );
+}
+
+#[test]
+fn separate_connections_have_independent_authentication() {
+ let mut daemon = DaemonRunner::spawn();
+ let mut rpc_client = daemon.rpc_client().unwrap();
+ let openvpn_args_file = daemon.mock_openvpn_args_file();
+ let state_events = rpc_client.new_state_subscribe().unwrap();
+
+ rpc_client.set_account(Some("123456".to_owned())).unwrap();
+ rpc_client.connect().unwrap();
+
+ assert_state_event(&state_events, CONNECTING_STATE);
+
+ let mut first_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file);
+ let mut second_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file);
+
+ let auth_result = first_plugin_client.authenticate();
+ let call_result = second_plugin_client.up();
+
+ assert_eq!(auth_result, Ok(true));
+ assert!(call_result.is_err());
+ assert_no_state_event(&state_events);
+ assert_eq!(rpc_client.get_state().unwrap(), CONNECTING_STATE);
+}
+
+#[test]
+fn changes_to_connected_state() {
+ let mut daemon = DaemonRunner::spawn();
+ let mut rpc_client = daemon.rpc_client().unwrap();
+ let openvpn_args_file = daemon.mock_openvpn_args_file();
+ let state_events = rpc_client.new_state_subscribe().unwrap();
+
+ rpc_client.set_account(Some("123456".to_owned())).unwrap();
+ rpc_client.connect().unwrap();
+
+ assert_state_event(&state_events, CONNECTING_STATE);
+
+ let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file);
+
+ mock_plugin_client.authenticate().unwrap();
+ mock_plugin_client.up().unwrap();
+
+ assert_state_event(&state_events, CONNECTED_STATE);
+ assert_eq!(rpc_client.get_state().unwrap(), CONNECTED_STATE);
+}
+
+fn assert_state_event(receiver: &mpsc::Receiver<DaemonState>, expected_state: DaemonState) {
let received_state = receiver
.recv_timeout(Duration::from_secs(1))
.expect("Failed to receive new state event from daemon");
assert_eq!(received_state, expected_state);
}
+
+fn assert_no_state_event(receiver: &mpsc::Receiver<DaemonState>) {
+ assert_eq!(
+ receiver.recv_timeout(Duration::from_secs(1)),
+ Err(mpsc::RecvTimeoutError::Timeout),
+ );
+}
+
+fn create_mock_openvpn_plugin_client<P: AsRef<Path>>(
+ openvpn_args_file_path: P,
+) -> MockOpenVpnPluginRpcClient {
+ let (address, credentials) = get_plugin_arguments(openvpn_args_file_path);
+
+ MockOpenVpnPluginRpcClient::new(address, credentials)
+ .expect("Failed to create mock RPC client to connect to OpenVPN plugin event listener")
+}
+
+fn get_plugin_arguments<P: AsRef<Path>>(openvpn_args_file_path: P) -> (String, String) {
+ let args_file_path = openvpn_args_file_path.as_ref();
+
+ wait_for_file_write_finish(&args_file_path, Duration::from_secs(5));
+
+ let args_file = File::open(&args_file_path).expect(&format!(
+ "Failed to open mock OpenVPN command-line file: {}",
+ args_file_path.display(),
+ ));
+
+ let args_reader = BufReader::new(args_file).lines();
+ let mut arguments = args_reader
+ .skip_while(|element| {
+ element.is_ok() && !element.as_ref().unwrap().contains(OPENVPN_PLUGIN_NAME)
+ })
+ .skip(1);
+
+ let address = arguments
+ .next()
+ .expect("Missing OpenVPN plugin RPC listener address argument")
+ .expect("Failed to read from mock OpenVPN arguments file");
+ let credentials = arguments
+ .next()
+ .expect("Missing OpenVPN plugin RPC listener credentials argument")
+ .expect("Failed to read from mock OpenVPN arguments file");
+
+ (address, credentials)
+}