aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2021-06-07 20:42:01 +0200
committerGustav Sörnäs <gustav@sornas.net>2021-06-07 20:42:01 +0200
commit9f1d465ac411ef2efc5930bbdf56b8ea67b48690 (patch)
tree82418246bf2fd4864cccd88a4e5baebc5358ac67
parentbe76c2aa51733a0cf495e92659fbcbe527f41149 (diff)
downloadmum-9f1d465ac411ef2efc5930bbdf56b8ea67b48690.tar.gz
specify if we accept invalid server certs or not
-rw-r--r--mumctl/src/main.rs31
-rw-r--r--mumd/src/command.rs23
-rw-r--r--mumd/src/network/tcp.rs33
-rw-r--r--mumd/src/state.rs45
-rw-r--r--mumlib/src/command.rs1
-rw-r--r--mumlib/src/config.rs14
6 files changed, 105 insertions, 42 deletions
diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs
index bde24a1..cb0ec2f 100644
--- a/mumctl/src/main.rs
+++ b/mumctl/src/main.rs
@@ -236,7 +236,7 @@ fn match_opt() -> Result<(), Error> {
} => {
let port = port.unwrap_or(mumlib::DEFAULT_PORT);
- let (host, username, password, port) =
+ let (host, username, password, port, accept_invalid_cert) =
match config.servers.iter().find(|e| e.name == host) {
Some(server) => (
&server.host,
@@ -247,27 +247,45 @@ fn match_opt() -> Result<(), Error> {
.ok_or(CliError::NoUsername)?,
server.password.as_ref().or(password.as_ref()),
server.port.unwrap_or(port),
+ server.accept_invalid_cert,
),
None => (
&host,
username.as_ref().ok_or(CliError::NoUsername)?,
password.as_ref(),
port,
+ None,
),
};
+ let accept_invalid_cert = accept_invalid_cert
+ .or(config.allow_invalid_server_cert);
+ let specified_accept_invalid_cert = accept_invalid_cert.is_some();
+
let response = send_command(MumCommand::ServerConnect {
host: host.to_string(),
port,
username: username.to_string(),
password: password.map(|x| x.to_string()),
- accept_invalid_cert: true, //TODO
+ accept_invalid_cert: accept_invalid_cert.unwrap_or(false), //TODO force true/false via flags
})??;
- if let Some(CommandResponse::ServerConnect { welcome_message }) = response {
- println!("Connected to {}", host);
- if let Some(message) = welcome_message {
- println!("Welcome: {}", message);
+ match response {
+ Some(CommandResponse::ServerConnect { welcome_message }) => {
+ println!("Connected to {}", host);
+ if let Some(message) = welcome_message {
+ println!("Welcome: {}", message);
+ }
+ }
+ Some(CommandResponse::ServerCertReject) => {
+ error!("Connection rejected since the server supplied an invalid certificate.");
+ if !specified_accept_invalid_cert {
+ eprintln!("help: If you trust this server anyway, you can do any of the following to connect:");
+ // eprintln!(" 1. Temporarily trust this server by passing --accept-invalid-cert when connecting.");
+ eprintln!(" 1. Permanently trust this server by setting accept_invalid_cert=true in the server's config.");
+ eprintln!(" 2. Permantently trust all invalid certificates by setting accept_all_invalid_certs=true globally");
+ }
}
+ other => unreachable!("Response should only be a ServerConnect or ServerCertReject. Got {:?}", other)
}
}
Command::Disconnect => {
@@ -536,6 +554,7 @@ fn match_server_command(server_command: Server, config: &mut Config) -> Result<(
port,
username,
password,
+ accept_invalid_cert: None, //TODO
});
}
}
diff --git a/mumd/src/command.rs b/mumd/src/command.rs
index 410751a..73ab3bd 100644
--- a/mumd/src/command.rs
+++ b/mumd/src/command.rs
@@ -32,16 +32,19 @@ pub async fn handle(
&mut connection_info_sender,
);
match event {
- ExecutionContext::TcpEventCallback(event, generator) => {
- tcp_event_queue.register_callback(
- event,
- Box::new(move |e| {
- let response = generator(e);
- for response in response {
- response_sender.send(response).unwrap();
- }
- }),
- );
+ ExecutionContext::TcpEventCallback(callbacks) => {
+ for (event, generator) in callbacks {
+ let response_sender = response_sender.clone();
+ tcp_event_queue.register_callback(
+ event,
+ Box::new(move |e| {
+ let response = generator(e);
+ for response in response {
+ response_sender.send(response).unwrap();
+ }
+ }),
+ );
+ }
}
ExecutionContext::TcpEventSubscriber(event, mut handler) => tcp_event_queue
.register_subscriber(
diff --git a/mumd/src/network/tcp.rs b/mumd/src/network/tcp.rs
index 5cc2bf7..1c7123f 100644
--- a/mumd/src/network/tcp.rs
+++ b/mumd/src/network/tcp.rs
@@ -35,17 +35,23 @@ type TcpReceiver =
pub(crate) type TcpEventCallback = Box<dyn FnOnce(TcpEventData)>;
pub(crate) type TcpEventSubscriber = Box<dyn FnMut(TcpEventData) -> bool>; //the bool indicates if it should be kept or not
-#[derive(Debug, Clone, Hash, Eq, PartialEq)]
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+pub enum DisconnectedReason {
+ InvalidTls,
+ Other,
+}
+
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum TcpEvent {
Connected, //fires when the client has connected to a server
- Disconnected, //fires when the client has disconnected from a server
+ Disconnected(DisconnectedReason), //fires when the client has disconnected from a server
TextMessage, //fires when a text message comes in
}
#[derive(Clone)]
pub enum TcpEventData<'a> {
Connected(Result<&'a msgs::ServerSync, mumlib::Error>),
- Disconnected,
+ Disconnected(DisconnectedReason),
TextMessage(&'a msgs::TextMessage),
}
@@ -53,7 +59,7 @@ impl<'a> From<&TcpEventData<'a>> for TcpEvent {
fn from(t: &TcpEventData) -> Self {
match t {
TcpEventData::Connected(_) => TcpEvent::Connected,
- TcpEventData::Disconnected => TcpEvent::Disconnected,
+ TcpEventData::Disconnected(reason) => TcpEvent::Disconnected(*reason),
TcpEventData::TextMessage(_) => TcpEvent::TextMessage,
}
}
@@ -141,12 +147,25 @@ pub async fn handle(
}
return Err(TcpError::NoConnectionInfoReceived);
};
- let (mut sink, stream) = connect(
+ let connect_result = connect(
connection_info.socket_addr,
connection_info.hostname,
connection_info.accept_invalid_cert,
)
- .await?;
+ .await;
+
+ let (mut sink, stream) = match connect_result {
+ Ok(ok) => ok,
+ Err(TcpError::TlsConnectError(_)) => {
+ warn!("Invalid TLS");
+ state.read().unwrap().broadcast_phase(StatePhase::Disconnected);
+ event_queue.resolve(TcpEventData::Disconnected(DisconnectedReason::InvalidTls));
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ };
// Handshake (omitting `Version` message for brevity)
let (username, password) = {
@@ -193,7 +212,7 @@ pub async fn handle(
.await
.unwrap_or(Ok(()))?;
- event_queue.resolve(TcpEventData::Disconnected);
+ event_queue.resolve(TcpEventData::Disconnected(DisconnectedReason::Other));
debug!("Fully disconnected TCP stream, waiting for new connection info");
}
diff --git a/mumd/src/state.rs b/mumd/src/state.rs
index 84583e0..e0e3ccd 100644
--- a/mumd/src/state.rs
+++ b/mumd/src/state.rs
@@ -2,7 +2,7 @@ pub mod channel;
pub mod server;
pub mod user;
-use crate::audio::{AudioInput, AudioOutput, NotificationEvents};
+use crate::{audio::{AudioInput, AudioOutput, NotificationEvents}, network::tcp::DisconnectedReason};
use crate::error::StateError;
use crate::network::tcp::{TcpEvent, TcpEventData};
use crate::network::{ConnectionInfo, VoiceStreamType};
@@ -26,8 +26,10 @@ use std::{
use tokio::sync::{mpsc, watch};
macro_rules! at {
- ($event:expr, $generator:expr) => {
- ExecutionContext::TcpEventCallback($event, Box::new($generator))
+ ( $( $event:expr => $generator:expr ),+ $(,)? ) => {
+ ExecutionContext::TcpEventCallback(vec![
+ $( ($event, Box::new($generator)), )*
+ ])
};
}
@@ -41,7 +43,7 @@ type Responses = Box<dyn Iterator<Item = mumlib::error::Result<Option<CommandRes
//TODO give me a better name
pub enum ExecutionContext {
- TcpEventCallback(TcpEvent, Box<dyn FnOnce(TcpEventData) -> Responses>),
+ TcpEventCallback(Vec<(TcpEvent, Box<dyn FnOnce(TcpEventData) -> Responses>)>),
TcpEventSubscriber(
TcpEvent,
Box<
@@ -555,22 +557,27 @@ pub fn handle_command(
accept_invalid_cert,
)))
.unwrap();
- at!(TcpEvent::Connected, |res| {
- //runs the closure when the client is connected
- if let TcpEventData::Connected(res) = res {
- Box::new(iter::once(res.map(|msg| {
- Some(CommandResponse::ServerConnect {
- welcome_message: if msg.has_welcome_text() {
- Some(msg.get_welcome_text().to_string())
- } else {
- None
- },
- })
- })))
- } else {
- unreachable!("callback should be provided with a TcpEventData::Connected");
+ at!(
+ TcpEvent::Connected => |res| {
+ //runs the closure when the client is connected
+ if let TcpEventData::Connected(res) = res {
+ Box::new(iter::once(res.map(|msg| {
+ Some(CommandResponse::ServerConnect {
+ welcome_message: if msg.has_welcome_text() {
+ Some(msg.get_welcome_text().to_string())
+ } else {
+ None
+ },
+ })
+ })))
+ } else {
+ unreachable!("callback should be provided with a TcpEventData::Connected");
+ }
+ },
+ TcpEvent::Disconnected(DisconnectedReason::InvalidTls) => |_| {
+ Box::new(iter::once(Ok(Some(CommandResponse::ServerCertReject))))
}
- })
+ )
}
Command::ServerDisconnect => {
if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
diff --git a/mumlib/src/command.rs b/mumlib/src/command.rs
index 351d7f6..36bda72 100644
--- a/mumlib/src/command.rs
+++ b/mumlib/src/command.rs
@@ -53,6 +53,7 @@ pub enum CommandResponse {
ServerConnect {
welcome_message: Option<String>,
},
+ ServerCertReject,
ServerStatus {
version: u32,
users: u32,
diff --git a/mumlib/src/config.rs b/mumlib/src/config.rs
index 1bd3784..3edef37 100644
--- a/mumlib/src/config.rs
+++ b/mumlib/src/config.rs
@@ -10,8 +10,15 @@ use std::path::{Path, PathBuf};
use toml::value::Array;
use toml::Value;
+/// A TOML-friendly version of [Config].
+///
+/// Values need to be placed before tables due to how TOML works.
#[derive(Debug, Deserialize, Serialize)]
struct TOMLConfig {
+ // Values
+ accept_all_invalid_certs: Option<bool>,
+
+ // Tables
audio: Option<AudioConfig>,
servers: Option<Array>,
}
@@ -20,6 +27,10 @@ struct TOMLConfig {
pub struct Config {
pub audio: AudioConfig,
pub servers: Vec<ServerConfig>,
+ /// Whether we allow connecting to servers with invalid server certificates.
+ ///
+ /// None implies false but we can show a better message to the user.
+ pub allow_invalid_server_cert: Option<bool>,
}
impl Config {
@@ -64,6 +75,7 @@ pub struct ServerConfig {
pub port: Option<u16>,
pub username: Option<String>,
pub password: Option<String>,
+ pub accept_invalid_cert: Option<bool>,
}
impl ServerConfig {
@@ -105,6 +117,7 @@ impl TryFrom<TOMLConfig> for Config {
})
.transpose()?
.unwrap_or_default(),
+ allow_invalid_server_cert: config.accept_all_invalid_certs,
})
}
}
@@ -125,6 +138,7 @@ impl From<Config> for TOMLConfig {
.map(|s| Value::try_from::<ServerConfig>(s).unwrap())
.collect(),
),
+ accept_all_invalid_certs: config.allow_invalid_server_cert,
}
}
}