From 058a25cdcb42572867d613ec13c68a350b9d57b6 Mon Sep 17 00:00:00 2001 From: Jim Jagielski Date: Mon, 11 Nov 2013 14:00:57 +0000 Subject: [PATCH] Merge r834378, r835046, r1040304, r1040373, r1090645, r1294306, r1509872, r1308862, r1509875 from trunk: enable support for ECC keys and ECDH ciphers. Tested against OpenSSL 1.0.0b3. [Vipul Gupta vipul.gupta sun.com, Sander Temme] * Use correct #ifndef's to compile again on openssl 0.9.8 and fix compiler warnings. Noted by: sf Removed unused var. Stop warning, init should be an int. Remove unused variable Initialize EC temporary key on server startup, as for DH and RSA. This fixes a race condition that could lead to a crash with threaded MPMs. Mention ECC support Submitted by: sctemme, rpluem, fuankg, drh, sf, sf, sf, jim, sf Reviewed/backported by: jim git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@1540727 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 4 ++ docs/conf/extra/httpd-ssl.conf.in | 6 ++ docs/manual/mod/mod_ssl.xml | 22 +++---- modules/ssl/mod_ssl.c | 3 + modules/ssl/ssl_engine_init.c | 99 +++++++++++++++++++++++++++++-- modules/ssl/ssl_engine_kernel.c | 21 +++++++ modules/ssl/ssl_private.h | 18 ++++++ modules/ssl/ssl_toolkit_compat.h | 6 ++ modules/ssl/ssl_util.c | 14 +++++ 9 files changed, 179 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index bcf34e3e37b..79e272dc717 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,10 @@ Changes with Apache 2.2.26 *) mod_ssl: Check SNI hostname against Host header case-insensitively. PR 49491. [Mayank Agrawal ] + *) mod_ssl: enable support for ECC keys and ECDH ciphers. Tested against + OpenSSL 1.0.0b3. [Vipul Gupta vipul.gupta sun.com, Sander Temme, + Stefan Fritsch] + *) mod_ssl: Change default for SSLCompression to off, as compression causes security issues in most setups. (The so called "CRIME" attack). [Stefan Fritsch] diff --git a/docs/conf/extra/httpd-ssl.conf.in b/docs/conf/extra/httpd-ssl.conf.in index 7172d8cb541..ccd0770b03c 100644 --- a/docs/conf/extra/httpd-ssl.conf.in +++ b/docs/conf/extra/httpd-ssl.conf.in @@ -114,16 +114,22 @@ SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5 # in mind that if you have both an RSA and a DSA certificate you # can configure both in parallel (to also allow the use of DSA # ciphers, etc.) +# Some ECC cipher suites (http://www.ietf.org/rfc/rfc4492.txt) +# require an ECC certificate which can also be configured in +# parallel. SSLCertificateFile "@exp_sysconfdir@/server.crt" #SSLCertificateFile "@exp_sysconfdir@/server-dsa.crt" +#SSLCertificateFile "@exp_sysconfdir@/server-ecc.crt" # Server Private Key: # If the key is not combined with the certificate, use this # directive to point at the key file. Keep in mind that if # you've both a RSA and a DSA private key you can configure # both in parallel (to also allow the use of DSA ciphers, etc.) +# ECC keys, when in use, can also be configured in parallel SSLCertificateKeyFile "@exp_sysconfdir@/server.key" #SSLCertificateKeyFile "@exp_sysconfdir@/server-dsa.key" +#SSLCertificateKeyFile "@exp_sysconfdir@/server-ecc.key" # Server Certificate Chain: # Point SSLCertificateChainFile at a file containing the diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index f4b00b7aaa3..600b6a2c4df 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -188,12 +188,12 @@ query can be done in two ways which can be configured by Here an external program is configured which is called at startup for each encrypted Private Key file. It is called with two arguments (the first is of the form ``servername:portnumber'', the second is either - ``RSA'' or ``DSA''), which indicate for which - server and algorithm it has to print the corresponding Pass Phrase to - stdout. The intent is that this external program first runs - security checks to make sure that the system is not compromised by an - attacker, and only when these checks were passed successfully it provides - the Pass Phrase.

+ ``RSA'', ``DSA'', or ``ECC''), which + indicate for which server and algorithm it has to print the corresponding + Pass Phrase to stdout. The intent is that this external + program first runs security checks to make sure that the system is not + compromised by an attacker, and only when these checks were passed + successfully it provides the Pass Phrase.

Both these security checks, and the way the Pass Phrase is determined, can be as complex as you like. Mod_ssl just defines the interface: an @@ -761,6 +761,7 @@ SSLCipherSuite RSA:!EXP:!NULL:+HIGH:+MEDIUM:-LOW SSLCertificateFile file-path server config virtual host +ECC support is available in Apache 2.2.26 and later

@@ -768,8 +769,8 @@ This directive points to the PEM-encoded Certificate file for the server and optionally also to the corresponding RSA or DSA Private Key file for it (contained in the same file). If the contained Private Key is encrypted the Pass Phrase dialog is forced at startup time. This directive can be used up to -two times (referencing different filenames) when both a RSA and a DSA based -server certificate is used in parallel.

+three times (referencing different filenames) when both a RSA, a DSA, and an +ECC based server certificate is used in parallel.

Example SSLCertificateFile /usr/local/apache2/conf/ssl.crt/server.crt @@ -782,6 +783,7 @@ SSLCertificateFile /usr/local/apache2/conf/ssl.crt/server.crt SSLCertificateKeyFile file-path server config virtual host +ECC support is available in Apache 2.2.26 and later

@@ -794,8 +796,8 @@ contains both the Certificate and the Private Key this directive need not be used. But we strongly discourage this practice. Instead we recommend you to separate the Certificate and the Private Key. If the contained Private Key is encrypted, the Pass Phrase dialog is forced -at startup time. This directive can be used up to two times -(referencing different filenames) when both a RSA and a DSA based +at startup time. This directive can be used up to three times +(referencing different filenames) when both a RSA, a DSA, and an ECC based private key is used in parallel.

Example SSLCertificateKeyFile /usr/local/apache2/conf/ssl.key/server.key diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c index b9e3f939280..19794f07de4 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -441,6 +441,9 @@ int ssl_init_ssl_connection(conn_rec *c) */ SSL_set_tmp_rsa_callback(ssl, ssl_callback_TmpRSA); SSL_set_tmp_dh_callback(ssl, ssl_callback_TmpDH); +#ifndef OPENSSL_NO_EC + SSL_set_tmp_ecdh_callback(ssl, ssl_callback_TmpECDH); +#endif SSL_set_verify_result(ssl, X509_V_OK); diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index dcae945cf96..d8b4802cd96 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -72,6 +72,9 @@ static void ssl_tmp_keys_free(server_rec *s) MODSSL_TMP_KEYS_FREE(mc, RSA); MODSSL_TMP_KEYS_FREE(mc, DH); +#ifndef OPENSSL_NO_EC + MODSSL_TMP_KEY_FREE(mc, EC_KEY, SSL_TMP_KEY_EC_256); +#endif } static int ssl_tmp_key_init_rsa(server_rec *s, @@ -133,6 +136,40 @@ static int ssl_tmp_key_init_dh(server_rec *s, return OK; } +#ifndef OPENSSL_NO_EC +static int ssl_tmp_key_init_ec(server_rec *s, + int bits, int idx) +{ + SSLModConfigRec *mc = myModConfig(s); + EC_KEY *ecdh = NULL; + + /* XXX: Are there any FIPS constraints we should enforce? */ + + if (bits != 256) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Init: Failed to generate temporary " + "%d bit EC parameters, only 256 bits supported", bits); + return !OK; + } + + if ((ecdh = EC_KEY_new()) == NULL || + EC_KEY_set_group(ecdh, EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) != 1) + { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Init: Failed to generate temporary " + "%d bit EC parameters", bits); + return !OK; + } + + mc->pTmpKeys[idx] = ecdh; + return OK; +} + +#define MODSSL_TMP_KEY_INIT_EC(s, bits) \ + ssl_tmp_key_init_ec(s, bits, SSL_TMP_KEY_EC_##bits) + +#endif + #define MODSSL_TMP_KEY_INIT_RSA(s, bits) \ ssl_tmp_key_init_rsa(s, bits, SSL_TMP_KEY_RSA_##bits) @@ -157,6 +194,15 @@ static int ssl_tmp_keys_init(server_rec *s) return !OK; } +#ifndef OPENSSL_NO_EC + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Init: Generating temporary EC parameters (256 bits)"); + + if (MODSSL_TMP_KEY_INIT_EC(s, 256)) { + return !OK; + } +#endif + return OK; } @@ -399,7 +445,11 @@ static void ssl_init_server_check(server_rec *s, * Check for problematic re-initializations */ if (mctx->pks->certs[SSL_AIDX_RSA] || - mctx->pks->certs[SSL_AIDX_DSA]) + mctx->pks->certs[SSL_AIDX_DSA] +#ifndef OPENSSL_NO_EC + || mctx->pks->certs[SSL_AIDX_ECC] +#endif + ) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Illegal attempt to re-initialise SSL for server " @@ -599,6 +649,9 @@ static void ssl_init_ctx_callbacks(server_rec *s, SSL_CTX_set_tmp_rsa_callback(ctx, ssl_callback_TmpRSA); SSL_CTX_set_tmp_dh_callback(ctx, ssl_callback_TmpDH); +#ifndef OPENSSL_NO_EC + SSL_CTX_set_tmp_ecdh_callback(ctx,ssl_callback_TmpECDH); +#endif SSL_CTX_set_info_callback(ctx, ssl_callback_Info); } @@ -866,9 +919,16 @@ static int ssl_server_import_key(server_rec *s, ssl_asn1_t *asn1; MODSSL_D2I_PrivateKey_CONST unsigned char *ptr; const char *type = ssl_asn1_keystr(idx); - int pkey_type = (idx == SSL_AIDX_RSA) ? EVP_PKEY_RSA : EVP_PKEY_DSA; + int pkey_type; EVP_PKEY *pkey; +#ifndef OPENSSL_NO_EC + if (idx == SSL_AIDX_ECC) + pkey_type = EVP_PKEY_EC; + else +#endif + pkey_type = (idx == SSL_AIDX_RSA) ? EVP_PKEY_RSA : EVP_PKEY_DSA; + if (!(asn1 = ssl_asn1_table_get(mc->tPrivateKey, id))) { return FALSE; } @@ -979,19 +1039,39 @@ static void ssl_init_server_certs(server_rec *s, modssl_ctx_t *mctx) { const char *rsa_id, *dsa_id; +#ifndef OPENSSL_NO_EC + const char *ecc_id; +#endif const char *vhost_id = mctx->sc->vhost_id; int i; int have_rsa, have_dsa; +#ifndef OPENSSL_NO_EC + int have_ecc; +#endif rsa_id = ssl_asn1_table_keyfmt(ptemp, vhost_id, SSL_AIDX_RSA); dsa_id = ssl_asn1_table_keyfmt(ptemp, vhost_id, SSL_AIDX_DSA); +#ifndef OPENSSL_NO_EC + ecc_id = ssl_asn1_table_keyfmt(ptemp, vhost_id, SSL_AIDX_ECC); +#endif have_rsa = ssl_server_import_cert(s, mctx, rsa_id, SSL_AIDX_RSA); have_dsa = ssl_server_import_cert(s, mctx, dsa_id, SSL_AIDX_DSA); +#ifndef OPENSSL_NO_EC + have_ecc = ssl_server_import_cert(s, mctx, ecc_id, SSL_AIDX_ECC); +#endif - if (!(have_rsa || have_dsa)) { + if (!(have_rsa || have_dsa +#ifndef OPENSSL_NO_EC + || have_ecc +#endif +)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, +#ifndef OPENSSL_NO_EC + "Oops, no RSA, DSA or ECC server certificate found " +#else "Oops, no RSA or DSA server certificate found " +#endif "for '%s:%d'?!", s->server_hostname, s->port); ssl_die(); } @@ -1002,10 +1082,21 @@ static void ssl_init_server_certs(server_rec *s, have_rsa = ssl_server_import_key(s, mctx, rsa_id, SSL_AIDX_RSA); have_dsa = ssl_server_import_key(s, mctx, dsa_id, SSL_AIDX_DSA); +#ifndef OPENSSL_NO_EC + have_ecc = ssl_server_import_key(s, mctx, ecc_id, SSL_AIDX_ECC); +#endif - if (!(have_rsa || have_dsa)) { + if (!(have_rsa || have_dsa +#ifndef OPENSSL_NO_EC + || have_ecc +#endif + )) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, +#ifndef OPENSSL_NO_EC + "Oops, no RSA, DSA or ECC server private key found?!"); +#else "Oops, no RSA or DSA server private key found?!"); +#endif ssl_die(); } } diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index 6cb20879be5..28bc47aae2d 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -1267,6 +1267,27 @@ DH *ssl_callback_TmpDH(SSL *ssl, int export, int keylen) return (DH *)mc->pTmpKeys[idx]; } +#ifndef OPENSSL_NO_EC +EC_KEY *ssl_callback_TmpECDH(SSL *ssl, int export, int keylen) +{ + conn_rec *c = (conn_rec *)SSL_get_app_data(ssl); + SSLModConfigRec *mc = myModConfigFromConn(c); + int idx; + + /* XXX Uses 256-bit key for now. TODO: support other sizes. */ + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "handing out temporary 256 bit ECC key"); + + switch (keylen) { + case 256: + default: + idx = SSL_TMP_KEY_EC_256; + } + + return (EC_KEY *)mc->pTmpKeys[idx]; +} +#endif + /* * This OpenSSL callback function is called when OpenSSL * does client authentication and verifies the certificate chain. diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index 650043dfa9b..79d222e8b9b 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -191,11 +191,21 @@ typedef int ssl_algo_t; #define SSL_ALGO_UNKNOWN (0) #define SSL_ALGO_RSA (1<<0) #define SSL_ALGO_DSA (1<<1) +#ifndef OPENSSL_NO_EC +#define SSL_ALGO_ECC (1<<2) +#define SSL_ALGO_ALL (SSL_ALGO_RSA|SSL_ALGO_DSA|SSL_ALGO_ECC) +#else #define SSL_ALGO_ALL (SSL_ALGO_RSA|SSL_ALGO_DSA) +#endif #define SSL_AIDX_RSA (0) #define SSL_AIDX_DSA (1) +#ifndef OPENSSL_NO_EC +#define SSL_AIDX_ECC (2) +#define SSL_AIDX_MAX (3) +#else #define SSL_AIDX_MAX (2) +#endif /** @@ -206,7 +216,12 @@ typedef int ssl_algo_t; #define SSL_TMP_KEY_RSA_1024 (1) #define SSL_TMP_KEY_DH_512 (2) #define SSL_TMP_KEY_DH_1024 (3) +#ifndef OPENSSL_NO_EC +#define SSL_TMP_KEY_EC_256 (4) +#define SSL_TMP_KEY_MAX (5) +#else #define SSL_TMP_KEY_MAX (4) +#endif /** * Define the SSL options @@ -625,6 +640,9 @@ void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s); /** OpenSSL callbacks */ RSA *ssl_callback_TmpRSA(SSL *, int, int); DH *ssl_callback_TmpDH(SSL *, int, int); +#ifndef OPENSSL_NO_EC +EC_KEY *ssl_callback_TmpECDH(SSL *, int, int); +#endif int ssl_callback_SSLVerify(int, X509_STORE_CTX *); int ssl_callback_SSLVerify_CRL(int, X509_STORE_CTX *, conn_rec *); int ssl_callback_proxy_cert(SSL *ssl, MODSSL_CLIENT_CERT_CB_ARG_TYPE **x509, EVP_PKEY **pkey); diff --git a/modules/ssl/ssl_toolkit_compat.h b/modules/ssl/ssl_toolkit_compat.h index bd57a17ae6f..6f49f30162b 100644 --- a/modules/ssl/ssl_toolkit_compat.h +++ b/modules/ssl/ssl_toolkit_compat.h @@ -38,6 +38,12 @@ #include #include #include + +/* ECC support came along in OpenSSL 1.0.0 */ +#if (OPENSSL_VERSION_NUMBER < 0x10000000) +#define OPENSSL_NO_EC +#endif + /** Avoid tripping over an engine build installed globally and detected * when the user points at an explicit non-engine flavor of OpenSSL */ diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c index e4387e6181f..efac3105f91 100644 --- a/modules/ssl/ssl_util.c +++ b/modules/ssl/ssl_util.c @@ -150,6 +150,11 @@ ssl_algo_t ssl_util_algotypeof(X509 *pCert, EVP_PKEY *pKey) case EVP_PKEY_DSA: t = SSL_ALGO_DSA; break; +#ifndef OPENSSL_NO_EC + case EVP_PKEY_EC: + t = SSL_ALGO_ECC; + break; +#endif default: break; } @@ -174,6 +179,11 @@ char *ssl_util_algotypestr(ssl_algo_t t) case SSL_ALGO_DSA: cp = "DSA"; break; +#ifndef OPENSSL_NO_EC + case SSL_ALGO_ECC: + cp = "ECC"; + break; +#endif default: break; } @@ -245,7 +255,11 @@ void ssl_asn1_table_unset(apr_hash_t *table, apr_hash_set(table, key, klen, NULL); } +#ifndef OPENSSL_NO_EC +static const char *ssl_asn1_key_types[] = {"RSA", "DSA", "ECC"}; +#else static const char *ssl_asn1_key_types[] = {"RSA", "DSA"}; +#endif const char *ssl_asn1_keystr(int keytype) {