-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Add MTA-STS #1815
base: 3.2
Are you sure you want to change the base?
[WIP] Add MTA-STS #1815
Conversation
This commit adds a first PoC implementation of MTA-STS (RFC 8461), see also issue #1646. What works: - test a hostname which is equal to a MX record and a domainname and has a MTS-STS setup (dev.testssl.sh) - check _mta-sts TXT record + https://mta-sts.$NODE/.well-known/mta-sts.txt - check also _smtp._tls TXT record - screen output What doesn't work - test a hostname which is not equal to domainname - test a hostname which has not mx record - fileout put - any parsing of TXT record + .well-known/mta-sts.txt - when no TXT records or .well-known/mta-sts.txt are there - fileoutput - colored screen output There's a stub function for DANE. There are also two stub functions splitting HTTP body from HTTP header which I couldn't get to work and will be removed later. Besides to avoid confusion it changes from all GET requests over HTTPS tm_out to safe_echo. It's actually exactly the same only the name is different.
Fix temp diretoty for debugging
There are checks now whether testssl.sh was started with --max and whether we aim at a target which is an MX record. It has not been thoutoughly tested but works for a couple of scenarios. There were cases being identififed where this fails, see comments in the code. Also this commit addresses an error in the URL handling: for DNS queries a trailing dot is fine in the variable $NODE. For HTTP queries it is not.
* for sub.domain.tld $domain was empty * typo for checking empty variable mta_sts_record led to a missing query for some type of domains
* Imporved handling of quotes in TXT records. Previously we just stripped all quotes. Now get_txt_record includes ALL of them. sub_mta_sts() then removed the first and last double quote. (this need to be adjusted) - get_txt_record() has been tested better for every binary so that it should return always the correct string
* stripping quotes moved to get_txt_record() * fixing concatenation of errors: strings though need proper formatting * new count_char_occurence() function as a general helper func * better parsing of blanks in pattern (removed also where rfc states it's not allowed)
* failreason_mtasts_rec additions is now adding not to the first array index only * arrays for formatting error has now separators (but also at the last index) hint from https://web.archive.org/web/20101114051536/http://codesnippets.joyent.com/posts/show/1826 * replaced a couple of quoted double quotes by single quotes * replaced a couple of quoted single quotes by single quotes Unrelated to mta-sts: * HAS_DIG_NOIDNOUT was moved to places where we need dig * echo "$mx" --> safe_echo "$mx" The latter two should be backported to 3.0
|
||
tlsa_record="$(get_tlsa_record _$PORT._tcp.$NODE)" | ||
# parsing TLSA certificate usage, TLSA selector, TLSA matching type, hash | ||
rrsig_record="$(get_rrsig_record $NODE)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To determine whether DANE is securely implemented:
- Determine whether the destination domain's MX RRset is DNSSEC signed and validates. Best done by inspecting the "AD" bit from the local (loopback) DNS resolver. If the "AD" bit is on for ". IN SOA ?" and off for "root-servers.net. IN SOA ?", then your local resolver appears to support DNSSEC, and "example.com. IN MX ?" should indicate whether the domain is signed.
- For each MX host determine whether either its A or AAAA RRset is DNSSEC signed (if either is non-existent, NODATA, the denial of existence may be insecure due to NSEC3 opt-out).
- If the A or AAAA RRset of, say, "smtp.example." is obtained via CNAME expansion, and AD=0, the zone may still be signed, check for the AD bit of "smtp.example. IN CNAME ?".
- If any of the MX hosts are in unsigned zones, at best "partial DANE support" is possible, which is subject to active downgrade to the unsigned MX hosts, by blocking connections to the signed ones.
- For each MX host in a signed zone, check for TLSA RRs:
- first with the base domain set to the CNAME expansion, if AD=1 for the address records despite CNAME expansion.
- next with the base domain set to the original MX host name if no CNAME was used, was insecure, or no TLSA RRs found there.
- To check for TLSA RRs prepend "_25._tcp." to the base domain and perform a lookup with type "TLSA".
- OpenSSL
s_client
can be used to perform a DANE-validated STARTTLS session with given TLSA RRDATA, see the manpage. - Alternatively, the OpenSSL API documentation for DANE can be found at: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_dane_enable.html
- The "posttls-finger" program included with the Postfix source distribution, and in many OS Postfix distribution can perform DANE-based verification, though the output would need to be parsed to determine verification success, there's no exit code that directly reflects DANE authentication status.
- See also https://github.com/baknu/DANE-for-SMTP/wiki/2.-Implementation-resources
Hi @vdukhovni , thanks for you input! The function above was more a stub thing to signal that some thing is coming. The plan is to check for TLSA records first (as DNSSEC is more a pain) and label it as experimental. Then work on the DNSSEC part. Probably not only for SMTP. For DANE/TLSA I'll have a PoC which is not good enough to publish. I decided though not to use the bultin For DNSSEC I have no clue yet how to that but I think I have good input now. The catch here is probably the resolver which needs to be DNSSEC aware (ad), or can you think of any other way to do a proper test? |
I've seen many naive attempts to implement DANE-TLSA certificate chain validation that fail to implement all the required steps correctly. OpenSSL 1.1.0 is by now broadly available. My advice would be to not build a new validator from the ground up. You can of course do as you see fit.
Again, instead of reinventing a ground up DNSSEC implementation, keep track of evolving algorithms, keep track of a potentially changing root zone trust anchor, ... your simplest approach would in fact be to use a local validating resolver and to trust its "ad" bit. Otherwise use a library like libunbound that provides all the necessary support, but still depends on a valid trust-anchor, which requires something on the system that keeps that up to date. For me the focus would be on the end goal, determining wether a given site has a working DANE deployment. Implementing a DNSSEC validator and a DANE TLSA certificate chain validator would be left to the libraries that specialise in that sort of thing... |
... just comments and minor corrections in terminal output
... rebasing in order to kee u p with 3.1dev
This PR adds an implementation of MTA-STS (RFC 8461), see also issue #1073
Currently it's a WIP.
What doesn't work
test a hostname which is not equal to domainnametest a hostname which has no mx recordfile outputanycompliance parsing of TLS RPT TXT record + .well-known/mta-sts.txtwhen no TXT records or .well-known/mta-sts.txt are therecolored screen outputNeeded:
There's a new stub function for DANE too.
Besides: to avoid confusion I changed all GET requests over HTTPS from
tm_out
tosafe_echo
. It's actually exactly the same only the name is different but it seems more appropriate here.