Skip to content
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

Draft
wants to merge 12 commits into
base: 3.2
Choose a base branch
from
Draft

[WIP] Add MTA-STS #1815

wants to merge 12 commits into from

Conversation

drwetter
Copy link
Owner

@drwetter drwetter commented Jan 5, 2021

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 domainname
  • test a hostname which has no mx record
  • file output
  • any compliance parsing of TLS RPT TXT record + .well-known/mta-sts.txt
  • when no TXT records or .well-known/mta-sts.txt are there
  • colored screen output

Needed:

  • More testing and errors to be addressed.
  • sub.sub.domain.tld probably doesn't work
  • --mx does a check for every single MX instead of keeping the results
  • Policy delegation (CNAME?) --> Not tested yet
  • A simple unit test would be great. Anybody?

There's a new stub function for DANE too.

Besides: to avoid confusion I changed all GET requests over HTTPS from tm_out to safe_echo. It's actually exactly the same only the name is different but it seems more appropriate here.

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.
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
@drwetter drwetter changed the title [WIP] Add MTA-STS as a PoC [WIP] Add MTA-STS Jan 14, 2021
* 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)"

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

@drwetter
Copy link
Owner Author

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 -dane* functions of newer openssl, for compatibility reasons.

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?

@vdukhovni
Copy link

For DANE/TLSA I'll have a PoC which is not good enough to publish. I decided though not to use the bultin -dane* functions of newer openssl, for compatibility reasons.

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.

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?

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
@drwetter drwetter marked this pull request as draft September 22, 2021 20:03
@drwetter drwetter added the 3.3dev next release label May 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.3dev next release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants