Merge pull request #647 from deltachat/tls_checks_config

Add certificate check configuration options
This commit is contained in:
Alexander Krotov
2019-10-02 23:21:03 +00:00
committed by GitHub
5 changed files with 114 additions and 24 deletions

View File

@@ -157,6 +157,11 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
self.live_count += 1 self.live_count += 1
if "e2ee_enabled" not in configdict: if "e2ee_enabled" not in configdict:
configdict["e2ee_enabled"] = "1" configdict["e2ee_enabled"] = "1"
# Enable strict certificate checks for online accounts
configdict["imap_certificate_checks"] = "1"
configdict["smtp_certificate_checks"] = "1"
tmpdb = tmpdir.join("livedb%d" % self.live_count) tmpdb = tmpdir.join("livedb%d" % self.live_count)
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count)) ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count))
ac._evlogger.init_time = self.init_time ac._evlogger.init_time = self.init_time

View File

@@ -19,10 +19,12 @@ pub enum Config {
MailUser, MailUser,
MailPw, MailPw,
MailPort, MailPort,
ImapCertificateChecks,
SendServer, SendServer,
SendUser, SendUser,
SendPw, SendPw,
SendPort, SendPort,
SmtpCertificateChecks,
ServerFlags, ServerFlags,
#[strum(props(default = "INBOX"))] #[strum(props(default = "INBOX"))]
ImapFolder, ImapFolder,
@@ -52,10 +54,12 @@ pub enum Config {
ConfiguredMailPw, ConfiguredMailPw,
ConfiguredMailPort, ConfiguredMailPort,
ConfiguredMailSecurity, ConfiguredMailSecurity,
ConfiguredImapCertificateChecks,
ConfiguredSendServer, ConfiguredSendServer,
ConfiguredSendUser, ConfiguredSendUser,
ConfiguredSendPw, ConfiguredSendPw,
ConfiguredSendPort, ConfiguredSendPort,
ConfiguredSmtpCertificateChecks,
ConfiguredServerFlags, ConfiguredServerFlags,
ConfiguredSendSecurity, ConfiguredSendSecurity,
ConfiguredE2EEEnabled, ConfiguredE2EEEnabled,

View File

@@ -11,7 +11,7 @@ use crate::dc_receive_imf::dc_receive_imf;
use crate::error::Error; use crate::error::Error;
use crate::events::Event; use crate::events::Event;
use crate::job::{connect_to_inbox, job_add, Action}; use crate::job::{connect_to_inbox, job_add, Action};
use crate::login_param::LoginParam; use crate::login_param::{dc_build_tls, CertificateChecks, LoginParam};
use crate::message::{self, update_msg_move_state, update_server_uid}; use crate::message::{self, update_msg_move_state, update_server_uid};
use crate::oauth2::dc_get_oauth2_access_token; use crate::oauth2::dc_get_oauth2_access_token;
use crate::param::Params; use crate::param::Params;
@@ -108,14 +108,10 @@ impl Client {
pub fn connect_secure<A: net::ToSocketAddrs, S: AsRef<str>>( pub fn connect_secure<A: net::ToSocketAddrs, S: AsRef<str>>(
addr: A, addr: A,
domain: S, domain: S,
certificate_checks: CertificateChecks,
) -> imap::error::Result<Self> { ) -> imap::error::Result<Self> {
let stream = net::TcpStream::connect(addr)?; let stream = net::TcpStream::connect(addr)?;
let tls = native_tls::TlsConnector::builder() let tls = dc_build_tls(certificate_checks).unwrap();
// see also: https://github.com/deltachat/deltachat-core-rust/issues/203
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
.unwrap();
let s = stream.try_clone().expect("cloning the stream failed"); let s = stream.try_clone().expect("cloning the stream failed");
let tls_stream = native_tls::TlsConnector::connect(&tls, domain.as_ref(), s)?; let tls_stream = native_tls::TlsConnector::connect(&tls, domain.as_ref(), s)?;
@@ -135,13 +131,14 @@ impl Client {
Ok(Client::Insecure(client, stream)) Ok(Client::Insecure(client, stream))
} }
pub fn secure<S: AsRef<str>>(self, domain: S) -> imap::error::Result<Client> { pub fn secure<S: AsRef<str>>(
self,
domain: S,
certificate_checks: CertificateChecks,
) -> imap::error::Result<Client> {
match self { match self {
Client::Insecure(client, stream) => { Client::Insecure(client, stream) => {
let tls = native_tls::TlsConnector::builder() let tls = dc_build_tls(certificate_checks).unwrap();
.danger_accept_invalid_hostnames(true)
.build()
.unwrap();
let client_sec = client.secure(domain, &tls)?; let client_sec = client.secure(domain, &tls)?;
@@ -321,6 +318,7 @@ struct ImapConfig {
pub imap_port: u16, pub imap_port: u16,
pub imap_user: String, pub imap_user: String,
pub imap_pw: String, pub imap_pw: String,
pub certificate_checks: CertificateChecks,
pub server_flags: usize, pub server_flags: usize,
pub selected_folder: Option<String>, pub selected_folder: Option<String>,
pub selected_mailbox: Option<imap::types::Mailbox>, pub selected_mailbox: Option<imap::types::Mailbox>,
@@ -339,6 +337,7 @@ impl Default for ImapConfig {
imap_port: 0, imap_port: 0,
imap_user: "".into(), imap_user: "".into(),
imap_pw: "".into(), imap_pw: "".into(),
certificate_checks: Default::default(),
server_flags: 0, server_flags: 0,
selected_folder: None, selected_folder: None,
selected_mailbox: None, selected_mailbox: None,
@@ -397,7 +396,7 @@ impl Imap {
Client::connect_insecure((imap_server, imap_port)).and_then(|client| { Client::connect_insecure((imap_server, imap_port)).and_then(|client| {
if (server_flags & DC_LP_IMAP_SOCKET_STARTTLS) != 0 { if (server_flags & DC_LP_IMAP_SOCKET_STARTTLS) != 0 {
client.secure(imap_server) client.secure(imap_server, config.certificate_checks)
} else { } else {
Ok(client) Ok(client)
} }
@@ -407,7 +406,11 @@ impl Imap {
let imap_server: &str = config.imap_server.as_ref(); let imap_server: &str = config.imap_server.as_ref();
let imap_port = config.imap_port; let imap_port = config.imap_port;
Client::connect_secure((imap_server, imap_port), imap_server) Client::connect_secure(
(imap_server, imap_port),
imap_server,
config.certificate_checks,
)
}; };
let login_res = match connection_res { let login_res = match connection_res {
@@ -534,6 +537,7 @@ impl Imap {
config.imap_port = imap_port; config.imap_port = imap_port;
config.imap_user = imap_user.to_string(); config.imap_user = imap_user.to_string();
config.imap_pw = imap_pw.to_string(); config.imap_pw = imap_pw.to_string();
config.certificate_checks = lp.imap_certificate_checks;
config.server_flags = server_flags; config.server_flags = server_flags;
} }

View File

@@ -4,6 +4,22 @@ use std::fmt;
use crate::context::Context; use crate::context::Context;
use crate::error::Error; use crate::error::Error;
#[derive(Copy, Clone, Debug, Display, FromPrimitive)]
#[repr(i32)]
#[strum(serialize_all = "snake_case")]
pub enum CertificateChecks {
Automatic = 0,
Strict = 1,
AcceptInvalidHostnames = 2,
AcceptInvalidCertificates = 3,
}
impl Default for CertificateChecks {
fn default() -> Self {
Self::Automatic
}
}
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct LoginParam { pub struct LoginParam {
pub addr: String, pub addr: String,
@@ -11,10 +27,14 @@ pub struct LoginParam {
pub mail_user: String, pub mail_user: String,
pub mail_pw: String, pub mail_pw: String,
pub mail_port: i32, pub mail_port: i32,
/// IMAP TLS options: whether to allow invalid certificates and/or invalid hostnames
pub imap_certificate_checks: CertificateChecks,
pub send_server: String, pub send_server: String,
pub send_user: String, pub send_user: String,
pub send_pw: String, pub send_pw: String,
pub send_port: i32, pub send_port: i32,
/// SMTP TLS options: whether to allow invalid certificates and/or invalid hostnames
pub smtp_certificate_checks: CertificateChecks,
pub server_flags: i32, pub server_flags: i32,
} }
@@ -48,6 +68,14 @@ impl LoginParam {
let key = format!("{}mail_pw", prefix); let key = format!("{}mail_pw", prefix);
let mail_pw = sql.get_config(context, key).unwrap_or_default(); let mail_pw = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}imap_certificate_checks", prefix);
let imap_certificate_checks =
if let Some(certificate_checks) = sql.get_config_int(context, key) {
num_traits::FromPrimitive::from_i32(certificate_checks).unwrap_or_default()
} else {
Default::default()
};
let key = format!("{}send_server", prefix); let key = format!("{}send_server", prefix);
let send_server = sql.get_config(context, key).unwrap_or_default(); let send_server = sql.get_config(context, key).unwrap_or_default();
@@ -60,6 +88,14 @@ impl LoginParam {
let key = format!("{}send_pw", prefix); let key = format!("{}send_pw", prefix);
let send_pw = sql.get_config(context, key).unwrap_or_default(); let send_pw = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}smtp_certificate_checks", prefix);
let smtp_certificate_checks =
if let Some(certificate_checks) = sql.get_config_int(context, key) {
num_traits::FromPrimitive::from_i32(certificate_checks).unwrap_or_default()
} else {
Default::default()
};
let key = format!("{}server_flags", prefix); let key = format!("{}server_flags", prefix);
let server_flags = sql.get_config_int(context, key).unwrap_or_default(); let server_flags = sql.get_config_int(context, key).unwrap_or_default();
@@ -69,10 +105,12 @@ impl LoginParam {
mail_user, mail_user,
mail_pw, mail_pw,
mail_port, mail_port,
imap_certificate_checks,
send_server, send_server,
send_user, send_user,
send_pw, send_pw,
send_port, send_port,
smtp_certificate_checks,
server_flags, server_flags,
} }
} }
@@ -105,6 +143,9 @@ impl LoginParam {
let key = format!("{}mail_pw", prefix); let key = format!("{}mail_pw", prefix);
sql.set_config(context, key, Some(&self.mail_pw))?; sql.set_config(context, key, Some(&self.mail_pw))?;
let key = format!("{}imap_certificate_checks", prefix);
sql.set_config_int(context, key, self.imap_certificate_checks as i32)?;
let key = format!("{}send_server", prefix); let key = format!("{}send_server", prefix);
sql.set_config(context, key, Some(&self.send_server))?; sql.set_config(context, key, Some(&self.send_server))?;
@@ -117,6 +158,9 @@ impl LoginParam {
let key = format!("{}send_pw", prefix); let key = format!("{}send_pw", prefix);
sql.set_config(context, key, Some(&self.send_pw))?; sql.set_config(context, key, Some(&self.send_pw))?;
let key = format!("{}smtp_certificate_checks", prefix);
sql.set_config_int(context, key, self.smtp_certificate_checks as i32)?;
let key = format!("{}server_flags", prefix); let key = format!("{}server_flags", prefix);
sql.set_config_int(context, key, self.server_flags)?; sql.set_config_int(context, key, self.server_flags)?;
@@ -133,16 +177,18 @@ impl fmt::Display for LoginParam {
write!( write!(
f, f,
"{} {}:{}:{}:{} {}:{}:{}:{} {}", "{} imap:{}:{}:{}:{}:cert_{} smtp:{}:{}:{}:{}:cert_{} {}",
unset_empty(&self.addr), unset_empty(&self.addr),
unset_empty(&self.mail_user), unset_empty(&self.mail_user),
if !self.mail_pw.is_empty() { pw } else { unset }, if !self.mail_pw.is_empty() { pw } else { unset },
unset_empty(&self.mail_server), unset_empty(&self.mail_server),
self.mail_port, self.mail_port,
self.imap_certificate_checks,
unset_empty(&self.send_user), unset_empty(&self.send_user),
if !self.send_pw.is_empty() { pw } else { unset }, if !self.send_pw.is_empty() { pw } else { unset },
unset_empty(&self.send_server), unset_empty(&self.send_server),
self.send_port, self.send_port,
self.smtp_certificate_checks,
flags_readable, flags_readable,
) )
} }
@@ -204,3 +250,41 @@ fn get_readable_flags(flags: i32) -> String {
res res
} }
pub fn dc_build_tls(
certificate_checks: CertificateChecks,
) -> Result<native_tls::TlsConnector, native_tls::Error> {
let mut tls_builder = native_tls::TlsConnector::builder();
match certificate_checks {
CertificateChecks::Automatic => {
// Same as AcceptInvalidCertificates for now.
// TODO: use provider database when it becomes available
tls_builder
.danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true)
}
CertificateChecks::Strict => &mut tls_builder,
CertificateChecks::AcceptInvalidHostnames => {
tls_builder.danger_accept_invalid_hostnames(true)
}
CertificateChecks::AcceptInvalidCertificates => tls_builder
.danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true),
}
.build()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_certificate_checks_display() {
use std::string::ToString;
assert_eq!(
"accept_invalid_hostnames".to_string(),
CertificateChecks::AcceptInvalidHostnames.to_string()
);
}
}

View File

@@ -5,7 +5,7 @@ use crate::constants::*;
use crate::context::Context; use crate::context::Context;
use crate::error::Error; use crate::error::Error;
use crate::events::Event; use crate::events::Event;
use crate::login_param::LoginParam; use crate::login_param::{dc_build_tls, LoginParam};
use crate::oauth2::*; use crate::oauth2::*;
#[derive(DebugStub)] #[derive(DebugStub)]
@@ -68,14 +68,7 @@ impl Smtp {
let domain = &lp.send_server; let domain = &lp.send_server;
let port = lp.send_port as u16; let port = lp.send_port as u16;
let tls = native_tls::TlsConnector::builder() let tls = dc_build_tls(lp.smtp_certificate_checks).unwrap();
// see also: https://github.com/deltachat/deltachat-core-rust/issues/203
.danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true)
.min_protocol_version(Some(DEFAULT_TLS_PROTOCOLS[0]))
.build()
.unwrap();
let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls); let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls);
let creds = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) { let creds = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) {