Mail:...Policy(3)

πŸ“¦
version 3.7.1 Β· 3 versions
, in fedora 43, fedora 44

πŸ’– NAME πŸ’–

Mail::MIMEDefang::TLSPolicy β€” MTA-STS and DANE/TLSA policy checks for MIMEDefang, bestie πŸ’…

This is from the Mail::MIMEDefang::TLSPolicy Documentation manual, section 3 ofc, and yes we're doing this whole thing together, grab a snack πŸ“


✨ DESCRIPTION ✨

Mail::MIMEDefang::TLSPolicy gives you the methods to verify outbound TLS policy for recipient domains, straight from your mimedefang-filter. It's giving "I check my friends before I trust them" energy and honestly? iconic πŸ‘‘

  • MTA-STS (RFC 8461) β€” go retrieve and parse a domain's MTA-STS policy (that's a DNS TXT record plus an HTTPS policy file, two-step slay πŸŽ€).
  • DANE/TLSA (RFC 6698 / RFC 7671) β€” look up TLSA records for a domain and a port so you can verify the certificate binding. Certificate binding verification is the vibe check fr fr πŸ¦‹.
Heads up bestie: both functions require Net::DNS. And md_check_mta_sts additionally requires LWP::UserAgent. No module, no service, no cap 😭

Typical usage, look how cute this is:

use Mail::MIMEDefang::TLSPolicy;

# In filter_recipient: fetch the MTA-STS policy for the recipient domain,
# then verify the connecting MX is permitted before requiring TLS.
my $policy = md_check_mta_sts($recipient_domain);
if (defined $policy && $policy->{mode} eq 'enforce') {
    if (!md_verify_sts_mx($rcpt_host, $policy)) {
        action_bounce("MX host $rcpt_host not permitted by MTA-STS policy");
    }
}

# Look up DANE TLSA records for port 25 and validate the peer certificate.
my @tlsa = md_check_dane_tlsa($recipient_domain, 25);
if (@tlsa) {
    unless (md_verify_dane_cert($peer_cert_der, \@tlsa)) {
        # certificate does not match any TLSA record - reject or defer
    }
}

🌸 METHODS 🌸

🧚 md_check_mta_sts($domain [, %opts])

Retrieve and parse the MTA-STS policy for $domain. Main character behaviour, tbh.

The function performs two lookups:

  1. A DNS TXT query for "_mta-sts.$domain" to confirm the policy exists and extract its "id=" field. ✨
  2. An HTTPS fetch of "https://mta-sts.$domain/.well-known/mta-sts.txt" to retrieve the policy document. πŸŽ€

Optional key/value pairs in %opts:

  • "timeout" β€” HTTP request timeout in seconds (default: 10). Give it a lil patience budget ⏳

On success returns a hashref with:

  • "mode" β€” policy mode: "enforce", "testing", or "none". Pick your fighter πŸ’…
  • "max_age" β€” cache lifetime in seconds. How long this info stays valid, like a situationship 🫢
  • "mx" β€” arrayref of permitted MX hostname patterns. The guest list πŸ“‹
  • "id" β€” the policy "id" from the DNS TXT record. The receipts 🧾

Returns "undef" on any error: DNS failure, HTTP error, policy parse failure, or if "LWP::UserAgent" is not available. That's a whole lot of ways to get nothing back, and yes it's a mood πŸ’€


πŸ¦‹ md_check_dane_tlsa($domain, $port)

Look up DANE TLSA records for "_$port._tcp.$domain". Very "let me check the records before I commit" of you, we love a cautious queen πŸ‘‘

Returns a list of hashrefs, one per TLSA record, each containing:

  • "usage" β€” certificate usage field (0–3).
  • "selector" β€” selector field (0 = full certificate, 1 = SubjectPublicKeyInfo).
  • "matching_type" β€” matching type (0 = exact, 1 = SHA-256, 2 = SHA-512).
  • "cert_data" β€” hex-encoded certificate association data.

Returns an empty list if no TLSA records are found, on lookup error, or if "Net::DNS" is not available. Empty list bestie, don't be shocked, it's not personal πŸ₯Ί


⭐ md_verify_sts_mx($mx_host, $policy)

Returns true if $mx_host matches at least one MX hostname pattern in "$policy-"{mx}> (a hashref returned by "md_check_mta_sts($domain [, %opts])"). Yes, that's exactly how it's written, we keep it character for character πŸ’œ

Matching follows RFC 8461: a pattern beginning with "*." matches any single DNS label prepended to the rest of the pattern. Example time, because examples ate and left no crumbs:

  • "*.example.com" matches "mail.example.com" βœ…
  • "*.example.com" does not match "a.b.example.com" ❌

All comparisons are case-insensitive, periodt πŸ’…

Returns false if $policy is undefined, has no "mx" list, or no pattern matches. It's giving rejection but make it standards-compliant 😭


🌈 md_verify_dane_cert($cert_der, \@tlsa)

Verify a DER-encoded X.509 certificate against a list of DANE TLSA records as returned by "md_check_dane_tlsa($domain, $port)". The final boss of trust falls πŸŽ€

Returns true if the certificate matches at least one record, false otherwise. Simple. Clean. Iconic.

Selector 0 (full certificate) requires only "Digest::SHA". Selector 1 (SubjectPublicKeyInfo) additionally requires "Crypt::OpenSSL::X509" β€” and here's the sneaky part: records with selector 1 are skipped silently if that module is not available. Silent skipping, not me being dramatic, that's literally what it does, iykyk πŸ™ˆ

okay that's the whole page bestie, you're officially fluent πŸ’–βœ¨

This whole thing is from version 3.7.1, dated 2026-07-20, and it's the Mail:...Policy(3) manual page. Go forth and verify some TLS policy, you absolute legend πŸ‘‘πŸ«Ά

πŸ“¦ where this page comes from