From 15a5232629a37b3df8a476f984343375950fa2ff Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 22 Jun 2015 21:28:22 +0200 Subject: Use DH parameters from OpenSSL and remove support for ephemeral RSA This adds support for DH parameters from 1024 to 8192 bits. --- qmail-smtpd.c | 92 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 28 deletions(-) (limited to 'qmail-smtpd.c') diff --git a/qmail-smtpd.c b/qmail-smtpd.c index 3360cfe..817524d 100644 --- a/qmail-smtpd.c +++ b/qmail-smtpd.c @@ -1141,41 +1141,78 @@ void smtp_tls(char *arg) else tls_init(); } -RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen) +/* + * Grab well-defined DH parameters from OpenSSL, see the get_rfc* + * functions in for all available primes. + */ +static DH *make_dh_params(BIGNUM *(*prime)(BIGNUM *), const char *gen) { - if (!export) keylen = 512; - if (keylen == 512) { - FILE *in = fopen("control/rsa512.pem", "r"); - if (in) { - RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL); - fclose(in); - if (rsa) return rsa; + DH *dh = DH_new(); + + if (!dh) { + return NULL; } - } - return RSA_generate_key(keylen, RSA_F4, NULL, NULL); + dh->p = prime(NULL); + BN_dec2bn(&dh->g, gen); + if (!dh->p || !dh->g) { + DH_free(dh); + 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. */ DH *tmp_dh_cb(SSL *ssl, int export, int keylen) { - if (!export) keylen = 1024; - if (keylen == 512) { - FILE *in = fopen("control/dh512.pem", "r"); - if (in) { - DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); - fclose(in); - if (dh) return dh; + EVP_PKEY *pkey = SSL_get_privatekey(ssl); + int type = pkey ? EVP_PKEY_type(pkey->type) : 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, "2"); + return dhparams[n].dh; } } - if (keylen == 1024) { - FILE *in = fopen("control/dh1024.pem", "r"); - if (in) { - DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); - fclose(in); - if (dh) return dh; - } - } - return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL); -} + + return NULL; /* impossible to reach. */ +} /* don't want to fail handshake if cert isn't verifiable */ int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } @@ -1338,7 +1375,6 @@ void tls_init() SSL_set_cipher_list(myssl, ciphers); alloc_free(saciphers.s); - SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb); SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); -- cgit v1.2.3