MTA-STS Policy management

This commit is contained in:
mdecimus
2024-05-07 15:20:54 +02:00
parent 3e9d0ae5d2
commit baef85e55b
5 changed files with 311 additions and 36 deletions

View File

@@ -254,6 +254,17 @@ impl JMAP {
};
}
}
("mta-sts.txt", &Method::GET) => {
if let Some(policy) = self.core.build_mta_sts_policy() {
return Resource {
content_type: "text/plain",
contents: policy.to_string().into_bytes(),
}
.into_http_response();
} else {
return RequestError::not_found().into_http_response();
}
}
(_, &Method::OPTIONS) => {
return ().into_http_response();
}

View File

@@ -224,6 +224,72 @@ impl JMAP {
content: "v=spf1 mx ra=postmaster -all".to_string(),
});
records.push(DnsRecord {
typ: "CNAME".to_string(),
name: format!("autoconfig.{domain_name}."),
content: format!("{server_name}."),
});
let mut has_https = false;
for (protocol, port, is_tls) in self
.core
.storage
.config
.get_services()
.await
.unwrap_or_default()
{
match (protocol.as_str(), port) {
("smtp", port @ 26..=u16::MAX) => {
records.push(DnsRecord {
typ: "SRV".to_string(),
name: format!(
"_submission{}._tcp.{domain_name}.",
if is_tls { "s" } else { "" }
),
content: format!("0 1 {port} {server_name}."),
});
}
("imap" | "pop3", port @ 1..=u16::MAX) => {
records.push(DnsRecord {
typ: "SRV".to_string(),
name: format!(
"_{protocol}{}._tcp.{domain_name}.",
if is_tls { "s" } else { "" }
),
content: format!("0 1 {port} {server_name}."),
});
}
("http", port @ 1..=u16::MAX) => {
if is_tls {
has_https = true;
records.push(DnsRecord {
typ: "SRV".to_string(),
name: format!("_autodiscover._tcp.{domain_name}."),
content: format!("0 1 {port} {server_name}."),
});
}
}
_ => (),
}
}
// Add MTA-STS record
if has_https {
if let Some(policy) = self.core.build_mta_sts_policy() {
records.push(DnsRecord {
typ: "CNAME".to_string(),
name: format!("mta-sts.{domain_name}."),
content: format!("{server_name}."),
});
records.push(DnsRecord {
typ: "TXT".to_string(),
name: format!("_mta-sts.{domain_name}."),
content: format!("v=STSv1; id={}", policy.id),
});
}
}
// Add DMARC records
records.push(DnsRecord {
typ: "TXT".to_string(),
@@ -236,39 +302,37 @@ impl JMAP {
if !name.ends_with(domain_name) {
continue;
}
let cert = if let Some(cert) = key.cert.first().map(|cert| cert.as_ref()) {
cert
} else {
tracing::debug!("No certificate found for domain: {}", domain_name);
continue;
};
let parsed_cert = match parse_x509_certificate(cert) {
Ok((_, parsed_cert)) => parsed_cert,
Err(err) => {
tracing::debug!("Failed to parse certificate: {}", err);
continue;
}
};
let name = if !name.starts_with('.') {
format!("_25._tcp.{name}.")
} else {
format!("_25._tcp.mail.{name}.")
};
for (cert_num, cert) in key.cert.iter().enumerate() {
let parsed_cert = match parse_x509_certificate(cert) {
Ok((_, parsed_cert)) => parsed_cert,
Err(err) => {
tracing::debug!("Failed to parse certificate: {}", err);
continue;
}
};
for (s, cert) in [cert, parsed_cert.subject_pki.raw].into_iter().enumerate() {
for (m, hash) in [
format!("{:x}", sha2::Sha256::digest(cert)),
format!("{:x}", sha2::Sha512::digest(cert)),
]
.into_iter()
.enumerate()
{
records.push(DnsRecord {
typ: "TLSA".to_string(),
name: name.clone(),
content: format!("3 {} {} {}", s, m + 1, hash),
});
let name = if !name.starts_with('.') {
format!("_25._tcp.{name}.")
} else {
format!("_25._tcp.mail.{name}.")
};
let cu = if cert_num == 0 { 3 } else { 2 };
for (s, cert) in [cert, parsed_cert.subject_pki.raw].into_iter().enumerate() {
for (m, hash) in [
format!("{:x}", sha2::Sha256::digest(cert)),
format!("{:x}", sha2::Sha512::digest(cert)),
]
.into_iter()
.enumerate()
{
records.push(DnsRecord {
typ: "TLSA".to_string(),
name: name.clone(),
content: format!("{} {} {} {}", cu, s, m + 1, hash),
});
}
}
}
}