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 --- Makefile | 2 +- qmail-remote.c | 197 ++++++++++++++++++++++++++++------------------------ qmail-smtpd.c | 213 ++++++--------------------------------------------------- 3 files changed, 130 insertions(+), 282 deletions(-) diff --git a/Makefile b/Makefile index 5f0c0f6..5bd3765 100644 --- a/Makefile +++ b/Makefile @@ -1533,7 +1533,7 @@ dns.lib socket.lib ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ env.a str.a fs.a auto_qmail.o base64.o \ - `cat dns.lib` `cat socket.lib` -lval -lsres -lidn2 + `cat dns.lib` `cat socket.lib` -lidn2 qmail-remote.0: \ qmail-remote.8 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; diff --git a/qmail-smtpd.c b/qmail-smtpd.c index 5ea23dc..0d3b16d 100644 --- a/qmail-smtpd.c +++ b/qmail-smtpd.c @@ -67,8 +67,6 @@ static const stralloc *client_get_session_id(); #ifdef TLS # include -# include -# include # include "tls.h" # include "ssl_timeoutio.h" @@ -208,7 +206,7 @@ int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\ void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); do_hard_errors(); } int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); do_hard_errors(); return -1; } -int err_noauth2() { out("503 auth not available (#5.3.3)\r\n"); do_hard_errors(); } +int err_noauth2() { out("503 auth not available (#5.3.3)\r\n"); do_hard_errors(); return -1; } int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; } int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } int err_wantstarttls() { out("530 Must issue a STARTTLS command first (#5.7.0)\r\n"); return -1; }; @@ -386,7 +384,7 @@ char *arg; if (!stralloc_copys(&addr,"")) die_nomem(); flagesc = 0; flagquoted = 0; - for (i = 0;ch = arg[i];++i) { /* copy arg to addr, stripping quotes */ + for (i = 0; (ch = arg[i]); ++i) { /* copy arg to addr, stripping quotes */ if (flagesc) { if (!stralloc_append(&addr,&ch)) die_nomem(); flagesc = 0; @@ -485,7 +483,7 @@ int bmcheck(which) int which; } if ((negate) && (x == 0)) { if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem(); - if (!stralloc_0(&matchedregex)) die_nomem(); + if (!stralloc_0(&matchedregex)) die_nomem(); return 1; } if (!(negate) && (x > 0)) { @@ -582,8 +580,6 @@ void mailfrom_auth(arg,len) char *arg; int len; { - int j; - if (!stralloc_copys(&fuser,"")) die_nomem(); if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } else @@ -610,7 +606,7 @@ void mailfrom_parms(arg) char *arg; int len; len = str_len(arg); - if (!stralloc_copys(&mfparms,"")) die_nomem; + if (!stralloc_copys(&mfparms,"")) die_nomem(); i = byte_chr(arg,len,'>'); if (i > 4 && i < len) { while (len) { @@ -622,10 +618,10 @@ void mailfrom_parms(arg) char *arg; if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } /* we do not support this */ //if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); - if (!stralloc_copys(&mfparms,"")) die_nomem; + if (!stralloc_copys(&mfparms,"")) die_nomem(); } else - if (!stralloc_catb(&mfparms,arg,1)) die_nomem; + if (!stralloc_catb(&mfparms,arg,1)) die_nomem(); } } } @@ -1094,17 +1090,17 @@ int authenticate(void) if (authout.len > 0) { len = authout.len; arg = authout.s; - if (!stralloc_copys(&authparams, "")) die_nomem; + if (!stralloc_copys(&authparams, "")) die_nomem(); while (len) { if (*arg == '\0') { if (case_starts(authparams.s, "USER=") && (authparams.len - 5) > 0) { if (!stralloc_copyb(&user, authparams.s + 5, authparams.len - 5)) die_nomem(); if (!stralloc_0(&user)) die_nomem(); } - if (!stralloc_copys(&authparams, "")) die_nomem; + if (!stralloc_copys(&authparams, "")) die_nomem(); } else - if (!stralloc_catb(&authparams, arg, 1)) die_nomem; + if (!stralloc_catb(&authparams, arg, 1)) die_nomem(); arg++; len--; } @@ -1130,19 +1126,19 @@ int auth_login(arg) char *arg; if (tls_before_auth && !ssl) return err_wantstarttls(); #endif if (*arg) { - if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); + if ((r = b64decode(arg,str_len(arg),&user)) == 1) return err_input(); } else { out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ if (authgetl() < 0) return -1; - if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); + if ((r = b64decode(authin.s,authin.len,&user)) == 1) return err_input(); } if (r == -1) die_nomem(); out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ if (authgetl() < 0) return -1; - if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); + if ((r = b64decode(authin.s,authin.len,&pass)) == 1) return err_input(); if (r == -1) die_nomem(); if (!user.len || !pass.len) return err_input(); @@ -1157,12 +1153,12 @@ int auth_plain(arg) char *arg; if (tls_before_auth && !ssl) return err_wantstarttls(); #endif if (*arg) { - if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input(); + if ((r = b64decode(arg,str_len(arg),&resp)) == 1) return err_input(); } else { out("334 \r\n"); flush(); if (authgetl() < 0) return -1; - if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + if ((r = b64decode(authin.s,authin.len,&resp)) == 1) return err_input(); } if (r == -1 || !stralloc_0(&resp)) die_nomem(); while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ @@ -1297,140 +1293,6 @@ void smtp_tls(char *arg) else tls_init(); } -/* - * Grab well-defined DH parameters from OpenSSL, see the get_rfc* - * functions in for all available primes. - */ -#if OPENSSL_VERSION_NUMBER < 0x10100005L -static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - /* q is optional */ - if (p == NULL || g == NULL) - return 0; - BN_free(dh->p); - BN_free(dh->q); - BN_free(dh->g); - dh->p = p; - dh->q = q; - dh->g = g; - - if (q != NULL) - dh->length = BN_num_bits(q); - - return 1; -} -#endif - -static DH *make_dh_params(BIGNUM *(*prime)(BIGNUM *)) -{ - BIGNUM *p, *g; - DH *dh = DH_new(); - if (!dh) - return NULL; - - p = prime(NULL); - g = BN_new(); - if (g != NULL) - BN_set_word(g, 2); - if (!p || !g || !DH_set0_pqg(dh, p, NULL, g)) { - DH_free(dh); - BN_free(p); - BN_free(g); - return NULL; - } - return dh; -} - -/* Storage and initialization for DH parameters. */ -static struct dhparam { - BIGNUM *(*const prime)(BIGNUM *); /* function to generate... */ - DH *dh; /* ...this, used for keys.... */ - const unsigned int min; /* ...of length >= this. */ -} dhparams[] = { - { get_rfc3526_prime_8192, NULL, 6145 }, - { get_rfc3526_prime_6144, NULL, 4097 }, - { get_rfc3526_prime_4096, NULL, 3073 }, - { get_rfc3526_prime_3072, NULL, 2049 }, - { get_rfc3526_prime_2048, NULL, 1025 }, - { get_rfc2409_prime_1024, NULL, 0 } -}; - -/* Hand out the same DH structure though once generated as we leak - * memory otherwise and freeing the structure up after use would be - * hard to track and in fact is not needed at all as it is safe to - * use the same parameters over and over again security wise (in - * contrast to the keys itself) and code safe as the returned structure - * is duplicated by OpenSSL anyway. Hence no modification happens - * to our copy. */ -static DH *tmp_dh_cb(SSL *ssl, int export, int keylen) -{ - EVP_PKEY *pkey = SSL_get_privatekey(ssl); - int type = pkey ? EVP_PKEY_base_id(pkey) : EVP_PKEY_NONE; - unsigned n; - - /* - * OpenSSL will call us with either keylen == 512 or keylen == 1024 - * (see the definition of SSL_EXPORT_PKEYLENGTH in ssl_locl.h). - * Adjust the DH parameter length according to the size of the - * RSA/DSA private key used for the current connection, and always - * use at least 1024-bit parameters. - * Note: This may cause interoperability issues with implementations - * which limit their DH support to 1024 bit - e.g. Java 7 and earlier. - * In this case, SSLCertificateFile can be used to specify fixed - * 1024-bit DH parameters (with the effect that OpenSSL skips this - * callback). - */ - if (type == EVP_PKEY_RSA || type == EVP_PKEY_DSA) - keylen = EVP_PKEY_bits(pkey); - - for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++) { - if (keylen >= dhparams[n].min) { - if (dhparams[n].dh == NULL) - dhparams[n].dh = make_dh_params(dhparams[n].prime); - return dhparams[n].dh; - } - } - - return NULL; /* impossible to reach. */ -} - -static DH *ssl_dh_GetParamFromFile(const char *file) -{ - DH *dh = NULL; - BIO *bio; - - if ((bio = BIO_new_file(file, "r")) == NULL) - return NULL; - dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - BIO_free(bio); - if (!dh) - (void)ERR_get_error(); - return dh; -} - -#ifdef HAVE_ECC -static EC_GROUP *ssl_ec_GetParamFromFile(const char *file) -{ - EC_GROUP *group = NULL; - BIO *bio; - - if ((bio = BIO_new_file(file, "r")) == NULL) - return NULL; - group = PEM_read_bio_ECPKParameters(bio, NULL, NULL, NULL); - BIO_free(bio); - if (!group) - (void)ERR_get_error(); - return group; -} - -#if !defined(SSL_set_ecdh_auto) -static EC_KEY *tmp_ecdh_cb(SSL *ssl, int export, int keylen) -{ - return EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); -} -#endif -#endif - /* don't want to fail handshake if cert isn't verifiable */ int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } @@ -1455,15 +1317,7 @@ void tls_init() SSL_CTX *ctx; const char *ciphers; stralloc saciphers = {0}; - X509_STORE *store; - X509_LOOKUP *lookup; const char *servercert, *servercert2; - DH *dhparams; -#ifdef HAVE_ECC - EC_GROUP *ecparams; - int nid; - EC_KEY *eckey = NULL; -#endif struct stat st; /* if set, use servercert selected through SMTP_SERVERCERT env var */ @@ -1475,7 +1329,7 @@ void tls_init() SSL_library_init(); /* a new SSL context with the bare minimum of options */ - ctx = SSL_CTX_new(SSLv23_server_method()); + ctx = SSL_CTX_new(TLS_server_method()); if (!ctx) { tls_err("unable to initialize ctx"); return; } int min_ssl_version = (*childargs) ? TLS1_2_VERSION : TLS1_VERSION; SSL_CTX_set_min_proto_version(ctx, min_ssl_version); @@ -1490,13 +1344,13 @@ void tls_init() /* this will also check whether public and private keys match */ if (!SSL_CTX_use_PrivateKey_file(ctx, servercert, SSL_FILETYPE_PEM)) - { SSL_free(myssl); tls_err("no valid private key"); return; } + { SSL_CTX_free(ctx); tls_err("no valid private key"); return; } if (stat(servercert2, &st) == 0) { if (!SSL_CTX_use_certificate_chain_file(ctx, servercert2)) - { SSL_CTX_free(ctx); tls_err("missing alternate certificate"); return; } + { SSL_CTX_free(ctx); tls_err("invalid alternate certificate"); return; } if (!SSL_CTX_use_PrivateKey_file(ctx, servercert2, SSL_FILETYPE_PEM)) - { SSL_free(myssl); tls_err("no valid alternate private key"); return; } + { SSL_CTX_free(ctx); tls_err("no valid alternate private key"); return; } } /* a new SSL object, with the rest added to it directly to avoid copying */ @@ -1519,39 +1373,17 @@ void tls_init() SSL_set_cipher_list(myssl, ciphers); alloc_free(saciphers.s); - //TODO: we shouldn't use hardcoded DH: see https://weakdh.org/ - SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); - - /* try to read DH parameters from certificate */ - if ((dhparams = ssl_dh_GetParamFromFile(servercert))) - SSL_set_tmp_dh(myssl, dhparams); - -#ifdef HAVE_ECC - /* similarly, try to read the ECDH curve name from certificate... */ - if ((ecparams = ssl_ec_GetParamFromFile(servercert)) && - (nid = EC_GROUP_get_curve_name(ecparams)) && - (eckey = EC_KEY_new_by_curve_name(nid))) { - SSL_set_tmp_ecdh(myssl, eckey); - } - else { -#if defined(SSL_set_ecdh_auto) - /* ...otherwise, enable auto curve selection (OpenSSL 1.0.2 and later) */ - SSL_set_ecdh_auto(myssl, 1); -#else - /* or set callback, which will configure NIST P-256 */ - SSL_set_tmp_ecdh_callback(myssl, tmp_ecdh_cb); -#endif - } - EC_KEY_free(eckey); -#endif + SSL_set_dh_auto(myssl, 1); + SSL_set_ecdh_auto(myssl, 1); -#if OPENSSL_VERSION_NUMBER >= 0x10100005L stralloc opensslconf = {0}; if (control_readfile(&opensslconf, "control/opensslconf", 0) == -1) { SSL_free(myssl); die_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); @@ -1579,7 +1411,6 @@ void tls_init() (void)SSL_CONF_CTX_finish(cctx); } -#endif SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); -- cgit v1.2.3