From c877583979ab0bd76c135b1d88d58298bb9e3680 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Sat, 12 Sep 2020 21:47:24 +0200 Subject: [PATCH] Allow multiple SMTP Auth meganisms. - Allow all SMTP Auth meganisms supported by Lettre. - The config value order is leading and values can be separated by a comma ',' - Case doesn't matter, and invalid values are ignored. - Warning is printed when no valid value is found at all. --- .env.template | 11 +++++++---- src/config.rs | 4 ++-- src/mail.rs | 20 +++++++++++++++----- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.env.template b/.env.template index 61b960dc..06fd163e 100644 --- a/.env.template +++ b/.env.template @@ -1,7 +1,7 @@ ## Bitwarden_RS Configuration File ## Uncomment any of the following lines to change the defaults -## -## Be aware that most of these settings will be overridden if they were changed +## +## Be aware that most of these settings will be overridden if they were changed ## in the admin interface. Those overrides are stored within DATA_FOLDER/config.json . ## Main data folder @@ -70,7 +70,7 @@ ## Log level ## Change the verbosity of the log output ## Valid values are "trace", "debug", "info", "warn", "error" and "off" -## Setting it to "trace" or "debug" would also show logs for mounted +## Setting it to "trace" or "debug" would also show logs for mounted ## routes and static file, websocket and alive requests # LOG_LEVEL=Info @@ -184,7 +184,7 @@ ## Authenticator Settings ## Disable authenticator time drifted codes to be valid. ## TOTP codes of the previous and next 30 seconds will be invalid -## +## ## According to the RFC6238 (https://tools.ietf.org/html/rfc6238), ## we allow by default the TOTP code which was valid one step back and one in the future. ## This can however allow attackers to be a bit more lucky with there attempts because there are 3 valid codes. @@ -210,6 +210,9 @@ # SMTP_EXPLICIT_TLS=true # N.B. This variable configures Implicit TLS. It's currently mislabelled (see bug #851) # SMTP_USERNAME=username # SMTP_PASSWORD=password +## Defaults for SSL is "Plain" and "Login" and nothing for Non-SSL connections. +## Possible values: ["Plain", "Login", "Xoauth2"]. +## Multiple options need to be separated by a comma ','. # SMTP_AUTH_MECHANISM="Plain" # SMTP_TIMEOUT=15 diff --git a/src/config.rs b/src/config.rs index 9491edf2..4e25674d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -400,7 +400,7 @@ make_config! { smtp_username: String, true, option; /// Password smtp_password: Pass, true, option; - /// Json form auth mechanism |> Defaults for ssl is "Plain" and "Login" and nothing for non-ssl connections. Possible values: ["Plain", "Login", "Xoauth2"] + /// Json form auth mechanism |> Defaults for ssl is "Plain" and "Login" and nothing for non-ssl connections. Possible values: ["Plain", "Login", "Xoauth2"]. Multiple options need to be separated by a comma. smtp_auth_mechanism: String, true, option; /// SMTP connection timeout |> Number of seconds when to stop trying to connect to the SMTP server smtp_timeout: u64, true, def, 15; @@ -428,7 +428,7 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { let dom = cfg.domain.to_lowercase(); if !dom.starts_with("http://") && !dom.starts_with("https://") { - err!("DOMAIN variable needs to contain the protocol (http, https). Use 'http[s]://bw.example.com' instead of 'bw.example.com'"); + err!("DOMAIN variable needs to contain the protocol (http, https). Use 'http[s]://bw.example.com' instead of 'bw.example.com'"); } let whitelist = &cfg.signups_domains_whitelist; diff --git a/src/mail.rs b/src/mail.rs index a80b7ca5..2c6d7362 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -55,12 +55,22 @@ fn mailer() -> SmtpTransport { let smtp_client = match CONFIG.smtp_auth_mechanism() { Some(mechanism) => { - let correct_mechanism = format!("\"{}\"", crate::util::upcase_first(mechanism.trim_matches('"'))); + let allowed_mechanisms = vec![SmtpAuthMechanism::Plain, SmtpAuthMechanism::Login, SmtpAuthMechanism::Xoauth2]; + let mut selected_mechanisms = vec![]; + for wanted_mechanism in mechanism.split(',') { + for m in &allowed_mechanisms { + if m.to_string().to_lowercase() == wanted_mechanism.trim_matches(|c| c == '"' || c == '\'' || c == ' ').to_lowercase() { + selected_mechanisms.push(m.clone()); + } + } + }; - // TODO: Allow more than one mechanism - match serde_json::from_str::(&correct_mechanism) { - Ok(auth_mechanism) => smtp_client.authentication(vec![auth_mechanism]), - _ => panic!("Failure to parse mechanism. Is it proper Json? Eg. `\"Plain\"` not `Plain`"), + if !selected_mechanisms.is_empty() { + smtp_client.authentication(selected_mechanisms) + } else { + // Only show a warning, and return without setting an actual authentication mechanism + warn!("No valid SMTP Auth mechanism found for '{}', using default values", mechanism); + smtp_client } } _ => smtp_client,