summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-10-12 10:50:42 +0200
committerLinus Färnstrand <linus@mullvad.net>2017-10-12 10:50:42 +0200
commitd2199c89e37cf89789f8f2fd9af0a4a9c72a407a (patch)
tree26bfb6bc2a2b7ad0bcb68f4a87542609f4c354d7
parent5c06a80f3c8d96e1fff453f081197d1692dd8a90 (diff)
parentf1712e671c6e8d2db142f7e77d172e52ec87d4cf (diff)
downloadmullvadvpn-d2199c89e37cf89789f8f2fd9af0a4a9c72a407a.tar.xz
mullvadvpn-d2199c89e37cf89789f8f2fd9af0a4a9c72a407a.zip
Merge branch 'authenticate-rpc-api'
-rw-r--r--mullvad-daemon/src/main.rs11
-rw-r--r--mullvad-daemon/src/management_interface.rs137
-rw-r--r--mullvad-daemon/src/rpc_info.rs6
3 files changed, 114 insertions, 40 deletions
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index c0687026a3..2525a39308 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -229,15 +229,16 @@ impl Daemon {
fn start_management_interface_server(
event_tx: IntoSender<TunnelCommand, DaemonEvent>,
) -> Result<ManagementInterfaceServer> {
- let server = ManagementInterfaceServer::start(event_tx).chain_err(|| {
- ErrorKind::ManagementInterfaceError("Failed to start server")
- })?;
+ let shared_secret = uuid::Uuid::new_v4().to_string();
+ let server = ManagementInterfaceServer::start(event_tx, shared_secret.clone()).chain_err(
+ || ErrorKind::ManagementInterfaceError("Failed to start server"),
+ )?;
info!(
"Mullvad management interface listening on {}",
server.address()
);
- rpc_info::write(server.address()).chain_err(|| {
- ErrorKind::ManagementInterfaceError("Failed to write RPC address to file")
+ rpc_info::write(server.address(), &shared_secret).chain_err(|| {
+ ErrorKind::ManagementInterfaceError("Failed to write RPC connection info to file")
})?;
Ok(server)
}
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index 7b222b8504..e2fb6f33f7 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -19,6 +19,7 @@ use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::{Arc, Mutex, RwLock};
+use std::sync::atomic::{AtomicBool, Ordering};
use talpid_core::mpsc::IntoSender;
use talpid_ipc;
@@ -33,48 +34,53 @@ build_rpc_trait! {
pub trait ManagementInterfaceApi {
type Metadata;
+ /// Authenticate the client towards this daemon instance. This method must be called once
+ /// before any other call based on the same connection will work.
+ #[rpc(meta, name = "auth")]
+ fn auth(&self, Self::Metadata, String) -> BoxFuture<(), Error>;
+
/// Fetches and returns metadata about an account. Returns an error on non-existing
/// accounts.
- #[rpc(async, name = "get_account_data")]
- fn get_account_data(&self, AccountToken) -> BoxFuture<AccountData, Error>;
+ #[rpc(meta, name = "get_account_data")]
+ fn get_account_data(&self, Self::Metadata, AccountToken) -> BoxFuture<AccountData, Error>;
/// Returns available countries.
#[rpc(name = "get_countries")]
fn get_countries(&self) -> Result<HashMap<CountryCode, String>, Error>;
/// Set which account to connect with.
- #[rpc(async, name = "set_account")]
- fn set_account(&self, Option<AccountToken>) -> BoxFuture<(), Error>;
+ #[rpc(meta, name = "set_account")]
+ fn set_account(&self, Self::Metadata, Option<AccountToken>) -> BoxFuture<(), Error>;
/// Get which account is configured.
- #[rpc(async, name = "get_account")]
- fn get_account(&self) -> BoxFuture<Option<AccountToken>, Error>;
+ #[rpc(meta, name = "get_account")]
+ fn get_account(&self, Self::Metadata) -> BoxFuture<Option<AccountToken>, Error>;
/// Set which relay to connect to
- #[rpc(async, name = "set_custom_relay")]
- fn set_custom_relay(&self, RelayEndpoint) -> BoxFuture<(), Error>;
+ #[rpc(meta, name = "set_custom_relay")]
+ fn set_custom_relay(&self, Self::Metadata, RelayEndpoint) -> BoxFuture<(), Error>;
/// Unset the custom relay, reverting to the default relay listing
- #[rpc(async, name = "remove_custom_relay")]
- fn remove_custom_relay(&self) -> BoxFuture<(), Error>;
+ #[rpc(meta, name = "remove_custom_relay")]
+ fn remove_custom_relay(&self, Self::Metadata) -> BoxFuture<(), Error>;
/// Set if the client should automatically establish a tunnel on start or not.
- #[rpc(name = "set_autoconnect")]
- fn set_autoconnect(&self, bool) -> Result<(), Error>;
+ #[rpc(meta, name = "set_autoconnect")]
+ fn set_autoconnect(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
/// Try to connect if disconnected, or do nothing if already connecting/connected.
- #[rpc(async, name = "connect")]
- fn connect(&self) -> BoxFuture<(), Error>;
+ #[rpc(meta, name = "connect")]
+ fn connect(&self, Self::Metadata) -> BoxFuture<(), Error>;
/// Disconnect the VPN tunnel if it is connecting/connected. Does nothing if already
/// disconnected.
- #[rpc(async, name = "disconnect")]
- fn disconnect(&self) -> BoxFuture<(), Error>;
+ #[rpc(meta, name = "disconnect")]
+ fn disconnect(&self, Self::Metadata) -> BoxFuture<(), Error>;
/// Returns the current state of the Mullvad client. Changes to this state will
/// be announced to subscribers of `new_state`.
- #[rpc(async, name = "get_state")]
- fn get_state(&self) -> BoxFuture<DaemonState, Error>;
+ #[rpc(meta, name = "get_state")]
+ fn get_state(&self, Self::Metadata) -> BoxFuture<DaemonState, Error>;
/// Returns the current public IP of this computer.
#[rpc(name = "get_ip")]
@@ -139,11 +145,14 @@ pub struct ManagementInterfaceServer {
}
impl ManagementInterfaceServer {
- pub fn start<T>(tunnel_tx: IntoSender<TunnelCommand, T>) -> talpid_ipc::Result<Self>
+ pub fn start<T>(
+ tunnel_tx: IntoSender<TunnelCommand, T>,
+ shared_secret: String,
+ ) -> talpid_ipc::Result<Self>
where
T: From<TunnelCommand> + 'static + Send,
{
- let rpc = ManagementInterface::new(tunnel_tx);
+ let rpc = ManagementInterface::new(tunnel_tx, shared_secret);
let subscriptions = rpc.subscriptions.clone();
let mut io = PubSubHandler::default();
@@ -210,13 +219,15 @@ impl EventBroadcaster {
struct ManagementInterface<T: From<TunnelCommand> + 'static + Send> {
subscriptions: Arc<ActiveSubscriptions>,
tx: Mutex<IntoSender<TunnelCommand, T>>,
+ shared_secret: String,
}
impl<T: From<TunnelCommand> + 'static + Send> ManagementInterface<T> {
- pub fn new(tx: IntoSender<TunnelCommand, T>) -> Self {
+ pub fn new(tx: IntoSender<TunnelCommand, T>, shared_secret: String) -> Self {
ManagementInterface {
subscriptions: Default::default(),
tx: Mutex::new(tx),
+ shared_secret,
}
}
@@ -280,13 +291,49 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterface<T> {
_ => Error::internal_error(),
}
}
+
+ fn check_auth(&self, meta: &Meta) -> Result<(), Error> {
+ if meta.authenticated.load(Ordering::SeqCst) {
+ trace!("auth success");
+ Ok(())
+ } else {
+ trace!("auth failed");
+ Err(Error::invalid_request())
+ }
+ }
+}
+
+/// Evaluates a Result and early returns an error.
+/// If it is `Ok(val)`, evaluates to `val`.
+/// If it is `Err(e)` it early returns `Box<Future>` where the future will result in `e`.
+macro_rules! try_future {
+ ($result:expr) => (match $result {
+ ::std::result::Result::Ok(val) => val,
+ ::std::result::Result::Err(e) => return Box::new(future::err(e)),
+ });
}
impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for ManagementInterface<T> {
type Metadata = Meta;
- fn get_account_data(&self, account_token: AccountToken) -> BoxFuture<AccountData, Error> {
+ fn auth(&self, meta: Self::Metadata, shared_secret: String) -> BoxFuture<(), Error> {
+ let authenticated = shared_secret == self.shared_secret;
+ meta.authenticated.store(authenticated, Ordering::SeqCst);
+ debug!("authenticated: {}", authenticated);
+ if authenticated {
+ Box::new(future::ok(()))
+ } else {
+ Box::new(future::err(Error::internal_error()))
+ }
+ }
+
+ fn get_account_data(
+ &self,
+ meta: Self::Metadata,
+ account_token: AccountToken,
+ ) -> BoxFuture<AccountData, Error> {
trace!("get_account_data");
+ try_future!(self.check_auth(&meta));
let (tx, rx) = sync::oneshot::channel();
let future = self.send_command_to_daemon(TunnelCommand::GetAccountData(tx, account_token))
.and_then(|_| rx.map_err(|_| Error::internal_error()))
@@ -307,24 +354,35 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem
Ok(HashMap::new())
}
- fn set_account(&self, account_token: Option<AccountToken>) -> BoxFuture<(), Error> {
+ fn set_account(
+ &self,
+ meta: Self::Metadata,
+ account_token: Option<AccountToken>,
+ ) -> BoxFuture<(), Error> {
trace!("set_account");
+ try_future!(self.check_auth(&meta));
let (tx, rx) = sync::oneshot::channel();
let future = self.send_command_to_daemon(TunnelCommand::SetAccount(tx, account_token))
.and_then(|_| rx.map_err(|_| Error::internal_error()));
Box::new(future)
}
- fn get_account(&self) -> BoxFuture<Option<AccountToken>, Error> {
+ fn get_account(&self, meta: Self::Metadata) -> BoxFuture<Option<AccountToken>, Error> {
trace!("get_account");
+ try_future!(self.check_auth(&meta));
let (tx, rx) = sync::oneshot::channel();
let future = self.send_command_to_daemon(TunnelCommand::GetAccount(tx))
.and_then(|_| rx.map_err(|_| Error::internal_error()));
Box::new(future)
}
- fn set_custom_relay(&self, custom_relay: RelayEndpoint) -> BoxFuture<(), Error> {
+ fn set_custom_relay(
+ &self,
+ meta: Self::Metadata,
+ custom_relay: RelayEndpoint,
+ ) -> BoxFuture<(), Error> {
trace!("set_custom_relay");
+ try_future!(self.check_auth(&meta));
let (tx, rx) = sync::oneshot::channel();
let message = TunnelCommand::SetCustomRelay(tx, Some(custom_relay));
@@ -333,31 +391,36 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem
Box::new(future)
}
- fn remove_custom_relay(&self) -> BoxFuture<(), Error> {
+ fn remove_custom_relay(&self, meta: Self::Metadata) -> BoxFuture<(), Error> {
trace!("remove_custom_relay");
+ try_future!(self.check_auth(&meta));
let (tx, rx) = sync::oneshot::channel();
let future = self.send_command_to_daemon(TunnelCommand::SetCustomRelay(tx, None))
.and_then(|_| rx.map_err(|_| Error::internal_error()));
Box::new(future)
}
- fn set_autoconnect(&self, _autoconnect: bool) -> Result<(), Error> {
+ fn set_autoconnect(&self, meta: Self::Metadata, _autoconnect: bool) -> BoxFuture<(), Error> {
trace!("set_autoconnect");
- Ok(())
+ try_future!(self.check_auth(&meta));
+ Box::new(future::ok(()))
}
- fn connect(&self) -> BoxFuture<(), Error> {
+ fn connect(&self, meta: Self::Metadata) -> BoxFuture<(), Error> {
trace!("connect");
+ try_future!(self.check_auth(&meta));
self.send_command_to_daemon(TunnelCommand::SetTargetState(TargetState::Secured))
}
- fn disconnect(&self) -> BoxFuture<(), Error> {
+ fn disconnect(&self, meta: Self::Metadata) -> BoxFuture<(), Error> {
trace!("disconnect");
+ try_future!(self.check_auth(&meta));
self.send_command_to_daemon(TunnelCommand::SetTargetState(TargetState::Unsecured))
}
- fn get_state(&self) -> BoxFuture<DaemonState, Error> {
+ fn get_state(&self, meta: Self::Metadata) -> BoxFuture<DaemonState, Error> {
trace!("get_state");
+ try_future!(self.check_auth(&meta));
let (state_tx, state_rx) = sync::oneshot::channel();
let future = self.send_command_to_daemon(TunnelCommand::GetState(state_tx))
.and_then(|_| state_rx.map_err(|_| Error::internal_error()));
@@ -380,10 +443,13 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem
fn new_state_subscribe(
&self,
- _meta: Self::Metadata,
+ meta: Self::Metadata,
subscriber: pubsub::Subscriber<DaemonState>,
) {
trace!("new_state_subscribe");
+ if self.check_auth(&meta).is_err() {
+ return;
+ }
Self::subscribe(subscriber, &self.subscriptions.new_state_subscriptions);
}
@@ -392,8 +458,11 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem
Self::unsubscribe(id, &self.subscriptions.new_state_subscriptions)
}
- fn error_subscribe(&self, _meta: Self::Metadata, subscriber: pubsub::Subscriber<Vec<String>>) {
+ fn error_subscribe(&self, meta: Self::Metadata, subscriber: pubsub::Subscriber<Vec<String>>) {
trace!("error_subscribe");
+ if self.check_auth(&meta).is_err() {
+ return;
+ }
Self::subscribe(subscriber, &self.subscriptions.error_subscriptions);
}
@@ -410,6 +479,7 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem
#[derive(Clone, Debug, Default)]
pub struct Meta {
session: Option<Arc<Session>>,
+ authenticated: Arc<AtomicBool>,
}
/// Make the `Meta` type possible to use as jsonrpc metadata type.
@@ -426,5 +496,6 @@ impl PubSubMetadata for Meta {
fn meta_extractor(context: &jsonrpc_ws_server::RequestContext) -> Meta {
Meta {
session: Some(Arc::new(Session::new(context.sender()))),
+ authenticated: Arc::new(AtomicBool::new(false)),
}
}
diff --git a/mullvad-daemon/src/rpc_info.rs b/mullvad-daemon/src/rpc_info.rs
index 13d354d9bb..7826b736a8 100644
--- a/mullvad-daemon/src/rpc_info.rs
+++ b/mullvad-daemon/src/rpc_info.rs
@@ -29,9 +29,11 @@ lazy_static! {
/// Writes down the RPC address to some API to a file.
-pub fn write(rpc_address: &str) -> Result<()> {
+pub fn write(rpc_address: &str, shared_secret: &str) -> Result<()> {
open_file(RPC_ADDRESS_FILE_PATH.as_path())
- .and_then(|mut file| file.write_all(rpc_address.as_bytes()))
+ .and_then(|mut file| {
+ write!(file, "{}\n{}", rpc_address, shared_secret)
+ })
.chain_err(|| ErrorKind::WriteFailed(RPC_ADDRESS_FILE_PATH.to_owned()))?;
debug!(