From f3696facb188bda5b9995bb8cf50e9bac37e4f29 Mon Sep 17 00:00:00 2001 From: manuel Date: Thu, 10 Aug 2023 01:10:33 +0200 Subject: DANE: only enabled if MX lookup has been validated additionally require TLSA RRs to be validated as well --- qmail-remote.c | 51 +++++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/qmail-remote.c b/qmail-remote.c index c353bec..edfdb60 100644 --- a/qmail-remote.c +++ b/qmail-remote.c @@ -67,7 +67,6 @@ struct ip_address partner; # define EHLO 1 # define CLIENTCERT "control/clientcert.pem" -int tls_init(); const char *ssl_err_str = 0; char **myargv; #endif @@ -368,8 +367,6 @@ void blast() } #ifdef TLS -char *partner_fqdn = 0; - # define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", NULL) void tls_quit(const char *s1, const char *s2) { @@ -377,12 +374,12 @@ void tls_quit(const char *s1, const char *s2) } # define tls_quit_error(s) tls_quit(s, ssl_error()) -int match_partner(const char *s, int len) +int match_mx_host(const char *mx_host, const char *s, int len) { - if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1; + if (!case_diffb(mx_host, len, s) && !mx_host[len]) return 1; /* we also match if the name is *.domainname */ if (*s == '*') { - const char *domain = partner_fqdn + str_chr(partner_fqdn, '.'); + const char *domain = mx_host + str_chr(mx_host, '.'); if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1; } return 0; @@ -396,25 +393,26 @@ int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } * @return 0 ...TLS failed, continue plaintext * @return -1 ...skip to next MX */ -int tls_init() +static int tls_init(struct ip_mx *current_mx) { int i; SSL *myssl; SSL_CTX *ctx; stralloc saciphers = {0}; const char *ciphers, *servercert = 0; + const char *mx_host = current_mx->fqdn; - if (partner_fqdn) { + if (mx_host) { struct stat st; stralloc tmp = {0}; if (!stralloc_copys(&tmp, "control/tlshosts/") - || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)) + || !stralloc_catb(&tmp, mx_host, str_len(mx_host)) || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem(); if (stat(tmp.s, &st) == 0) servercert = tmp.s; else { if (!stralloc_copys(&tmp, "control/notlshosts/") - || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1)) + || !stralloc_catb(&tmp, mx_host, str_len(mx_host)+1)) temp_nomem(); if ((stat("control/tlshosts/exhaustivelist", &st) == 0) || (stat(tmp.s, &st) == 0)) { @@ -431,7 +429,7 @@ int tls_init() int tls_required = (smtps || servercert != NULL); stralloc tlsa_rr = { 0 }; - if (partner_fqdn && !servercert) { + if (mx_host && !servercert && current_mx->validated) { stralloc tlsa_label = { 0 }; char port[FMT_ULONG]; @@ -439,7 +437,7 @@ int tls_init() port[fmt_ulong(port, smtp_port)] = 0; if (!stralloc_cats(&tlsa_label, port)) temp_nomem(); if (!stralloc_cats(&tlsa_label, "._tcp.")) temp_nomem(); - if (!stralloc_cats(&tlsa_label, partner_fqdn)) temp_nomem(); + if (!stralloc_cats(&tlsa_label, mx_host)) temp_nomem(); switch (dns_tlsa(&tlsa_rr, &tlsa_label)) { case DNS_MEM: temp_nomem(); @@ -447,7 +445,7 @@ int tls_init() case DNS_HARD: tlsa_rr.len = 0; // no record found } - if (tlsa_rr.len) + if (tlsa_rr.len && dns_last_query_validated()) tls_required = 1; } @@ -549,12 +547,12 @@ int tls_init() } /* set SNI hostname */ - if (partner_fqdn) - SSL_set_tlsext_host_name(myssl, partner_fqdn); + if (mx_host) + SSL_set_tlsext_host_name(myssl, mx_host); /* DANE: enable + add records */ if (tlsa_rr.len) { - if (SSL_dane_enable(myssl, partner_fqdn) <= 0) { + if (SSL_dane_enable(myssl, mx_host) <= 0) { smtptext.len = 0; tls_quit_error("ZTLS error enabling dane"); } @@ -657,7 +655,7 @@ int tls_init() peercert = SSL_get_peer_certificate(myssl); if (!peercert) { out("ZTLS unable to verify server "); - tls_quit(partner_fqdn, "no certificate provided"); + tls_quit(mx_host, "no certificate provided"); } /* RFC 2595 section 2.4: find a matching name @@ -668,7 +666,7 @@ int tls_init() { const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); if (gn->type == GEN_DNS) - if (match_partner(gn->d.ia5->data, gn->d.ia5->length)) break; + if (match_mx_host(mx_host, gn->d.ia5->data, gn->d.ia5->length)) break; } sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); } @@ -685,10 +683,10 @@ int tls_init() } if (peer.len <= 0) { out("ZTLS unable to verify server "); - tls_quit(partner_fqdn, "certificate contains no valid commonName"); + tls_quit(mx_host, "certificate contains no valid commonName"); } - if (!match_partner(peer.s, peer.len)) { - out("ZTLS unable to verify server "); out(partner_fqdn); + if (!match_mx_host(mx_host, peer.s, peer.len)) { + out("ZTLS unable to verify server "); out(mx_host); out(": received certificate for "); outsafe(&peer); TLS_QUIT; } } @@ -699,7 +697,7 @@ int tls_init() else if (tlsa_rr.len) { if (!SSL_get_peer_certificate(myssl) || SSL_get_verify_result(myssl) != X509_V_OK) { out("lNo TLSA record matched: "); - out(partner_fqdn); + out(mx_host); quit2("/", NULL, 0); return -1; } @@ -771,7 +769,7 @@ void mail_without_auth() substdio_flush(&smtpto); } -void smtp() +static void smtp(struct ip_mx *current_mx) { unsigned long code = 0; int flagbother; @@ -791,7 +789,7 @@ void smtp() #endif #ifdef TLS - int tls = tls_init(); + int tls = tls_init(current_mx); if (tls == -1) { if (ssl) ssl_free(ssl); @@ -1194,10 +1192,7 @@ char **argv; if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) smtp_port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; -#ifdef TLS - partner_fqdn = ip.ix[i].fqdn; -#endif - smtp(); /* only returns when the next MX is to be tried */ + smtp(&ip.ix[i]); /* only returns when the next MX is to be tried */ } tcpto_err(&ip.ix[i].ip,errno == error_timeout || errno == error_refused); close(smtpfd); -- cgit v1.2.3