On 16.09.2013 07:18, Kaspar Brand wrote: > On 15.09.2013 15:16, Erwann Abalea wrote: >> Ideally, the second patch should integrate the 2048bits parameters inside >> the "generated section", and adapt the Perl code accordingly. >> That way, a paranoid sysadmin can run this file in his perl interpreter, >> and have his own 512/1024/2048 parameters generated by OpenSSL. >> You could also decide to remove that possibility once admin-chosen DH >> parameters become available. > > I'm definitely in favor of the latter (not sure how many people really > do know that ssl_engine_dh.c can be executed as a Perl script - and if > we hardcode DH parameters, I'd rather go for well-defined ones).
Attached is an improved version of the second part of the patch, which does the following: - completely drops ssl_engine_dh.c from mod_ssl - relies on DH parameters from RFCs 2407 and 3526 (which are already included as constants in OpenSSL 0.9.8a and later), and configures them in the callback based on the certificate's key length (1024 bits at the minimum, 4096 bits max., currently) - allows admins to configure their own DH parameters via SSLCertificateFile (already implemented with the previous version of the patch), in which case they have precedence over the hardcoded parameters Feedback on this approach is again very welcome. Increasing the minimum required OpenSSL version from 0.9.7 to 0.9.8a shouldn't be of concern, IMO, as 0.9.7 is no longer maintained, and 0.9.8a was released in October 2005 already. Kaspar
Streamline/improve ephemeral key handling, part 2, version 2: [to be applied on top of mod_ssl_ekh_part1.diff posted with http://mail-archives.apache.org/mod_mbox/httpd-dev/201309.mbox/%[email protected]%3E] - add well-defined DH parameters (from RFCs 2409 and 3526), and use them based on the length of the certificate's RSA or DSA key - allow to configure custom DHE or ECDHE parameters via the SSLCertificateFile directive - move ssl_dh_GetParamFromFile() from ssl_engine_dh.c to ssl_util_ssl.c, and add ssl_ec_GetParamFromFile() - drop ssl_engine_dh.c from mod_ssl - require OpenSSL 0.9.8a or later Index: ssl_private.h =================================================================== --- ssl_private.h (revision 1509097) +++ ssl_private.h (working copy) @@ -901,8 +901,10 @@ void ssl_pphrase_Handle(server_rec *, apr_pool_t *); /** Diffie-Hellman Parameter Support */ -DH *ssl_dh_GetTmpParam(int); -DH *ssl_dh_GetParamFromFile(char *); +DH *ssl_dh_GetParamFromFile(const char *); +#ifndef OPENSSL_NO_EC +EC_GROUP *ssl_ec_GetParamFromFile(const char *); +#endif unsigned char *ssl_asn1_table_set(apr_hash_t *table, const char *key, Index: ssl_engine_dh.c =================================================================== --- ssl_engine_dh.c (revision 1509097) +++ ssl_engine_dh.c (working copy) @@ -1,244 +0,0 @@ -#if 0 -=pod -#endif - -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* _ _ - * _ __ ___ ___ __| | ___ ___| | mod_ssl - * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL - * | | | | | | (_) | (_| | \__ \__ \ | - * |_| |_| |_|\___/ \__,_|___|___/___/_| - * |_____| - * ssl_engine_dh.c - * Diffie-Hellman Built-in Temporary Parameters - */ - -#include "ssl_private.h" - -/* ----BEGIN GENERATED SECTION-------- */ - -/* -** Diffie-Hellman-Parameters: (512 bit) -** prime: -** 00:9f:db:8b:8a:00:45:44:f0:04:5f:17:37:d0:ba: -** 2e:0b:27:4c:df:1a:9f:58:82:18:fb:43:53:16:a1: -** 6e:37:41:71:fd:19:d8:d8:f3:7c:39:bf:86:3f:d6: -** 0e:3e:30:06:80:a3:03:0c:6e:4c:37:57:d0:8f:70: -** e6:aa:87:10:33 -** generator: 2 (0x2) -** Diffie-Hellman-Parameters: (1024 bit) -** prime: -** 00:d6:7d:e4:40:cb:bb:dc:19:36:d6:93:d3:4a:fd: -** 0a:d5:0c:84:d2:39:a4:5f:52:0b:b8:81:74:cb:98: -** bc:e9:51:84:9f:91:2e:63:9c:72:fb:13:b4:b4:d7: -** 17:7e:16:d5:5a:c1:79:ba:42:0b:2a:29:fe:32:4a: -** 46:7a:63:5e:81:ff:59:01:37:7b:ed:dc:fd:33:16: -** 8a:46:1a:ad:3b:72:da:e8:86:00:78:04:5b:07:a7: -** db:ca:78:74:08:7d:15:10:ea:9f:cc:9d:dd:33:05: -** 07:dd:62:db:88:ae:aa:74:7d:e0:f4:d6:e2:bd:68: -** b0:e7:39:3e:0f:24:21:8e:b3 -** generator: 2 (0x2) -*/ - -static unsigned char dh512_p[] = { - 0x9F, 0xDB, 0x8B, 0x8A, 0x00, 0x45, 0x44, 0xF0, 0x04, 0x5F, 0x17, 0x37, - 0xD0, 0xBA, 0x2E, 0x0B, 0x27, 0x4C, 0xDF, 0x1A, 0x9F, 0x58, 0x82, 0x18, - 0xFB, 0x43, 0x53, 0x16, 0xA1, 0x6E, 0x37, 0x41, 0x71, 0xFD, 0x19, 0xD8, - 0xD8, 0xF3, 0x7C, 0x39, 0xBF, 0x86, 0x3F, 0xD6, 0x0E, 0x3E, 0x30, 0x06, - 0x80, 0xA3, 0x03, 0x0C, 0x6E, 0x4C, 0x37, 0x57, 0xD0, 0x8F, 0x70, 0xE6, - 0xAA, 0x87, 0x10, 0x33, -}; -static unsigned char dh512_g[] = { - 0x02, -}; - -static DH *get_dh512(void) -{ - DH *dh; - - if (!(dh = DH_new())) { - return NULL; - } - - dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL); - dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL); - if (!(dh->p && dh->g)) { - DH_free(dh); - return NULL; - } - - return dh; -} - -static unsigned char dh1024_p[] = { - 0xD6, 0x7D, 0xE4, 0x40, 0xCB, 0xBB, 0xDC, 0x19, 0x36, 0xD6, 0x93, 0xD3, - 0x4A, 0xFD, 0x0A, 0xD5, 0x0C, 0x84, 0xD2, 0x39, 0xA4, 0x5F, 0x52, 0x0B, - 0xB8, 0x81, 0x74, 0xCB, 0x98, 0xBC, 0xE9, 0x51, 0x84, 0x9F, 0x91, 0x2E, - 0x63, 0x9C, 0x72, 0xFB, 0x13, 0xB4, 0xB4, 0xD7, 0x17, 0x7E, 0x16, 0xD5, - 0x5A, 0xC1, 0x79, 0xBA, 0x42, 0x0B, 0x2A, 0x29, 0xFE, 0x32, 0x4A, 0x46, - 0x7A, 0x63, 0x5E, 0x81, 0xFF, 0x59, 0x01, 0x37, 0x7B, 0xED, 0xDC, 0xFD, - 0x33, 0x16, 0x8A, 0x46, 0x1A, 0xAD, 0x3B, 0x72, 0xDA, 0xE8, 0x86, 0x00, - 0x78, 0x04, 0x5B, 0x07, 0xA7, 0xDB, 0xCA, 0x78, 0x74, 0x08, 0x7D, 0x15, - 0x10, 0xEA, 0x9F, 0xCC, 0x9D, 0xDD, 0x33, 0x05, 0x07, 0xDD, 0x62, 0xDB, - 0x88, 0xAE, 0xAA, 0x74, 0x7D, 0xE0, 0xF4, 0xD6, 0xE2, 0xBD, 0x68, 0xB0, - 0xE7, 0x39, 0x3E, 0x0F, 0x24, 0x21, 0x8E, 0xB3, -}; -static unsigned char dh1024_g[] = { - 0x02, -}; - -static DH *get_dh1024(void) -{ - DH *dh; - - if (!(dh = DH_new())) { - return NULL; - } - - dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL); - dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL); - if (!(dh->p && dh->g)) { - DH_free(dh); - return NULL; - } - - return dh; -} - -/* ----END GENERATED SECTION---------- */ - -DH *ssl_dh_GetTmpParam(int nKeyLen) -{ - DH *dh; - - if (nKeyLen == 512) - dh = get_dh512(); - else if (nKeyLen == 1024) - dh = get_dh1024(); - else - dh = get_dh1024(); - return dh; -} - -DH *ssl_dh_GetParamFromFile(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); - return (dh); -} - -/* -=cut -## -## Embedded Perl script for generating the temporary DH parameters -## - -require 5.003; -use strict; - -# configuration -my $file = $0; -my $begin = '----BEGIN GENERATED SECTION--------'; -my $end = '----END GENERATED SECTION----------'; - -# read ourself and keep a backup -open(FP, "<$file") || die; -my $source = ''; -$source .= $_ while (<FP>); -close(FP); -open(FP, ">$file.bak") || die; -print FP $source; -close(FP); - -# generate the DH parameters -print "1. Generate 512 and 1024 bit Diffie-Hellman parameters (p, g)\n"; -my $rand = ''; -foreach $file (qw(/var/log/messages /var/adm/messages - /kernel /vmunix /vmlinuz /etc/hosts /etc/resolv.conf)) { - if (-f $file) { - $rand = $file if ($rand eq ''); - $rand .= ":$file" if ($rand ne ''); - } -} -$rand = "-rand $rand" if ($rand ne ''); -system("openssl gendh $rand -out dh512.pem 512"); -system("openssl gendh $rand -out dh1024.pem 1024"); - -# generate DH param info -my $dhinfo = ''; -open(FP, "openssl dh -noout -text -in dh512.pem |") || die; -$dhinfo .= $_ while (<FP>); -close(FP); -open(FP, "openssl dh -noout -text -in dh1024.pem |") || die; -$dhinfo .= $_ while (<FP>); -close(FP); -$dhinfo =~ s|^|** |mg; -$dhinfo = "\n\/\*\n$dhinfo\*\/\n\n"; - -my $indent_args = "-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1"; - -# generate C source from DH params -my $dhsource = ''; -open(FP, "openssl dh -noout -C -in dh512.pem | indent $indent_args | expand |") || die; -$dhsource .= $_ while (<FP>); -close(FP); -open(FP, "openssl dh -noout -C -in dh1024.pem | indent $indent_args | expand |") || die; -$dhsource .= $_ while (<FP>); -close(FP); -$dhsource =~ s|(DH\s+\*get_dh)(\d+)[^}]*\n}|static $1$2(void) -{ - DH *dh; - - if (!(dh = DH_new())) { - return NULL; - } - - dh->p = BN_bin2bn(dh$2_p, sizeof(dh$2_p), NULL); - dh->g = BN_bin2bn(dh$2_g, sizeof(dh$2_g), NULL); - if (!(dh->p && dh->g)) { - DH_free(dh); - return NULL; - } - - return dh; -} -|sg; - -# generate output -my $o = $dhinfo . $dhsource; - -# insert the generated code at the target location -$source =~ s|(\/\* $begin.+?\n).*\n(.*?\/\* $end)|$1$o$2|s; - -# and update the source on disk -print "Updating file `$file'\n"; -open(FP, ">$file") || die; -print FP $source; -close(FP); - -# cleanup -unlink("dh512.pem"); -unlink("dh1024.pem"); - -=pod -*/ Index: config.m4 =================================================================== --- config.m4 (revision 1509097) +++ config.m4 (working copy) @@ -20,7 +20,6 @@ ssl_objs="dnl mod_ssl.lo dnl ssl_engine_config.lo dnl -ssl_engine_dh.lo dnl ssl_engine_init.lo dnl ssl_engine_io.lo dnl ssl_engine_kernel.lo dnl Index: mod_ssl.dsp =================================================================== --- mod_ssl.dsp (revision 1509097) +++ mod_ssl.dsp (working copy) @@ -112,10 +112,6 @@ # End Source File # Begin Source File -SOURCE=.\ssl_engine_dh.c -# End Source File -# Begin Source File - SOURCE=.\ssl_engine_init.c # End Source File # Begin Source File Index: ssl_engine_init.c =================================================================== --- ssl_engine_init.c (revision 1509097) +++ ssl_engine_init.c (working copy) @@ -981,10 +981,14 @@ const char *rsa_id, *dsa_id; #ifndef OPENSSL_NO_EC const char *ecc_id; + EC_GROUP *ecparams; + int nid; + EC_KEY *eckey; #endif const char *vhost_id = mctx->sc->vhost_id; int i; int have_rsa, have_dsa; + DH *dhparams; #ifndef OPENSSL_NO_EC int have_ecc; #endif @@ -1032,10 +1036,38 @@ ssl_die(s); } + /* + * Try to read DHE parameters from the (first) SSLCertificateFile + */ + if ((mctx->pks->cert_files[0] != NULL) && + (dhparams = ssl_dh_GetParamFromFile(mctx->pks->cert_files[0]))) { + SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dhparams); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO() + "DHE parameters (%d bits) for %s configured from %s", + BN_num_bits(dhparams->p), vhost_id, + mctx->pks->cert_files[0]); + } + #ifndef OPENSSL_NO_EC - /* Enable ECDHE by configuring a default curve */ - SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, - EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + /* + * Similarly, try to read the ECDHE curve name from SSLCertificateFile... + */ + if ((mctx->pks->cert_files[0] != NULL) && + (ecparams = ssl_ec_GetParamFromFile(mctx->pks->cert_files[0])) && + (nid = EC_GROUP_get_curve_name(ecparams)) && + (eckey = EC_KEY_new_by_curve_name(nid))) { + SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO() + "ECDHE curve (%s) for %s configured from %s", + OBJ_nid2sn(nid), vhost_id, mctx->pks->cert_files[0]); + } + /* + * ...otherwise, configure a default curve (required to enable ECDHE) + */ + else { + SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, + EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + } #endif } Index: ssl_util_ssl.c =================================================================== --- ssl_util_ssl.c (revision 1509097) +++ ssl_util_ssl.c (working copy) @@ -483,6 +483,38 @@ /* _________________________________________________________________ ** +** Custom (EC)DH parameter support +** _________________________________________________________________ +*/ + +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); + return (dh); +} + +#ifndef OPENSSL_NO_EC +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); + return (group); +} +#endif + +/* _________________________________________________________________ +** ** Extra Server Certificate Chain Support ** _________________________________________________________________ */ Index: ssl_engine_kernel.c =================================================================== --- ssl_engine_kernel.c (revision 1509097) +++ ssl_engine_kernel.c (working copy) @@ -1287,16 +1287,68 @@ */ /* - * Hand out the already generated DH parameters... + * Grab well-defined DH parameters from OpenSSL (available in 0.9.8a + * and later), see crypto/bn/bn_const.c for all available primes. */ +#define make_get_dh(rfc,size,gen) \ +static DH *get_dh##size(void) \ +{ \ + DH *dh; \ + if (!(dh = DH_new())) { \ + return NULL; \ + } \ + dh->p = get_##rfc##_prime_##size(NULL); \ + BN_dec2bn(&dh->g, #gen); \ + if (!dh->p || !dh->g) { \ + DH_free(dh); \ + return NULL; \ + } \ + return dh; \ +} + +/* + * Prepare DH parameters from 1024 to 4096 bits, in 1024-bit increments + */ +make_get_dh(rfc2409, 1024, 2) +make_get_dh(rfc3526, 2048, 2) +make_get_dh(rfc3526, 3072, 2) +make_get_dh(rfc3526, 4096, 2) + +/* + * Hand out standard DH parameters, based on the authentication strength + */ DH *ssl_callback_TmpDH(SSL *ssl, int export, int keylen) { conn_rec *c = (conn_rec *)SSL_get_app_data(ssl); + EVP_PKEY *pkey = SSL_get_privatekey(ssl); + int type = EVP_PKEY_NONE; + /* + * 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 if we can determine the length of + * the RSA/DSA private key used for the current connection (and use + * at least 1024-bit parameters). + * XXX Note: This may cause interoperability issues with implementations + * which limit their DH support to 1024 bit - e.g. Java 7 and earlier, cf. + * http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/tip/src/share/classes/com/sun/crypto/provider/DHKeyPairGenerator.java + */ + if (pkey && (type = EVP_PKEY_type(pkey->type)) && + ((type == EVP_PKEY_RSA) || (type == EVP_PKEY_DSA))) { + keylen = EVP_PKEY_bits(pkey); + } + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, - "handing out parameters for temporary %d bit DH key", keylen); + "handing out temporary DH key parameters for %d-bit authentication", keylen); - return ssl_dh_GetTmpParam(keylen); + if (keylen >= 4096) + return get_dh4096(); + else if (keylen >= 3072) + return get_dh3072(); + else if (keylen >= 2048) + return get_dh2048(); + else + return get_dh1024(); } /* Index: ../../acinclude.m4 =================================================================== --- ../../acinclude.m4 (revision 1509097) +++ ../../acinclude.m4 (working copy) @@ -552,12 +552,12 @@ fi fi - AC_MSG_CHECKING([for OpenSSL version >= 0.9.7]) + AC_MSG_CHECKING([for OpenSSL version >= 0.9.8a]) AC_TRY_COMPILE([#include <openssl/opensslv.h>],[ #if !defined(OPENSSL_VERSION_NUMBER) #error "Missing OpenSSL version" #endif -#if OPENSSL_VERSION_NUMBER < 0x0090700f +#if OPENSSL_VERSION_NUMBER < 0x0090801f #error "Unsupported OpenSSL version " OPENSSL_VERSION_TEXT #endif], [AC_MSG_RESULT(OK)
