mirror of
https://github.com/chatmail/relay.git
synced 2026-08-11 02:50:53 +00:00
fix(dkim): Accept TXT records with escaped quotes (#61)
This commit is contained in:
committed by
GitHub
parent
8a1684d488
commit
52208c42b6
@@ -14,6 +14,18 @@ use viadkim::verifier::LookupTxt;
|
||||
// "top 1000 relays" is much more than enough, the limit is mostly to prevent DoS attacks.
|
||||
const LRU_CACHE_CAPACITY: NonZeroUsize = NonZeroUsize::new(1000).expect("1000 != 0");
|
||||
|
||||
/// Normalizes a TXT record RDATA by removing irrelevant characters.
|
||||
///
|
||||
/// Some DKIM key records use e.g. LF + WSP line breaks.
|
||||
/// This is technically not correct, and `viadkim` fails to parse such records,
|
||||
/// but in practice this is accepted by many implementations.
|
||||
///
|
||||
/// Additionally, removes escaped quotes, as such records as:
|
||||
/// `"...UL9" "\" \"7vGm..."` proved to still be accepted by e.g. dkimpy or OpenDKIM.
|
||||
fn normalize_rdata(txt_data: &str) -> String {
|
||||
txt_data.replace([' ', '\t', '\n', '\r', '"'], "")
|
||||
}
|
||||
|
||||
/// DNS resolver for DKIM TXT records, that caches RDATA in memory.
|
||||
#[derive(Clone)]
|
||||
struct CachedResolver {
|
||||
@@ -38,15 +50,6 @@ impl CachedResolver {
|
||||
})
|
||||
}
|
||||
|
||||
/// Normalizes a TXT record RDATA by removing whitespace characters.
|
||||
///
|
||||
/// Some DKIM key records use e.g. LF + WSP line breaks.
|
||||
/// This is technically not correct, and `viadkim` fails to parse such records,
|
||||
/// but in practice this is accepted by many implementations.
|
||||
fn normalize_rdata(txt_data: &str) -> String {
|
||||
txt_data.replace([' ', '\t', '\n', '\r'], "")
|
||||
}
|
||||
|
||||
/// Invalidates the cached RDATA for a given selector and domain.
|
||||
///
|
||||
/// Fails silently.
|
||||
@@ -98,7 +101,10 @@ impl LookupTxt for CachedResolver {
|
||||
.map(|txt| {
|
||||
let rdata_raw = txt.txt_data().concat();
|
||||
let rdata = String::from_utf8_lossy(&rdata_raw);
|
||||
Self::normalize_rdata(&rdata).into_bytes()
|
||||
log::trace!("TXT (concat): {:?}", rdata);
|
||||
let normalized = normalize_rdata(&rdata);
|
||||
log::trace!("TXT (normalized): {:?}", normalized);
|
||||
normalized.into_bytes()
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
@@ -124,7 +130,8 @@ impl LookupTxt for MockResolver {
|
||||
|
||||
fn lookup_txt(&self, _domain: &str) -> Self::Query<'_> {
|
||||
Box::pin(async move {
|
||||
let txts: Self::Answer = Box::new(std::iter::once(Ok(self.0.clone().into_bytes())));
|
||||
let txts: Self::Answer =
|
||||
Box::new(std::iter::once(Ok(normalize_rdata(&self.0).into_bytes())));
|
||||
Ok(txts)
|
||||
})
|
||||
}
|
||||
@@ -256,13 +263,29 @@ impl DkimVerifier {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn test_dkim_verifier() {
|
||||
let verifier = DkimVerifier::mock(
|
||||
r#"v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5krC4Xi5Wkr6eMlla38LCFmV645E3FLAgsRl2YJ0SrZ4N2Vw1/yH0mefvtk7HYE7ytV7RQl/er2CkSsaHLJSYLmPCBw5CO6PSsBSXuh6DBqdylh/1t9vVQ9p38fTwn9gU1QvplcpRQL9eepRra1k24VMIaVy2ZZcu3LI9zkPsR7o7TyNaeMhsL8ouWInWc1NSid+p0SgliQuwHIejZhlTPE60JLbJE0OR9I4wmq3377H6z/QrO8XeabCgtmTuzE/hTRyIyNS40jql/99pjlhIcjM2U+P2B0FjwYt7BwLHsgANr74ctlnKY+SdH25rNwVpPmkotaULG5SJCByKBkfCwIDAQAB;s=email;t=s"#.to_string()
|
||||
);
|
||||
let raw_mail = include_bytes!("../test_data/dkim-abjadiyah.eml");
|
||||
verifier.verify(raw_mail, "abjadiyah.xyz").await.unwrap();
|
||||
#[case::simple_simple_canonicalization(
|
||||
r#"v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5krC4Xi5Wkr6eMlla38LCFmV645E3FLAgsRl2YJ0SrZ4N2Vw1/yH0mefvtk7HYE7ytV7RQl/er2CkSsaHLJSYLmPCBw5CO6PSsBSXuh6DBqdylh/1t9vVQ9p38fTwn9gU1QvplcpRQL9eepRra1k24VMIaVy2ZZcu3LI9zkPsR7o7TyNaeMhsL8ouWInWc1NSid+p0SgliQuwHIejZhlTPE60JLbJE0OR9I4wmq3377H6z/QrO8XeabCgtmTuzE/hTRyIyNS40jql/99pjlhIcjM2U+P2B0FjwYt7BwLHsgANr74ctlnKY+SdH25rNwVpPmkotaULG5SJCByKBkfCwIDAQAB;s=email;t=s"#,
|
||||
include_bytes!("../test_data/dkim-abjadiyah.eml")
|
||||
)]
|
||||
#[case::txt_escaped_quotes(
|
||||
r#"v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1giTh8KDkEchWhrAB6hGnb+V87kTezkt5I3SP7BGNg8wpv0yAuj/SUmnsttYmcEU+zmNAPqxePmCNvmjLYi/c3YyWEBwHcLyZE9OlS9W4enPdsoCuEN3DayzN4JCV3MsXMedCORvLFXmIARDXDLJUSJeqCeQoudXa9GmF1CrCmx70YyTtV0xOIxEzo7z0DkUL9" "7vGmNJCv6EMpi9wccMKKu8NSmOv+DBw1MLIJqChSZMCs8CYZ5i0KT/+Lijtn6B7wyOcAuQsVL+zr7DWYrFdrePe0wGuivfJ3SvUEfUo1SIykl0nvm0iLGhjNmNa1e/tUw4ULXhQ12Qw685+sq7wIDAQAB;s=email;t=s"#,
|
||||
include_bytes!("../test_data/dkim-privitty.eml")
|
||||
)]
|
||||
async fn test_dkim_verifier(#[case] txt: &str, #[case] message: &[u8]) {
|
||||
let verifier = DkimVerifier::mock(txt.to_string());
|
||||
verifier.verify(message, "abjadiyah.xyz").await.unwrap();
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::escaped_quotes(
|
||||
r#"v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1giTh8KDkEchWhrAB6hGnb+V87kTezkt5I3SP7BGNg8wpv0yAuj/SUmnsttYmcEU+zmNAPqxePmCNvmjLYi/c3YyWEBwHcLyZE9OlS9W4enPdsoCuEN3DayzN4JCV3MsXMedCORvLFXmIARDXDLJUSJeqCeQoudXa9GmF1CrCmx70YyTtV0xOIxEzo7z0DkUL9" "7vGmNJCv6EMpi9wccMKKu8NSmOv+DBw1MLIJqChSZMCs8CYZ5i0KT/+Lijtn6B7wyOcAuQsVL+zr7DWYrFdrePe0wGuivfJ3SvUEfUo1SIykl0nvm0iLGhjNmNa1e/tUw4ULXhQ12Qw685+sq7wIDAQAB;s=email;t=s"#,
|
||||
r#"v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1giTh8KDkEchWhrAB6hGnb+V87kTezkt5I3SP7BGNg8wpv0yAuj/SUmnsttYmcEU+zmNAPqxePmCNvmjLYi/c3YyWEBwHcLyZE9OlS9W4enPdsoCuEN3DayzN4JCV3MsXMedCORvLFXmIARDXDLJUSJeqCeQoudXa9GmF1CrCmx70YyTtV0xOIxEzo7z0DkUL97vGmNJCv6EMpi9wccMKKu8NSmOv+DBw1MLIJqChSZMCs8CYZ5i0KT/+Lijtn6B7wyOcAuQsVL+zr7DWYrFdrePe0wGuivfJ3SvUEfUo1SIykl0nvm0iLGhjNmNa1e/tUw4ULXhQ12Qw685+sq7wIDAQAB;s=email;t=s"#
|
||||
)]
|
||||
fn test_normalize_rdata(#[case] input: &str, #[case] expected: &str) {
|
||||
assert_eq!(normalize_rdata(input), expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
Received: from chat.privittytech.com (unknown [35.154.144.0])
|
||||
by nine.testrun.org (Postfix) with ESMTPS
|
||||
for <i2kaq7knq@nine.testrun.org>; Sat, 21 Feb 2026 22:17:25 +0100 (CET)
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple;
|
||||
d=chat.privittytech.com; s=opendkim; t=1771708644;
|
||||
bh=Qs0Xoe6dH8cM3t3hv45QSkQQe5TLvyJ5R4OGJL9GYUc=;
|
||||
h=Content-Type:From:To:Subject:Date:References:from:reply-to:
|
||||
subject:date:to:cc:resent-date:resent-from:resent-sender:resent-to:
|
||||
resent-cc:in-reply-to:references:list-id:list-help:
|
||||
list-unsubscribe:list-subscribe:list-post:list-owner:list-archive:
|
||||
autocrypt;
|
||||
b=rsdW71iU79CF0dooc1PnbZsJbWZsnBampamyF3jTWirF8AXBBKeR2JJsxr4km3Vx9
|
||||
QoT1s7VAyl/hdUBh2tBWT7Kzvw4KJcgkZ7tV/uRcY7edlLoH8TqATeTkWeNX18rL92
|
||||
F6Vv/6krs/z8Rpz2ZaVkBdLQif0fLQ3e96ODOdpgkLFXWsMVTM5rGepPWYht8yA+BF
|
||||
/sMopzgHhJaBvDqV42Ep75IK4w3opf9fPTAvtpwKbnDmJQmLSDv1So/Rd4xL3Oj6PX
|
||||
CSO8dfJSzQ+ukyoDqVMddVCUpgVgFHTXMdxxQVTe54tj2JPZan5ql90SHa3lNFTgN8
|
||||
hrM8BExznme5Q==
|
||||
Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
|
||||
boundary="18965f9fc8f94f5b_d3ca8c34f6c5d05_4df177c93ae6931"
|
||||
MIME-Version: 1.0
|
||||
From: <8dn75miab@chat.privittytech.com>
|
||||
To: "hidden-recipients": ;
|
||||
Subject: [...]
|
||||
Date: Mon, 16 Feb 2026 06:28:47 +0000
|
||||
Message-ID: <0737aaa0-d6f9-4240-9d94-1b73216ca96d@localhost>
|
||||
References: <0737aaa0-d6f9-4240-9d94-1b73216ca96d@localhost>
|
||||
Chat-Version: 1.0
|
||||
|
||||
|
||||
--18965f9fc8f94f5b_d3ca8c34f6c5d05_4df177c93ae6931
|
||||
Content-Type: application/pgp-encrypted; charset="utf-8"
|
||||
Content-Description: PGP/MIME version identification
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Version: 1
|
||||
|
||||
--18965f9fc8f94f5b_d3ca8c34f6c5d05_4df177c93ae6931
|
||||
Content-Type: application/octet-stream; name="encrypted.asc";
|
||||
charset="utf-8"
|
||||
Content-Description: OpenPGP encrypted message
|
||||
Content-Disposition: inline; filename="encrypted.asc";
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
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
|
||||
3aljbg==
|
||||
=wRZT
|
||||
-----END PGP MESSAGE-----
|
||||
|
||||
|
||||
--18965f9fc8f94f5b_d3ca8c34f6c5d05_4df177c93ae6931--
|
||||
|
||||
Reference in New Issue
Block a user