diff options
| author | Gustav Sörnäs <gustav@sornas.net> | 2021-06-07 20:42:01 +0200 |
|---|---|---|
| committer | Gustav Sörnäs <gustav@sornas.net> | 2021-06-07 20:42:01 +0200 |
| commit | 9f1d465ac411ef2efc5930bbdf56b8ea67b48690 (patch) | |
| tree | 82418246bf2fd4864cccd88a4e5baebc5358ac67 | |
| parent | be76c2aa51733a0cf495e92659fbcbe527f41149 (diff) | |
| download | mum-9f1d465ac411ef2efc5930bbdf56b8ea67b48690.tar.gz | |
specify if we accept invalid server certs or not
| -rw-r--r-- | mumctl/src/main.rs | 31 | ||||
| -rw-r--r-- | mumd/src/command.rs | 23 | ||||
| -rw-r--r-- | mumd/src/network/tcp.rs | 33 | ||||
| -rw-r--r-- | mumd/src/state.rs | 45 | ||||
| -rw-r--r-- | mumlib/src/command.rs | 1 | ||||
| -rw-r--r-- | mumlib/src/config.rs | 14 |
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, } } } |
