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-smtpd.c | 213 ++++++---------------------------------------------------- 1 file changed, 22 insertions(+), 191 deletions(-) (limited to 'qmail-smtpd.c') 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