From b7039a7a69777c26993f12f9b8e0bd8c2878d1e2 Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Tue, 10 May 2016 00:12:10 +0200 Subject: [PATCH] feat(transport-smtp): Change default authentication mecanism default handling Change the default authentication mechanism selection check if the connection is encrypted, and only test PLAIN when it is the case. Also make the .authentication_mechnaism only take one mechanism, as a user will specify it he wants to ensure one particular method will be used. Closes #65 --- src/email/mod.rs | 2 +- src/lib.rs | 4 ++-- src/transport/smtp/client/mod.rs | 10 +++++++++- src/transport/smtp/client/net.rs | 9 +++++++++ src/transport/smtp/mod.rs | 25 +++++++++++++++++++------ 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/email/mod.rs b/src/email/mod.rs index 0d1a179..e404c03 100644 --- a/src/email/mod.rs +++ b/src/email/mod.rs @@ -72,7 +72,7 @@ pub struct EmailBuilder { date_issued: bool, } -/// todo +/// Simple email enveloppe representation #[derive(PartialEq,Eq,Clone,Debug)] pub struct Envelope { /// The envelope recipients' addresses diff --git a/src/lib.rs b/src/lib.rs index 212b4bb..25ed52b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,8 +144,8 @@ //! .security_level(SecurityLevel::AlwaysEncrypt) //! // Enable SMTPUTF8 if the server supports it //! .smtp_utf8(true) -//! // Configure accepted authentication mechanisms -//! .authentication_mechanisms(vec![Mechanism::CramMd5]) +//! // Configure expected authentication mechanism +//! .authentication_mechanism(Mechanism::CramMd5) //! // Enable connection reuse //! .connection_reuse(true).build(); //! diff --git a/src/transport/smtp/client/mod.rs b/src/transport/smtp/client/mod.rs index cd28019..710b247 100644 --- a/src/transport/smtp/client/mod.rs +++ b/src/transport/smtp/client/mod.rs @@ -82,7 +82,15 @@ impl Client { pub fn upgrade_tls_stream(&mut self, ssl_context: &SslContext) -> io::Result<()> { match self.stream { Some(ref mut stream) => stream.get_mut().upgrade_tls(ssl_context), - None => Ok(()) + None => Ok(()), + } + } + + /// Tells if the underlying stream is currently encrypted + pub fn is_encrypted(&self) -> bool { + match self.stream { + Some(ref stream) => stream.get_ref().is_encrypted(), + None => false, } } diff --git a/src/transport/smtp/client/net.rs b/src/transport/smtp/client/net.rs index a7b1298..15472a1 100644 --- a/src/transport/smtp/client/net.rs +++ b/src/transport/smtp/client/net.rs @@ -14,6 +14,8 @@ pub trait Connector: Sized { fn connect(addr: &SocketAddr, ssl_context: Option<&SslContext>) -> io::Result; /// Upgrades to TLS connection fn upgrade_tls(&mut self, ssl_context: &SslContext) -> io::Result<()>; + /// Is the NetworkStream encrypted + fn is_encrypted(&self) -> bool; } impl Connector for NetworkStream { @@ -43,6 +45,13 @@ impl Connector for NetworkStream { }; Ok(()) } + + fn is_encrypted(&self) -> bool { + match *self { + NetworkStream::Plain(_) => false, + NetworkStream::Ssl(_) => true, + } + } } diff --git a/src/transport/smtp/mod.rs b/src/transport/smtp/mod.rs index 01a5650..a194b64 100644 --- a/src/transport/smtp/mod.rs +++ b/src/transport/smtp/mod.rs @@ -83,8 +83,8 @@ pub struct SmtpTransportBuilder { security_level: SecurityLevel, /// Enable UTF8 mailboxes in envelope or headers smtp_utf8: bool, - /// List of authentication mechanisms, sorted by priority - authentication_mechanisms: Vec, + /// Optionnal enforced authentication mechanism + authentication_mechanism: Option, } /// Builder for the SMTP SmtpTransport @@ -104,7 +104,7 @@ impl SmtpTransportBuilder { connection_reuse_count_limit: 100, connection_reuse: false, hello_name: "localhost".to_string(), - authentication_mechanisms: vec![Mechanism::CramMd5, Mechanism::Plain], + authentication_mechanism: None, }) } None => Err(From::from("Could nor resolve hostname")), @@ -175,8 +175,8 @@ impl SmtpTransportBuilder { } /// Set the authentication mechanisms - pub fn authentication_mechanisms(mut self, mechanisms: Vec) -> SmtpTransportBuilder { - self.authentication_mechanisms = mechanisms; + pub fn authentication_mechanism(mut self, mechanism: Mechanism) -> SmtpTransportBuilder { + self.authentication_mechanism = Some(mechanism); self } @@ -325,7 +325,20 @@ impl EmailTransport for SmtpTransport { let mut found = false; - for mechanism in self.client_info.authentication_mechanisms.clone() { + // Compute accepted mechnism + let accepted_mechanisms = match self.client_info.authentication_mechanism { + Some(mechanism) => vec![mechanism], + None => { + match self.client.is_encrypted() { + // If encrypted, allow all mechanisms, with a preference for the simplest + true => vec![Mechanism::Plain, Mechanism::CramMd5], + // If not encrypted, do not all clear-text passwords + false => vec![Mechanism::CramMd5], + } + } + }; + + for mechanism in accepted_mechanisms { if self.server_info.as_ref().unwrap().supports_auth_mechanism(mechanism) { found = true; try_smtp!(self.client.auth(mechanism, &username, &password), self);