From 2ae3dd41c6d46d80574c59eec9689c16093edf2b Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 8 Aug 2023 23:37:39 +0200 Subject: Make qmail openssl 3.0 compatible - remove support for loading custom DH params from pem. use opensslconf if really required - remove support for loading custom ec group from params - reimplement DANE support using openssl DANE functions --- qmail-remote.c | 197 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 107 insertions(+), 90 deletions(-) (limited to 'qmail-remote.c') diff --git a/qmail-remote.c b/qmail-remote.c index 0e2377e..1a09df9 100644 --- a/qmail-remote.c +++ b/qmail-remote.c @@ -63,8 +63,6 @@ struct ip_address partner; # include "tls.h" # include "ssl_timeoutio.h" # include -# include -# include # define EHLO 1 # define CLIENTCERT "control/clientcert.pem" @@ -72,10 +70,6 @@ struct ip_address partner; int tls_init(); const char *ssl_err_str = 0; char **myargv; -val_context_t *dane_context = NULL; -int dane_context_failed = 0; -struct val_danestatus *dane_status = NULL; -struct val_ssl_data *dane_ssl_data = NULL; #endif #ifdef SMTPUTF8 @@ -241,10 +235,6 @@ unsigned long ehlo() if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len; ehlokw.len = 0; -# ifdef MXPS - if (type == 's') return 0; -# endif - substdio_puts(&smtpto, "EHLO "); substdio_put(&smtpto, helohost.s, helohost.len); substdio_puts(&smtpto, "\r\n"); @@ -437,34 +427,28 @@ int tls_init() } } - /* DANE: starts here */ - int dane_retval = VAL_DANE_INTERNAL_ERROR; + /* DANE: lookup TLSA records */ int tls_required = (smtps || servercert != NULL); - - if (partner_fqdn && !servercert && !dane_context_failed) { - if (dane_context == NULL && val_create_context(NULL, &dane_context) - != VAL_NO_ERROR) { - dane_context_failed = 1; - out("lUnable to initialize libval context\n"); - zeroflush(); + stralloc tlsa_rr = { 0 }; + + if (partner_fqdn && !servercert) { + stralloc tlsa_label = { 0 }; + char port[FMT_ULONG]; + + if (!stralloc_copyb(&tlsa_label, "_", 1)) temp_nomem(); + 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(); + + switch (dns_tlsa(&tlsa_rr, &tlsa_label)) { + case DNS_MEM: temp_nomem(); + case DNS_SOFT: temp_dns(); + case DNS_HARD: tlsa_rr.len = 0; // no record found } - if (dane_context) { - val_free_dane(dane_status); - val_free_dane_ssl(dane_ssl_data); - - /* DANE: lookup TLSA records */ - struct val_daneparams dane_params = { - .port = smtp_port, - .proto = DANE_PARAM_PROTO_TCP - }; - dane_retval = val_getdaneinfo(dane_context, partner_fqdn, &dane_params, &dane_status); - /* work around a bug if no error but no also no records */ - if (dane_retval == VAL_DANE_NOERROR && dane_status == NULL) - dane_retval = VAL_DANE_IGNORE_TLSA; - if (dane_retval == VAL_DANE_NOERROR) - tls_required = 1; - } + if (tlsa_rr.len) + tls_required = 1; } if (!smtps) { @@ -480,13 +464,20 @@ int tls_init() } SSL_library_init(); - ctx = SSL_CTX_new(SSLv23_client_method()); + ctx = SSL_CTX_new(TLS_client_method()); if (!ctx) { - if (!tls_required) return 0; smtptext.len = 0; tls_quit_error("ZTLS error initializing ctx"); } - SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); + SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION); + + /* we verify ourself below. see SSL_get_verify_result */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + + if (SSL_CTX_dane_enable(ctx) <= 0) { + smtptext.len = 0; + tls_quit_error("ZTLS error initializing dane ctx"); + } if (servercert) { if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) { @@ -497,19 +488,6 @@ int tls_init() /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); } - else if (dane_retval == VAL_DANE_NOERROR) { - /* DANE: treat PKIX-EE as it were DANE-EE */ - struct val_danestatus *dane_cur; - for (dane_cur = dane_status; dane_cur; dane_cur = dane_cur->next) { - if (dane_cur->usage == DANE_USE_SVC_CONSTRAINT) - dane_cur->usage = DANE_USE_DOMAIN_ISSUED; - } - /* DANE: set verify callback */ - int err = val_enable_dane_ssl(dane_context, ctx, partner_fqdn, dane_status, - &dane_ssl_data); - if (err != VAL_NO_ERROR) - tls_quit("ZCould not enable dane verification", p_val_error(err)); - } /* let the other side complain if it needs a cert and we don't have one */ if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT)) @@ -518,14 +496,10 @@ int tls_init() myssl = SSL_new(ctx); SSL_CTX_free(ctx); if (!myssl) { - if (!tls_required) return 0; smtptext.len = 0; tls_quit_error("ZTLS error initializing ssl"); } - if (!smtps) - substdio_putsflush(&smtpto, "STARTTLS\r\n"); - /* while the server is preparing a responce, do something else */ if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1) { SSL_free(myssl); temp_control(); } @@ -539,14 +513,15 @@ int tls_init() SSL_set_cipher_list(myssl, ciphers); alloc_free(saciphers.s); -#if OPENSSL_VERSION_NUMBER >= 0x10100005L stralloc opensslconf = {0}; if (control_readfile(&opensslconf, "control/opensslconf", 0) == -1) { SSL_free(myssl); temp_control(); } if (opensslconf.len) { SSL_CONF_CTX *cctx = SSL_CONF_CTX_new(); SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); + /* client + server so we can share one single file */ SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE); SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SHOW_ERRORS); SSL_CONF_CTX_set_ssl(cctx, myssl); @@ -572,13 +547,68 @@ int tls_init() (void)SSL_CONF_CTX_finish(cctx); } -#endif /* set SNI hostname */ if (partner_fqdn) SSL_set_tlsext_host_name(myssl, partner_fqdn); - /* SSL_set_options(myssl, SSL_OP_NO_TLSv1); */ + /* DANE: enable + add records */ + if (tlsa_rr.len) { + if (SSL_dane_enable(myssl, partner_fqdn) <= 0) { + smtptext.len = 0; + tls_quit_error("ZTLS error enabling dane"); + } + SSL_dane_set_flags(myssl, DANE_FLAG_NO_DANE_EE_NAMECHECKS); + + // loop through TLSA rr + int pos = 0, num_usable = 0; + unsigned char *rr_data = (unsigned char *)tlsa_rr.s; + while (pos < tlsa_rr.len && tlsa_rr.len - pos > 2 + 4) // sizeof(rrlen) + min(rrdata) + { + unsigned short rrlen = (rr_data[pos] << 8) + rr_data[pos + 1]; + pos += 2; + uint8_t usage = rr_data[pos]; + uint8_t selector = rr_data[pos + 1]; + uint8_t mtype = rr_data[pos + 2]; + + /* + * Opportunistic DANE TLS clients support only DANE-TA(2) or DANE-EE(3). + * They treat all other certificate usages, and in particular PKIX-TA(0) + * and PKIX-EE(1), as unusable. + */ + switch (usage) { + default: + case 0: /* PKIX-TA(0) */ + case 1: /* PKIX-EE(1) */ + pos += rrlen; + continue; + case 2: /* DANE-TA(2) */ + case 3: /* DANE-EE(3) */ + break; + } + + // rrlen includes usage+selector+mtype (3) byte. remove + unsigned const char *cdata = rr_data + pos + 3; + size_t cdlen = rrlen - 3; + int tlsa_added = SSL_dane_tlsa_add(myssl, usage, selector, mtype, cdata, cdlen); + if (tlsa_added < 0) { + smtptext.len = 0; + tls_quit_error("ZTLS error adding DANE TLSA record"); + } + else if (tlsa_added > 0) + ++num_usable; + + pos += rrlen; + } + + // no records usable. fallback to unauthenticated TLS + if (num_usable == 0) + tlsa_rr.len = 0; + } + + if (!smtps) + substdio_putsflush(&smtpto, "STARTTLS\r\n"); + SSL_set_fd(myssl, smtpfd); /* read the responce to STARTTLS */ @@ -593,7 +623,7 @@ int tls_init() } ssl = myssl; - if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0) { + if (ssl_timeoutconn(timeout, smtpfd, smtpfd, myssl) <= 0) { if (tls_required) tls_quit("ZTLS connect failed", ssl_error_str()); else { @@ -617,14 +647,14 @@ int tls_init() X509 *peercert; STACK_OF(GENERAL_NAME) *gens; - int r = SSL_get_verify_result(ssl); + int r = SSL_get_verify_result(myssl); if (r != X509_V_OK) { out("ZTLS unable to verify server with "); tls_quit(servercert, X509_verify_cert_error_string(r)); } alloc_free(servercert); - peercert = SSL_get_peer_certificate(ssl); + peercert = SSL_get_peer_certificate(myssl); if (!peercert) { out("ZTLS unable to verify server "); tls_quit(partner_fqdn, "no certificate provided"); @@ -651,10 +681,7 @@ int tls_init() if (i >= 0) { X509_NAME_ENTRY *entry = X509_NAME_get_entry(subj, i); ASN1_STRING *s = X509_NAME_ENTRY_get_data(entry); -#if OPENSSL_VERSION_NUMBER < 0x10100005L -#define ASN1_STRING_get0_data ASN1_STRING_data -#endif - if (s) { peer.len = ASN1_STRING_length(s); peer.s = (unsigned char *)ASN1_STRING_get0_data(s); } + if (s) { peer.len = ASN1_STRING_length(s); peer.s = (char *)ASN1_STRING_get0_data(s); } } if (peer.len <= 0) { out("ZTLS unable to verify server "); @@ -669,8 +696,8 @@ int tls_init() X509_free(peercert); } /* DANE: verify result */ - else if (dane_retval == VAL_DANE_NOERROR) { - if (!SSL_get_peer_certificate(ssl) || SSL_get_verify_result(ssl) != X509_V_OK) { + 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); quit2("/", NULL, 0); @@ -746,33 +773,22 @@ void mail_without_auth() void smtp() { - unsigned long code; + unsigned long code = 0; int flagbother; - int i, j; - -#ifndef PORT_SMTP - /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */ -# define port smtp_port -#endif #ifdef TLS -# ifdef MXPS - if (type == 'S') smtps = 1; - else if (type != 's') -# endif - if (port == 465) smtps = 1; - if (!smtps) + if (smtp_port == 465) smtps = 1; + if (!smtps) { #endif - code = smtpcode(); if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed"); if (code >= 400 && code < 500) return; /* try next MX, see RFC-2821 */ if (code != 220) quit("ZConnected to "," but greeting failed"); + code = ehlo(); #ifdef TLS - if (!smtps) + } #endif - code = ehlo(); #ifdef TLS int tls = tls_init(); @@ -782,7 +798,7 @@ void smtp() ssl = NULL; return; /* try next MX */ } - else if (tls) + else if (tls > 0) /* RFC2487 says we should issue EHLO (even if we might not need * extensions); at the same time, it does not prohibit a server * to reject the EHLO and make us fallback to HELO */ @@ -803,7 +819,7 @@ void smtp() if (code != 250) quit("ZConnected to "," but my name was rejected"); } - i = 0; + int i = 0; if (auth_smtp_user.len && auth_smtp_pass.len) { while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+8 < smtptext.len) && @@ -854,7 +870,7 @@ void smtp() if (code >= 400) quit("ZConnected to "," but sender was rejected"); flagbother = 0; - for (i = 0;i < reciplist.len;++i) { + for (int i = 0;i < reciplist.len;++i) { substdio_puts(&smtpto,"RCPT TO:<"); substdio_put(&smtpto,reciplist.sa[i].s,reciplist.sa[i].len); substdio_puts(&smtpto,">\r\n"); @@ -907,7 +923,6 @@ void qmtp() { struct stat st; unsigned long len; - int len2; char *x; int i; int n; @@ -1039,9 +1054,11 @@ void getcontrols() case -1: temp_control(); case 0: - if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break; + if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); + break; case 1: - if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; + if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); + break; } } @@ -1074,7 +1091,7 @@ char **argv; relayhost = 0; for (i = 0;i <= host.len;++i) if ((i == 0) || (i == host.len) || (host.s[i] == '.')) - if (relayhost = constmap(&maproutes,host.s + i,host.len - i)) + if ((relayhost = constmap(&maproutes,host.s + i,host.len - i))) break; if (relayhost && !*relayhost) relayhost = 0; -- cgit v1.2.3