From f0437832d3d4f91ade39b94b746194b3a610b4ad Mon Sep 17 00:00:00 2001 From: KLuka Date: Mon, 3 Aug 2015 19:11:38 +0200 Subject: [PATCH] Added support for Perfect Forward Secrecy (#331) * Support for PFS is enabled with help of chiper suits that use ECDHE key exchange. OpenSSL added support for eliptic curve operations (EC) in version 0.9.8. Note that there are also some library distributions which don't support EC operations. * Added precompiler guards for builds with OpenSSL older than 0.9.8 and builds with '--enable-runtime-loading' configure script option. * Cleaned up some SSL related code. --- libhttp/ssl.c | 68 ++++++++++++++++++++++++++++++++++++++++----------- libhttp/ssl.h | 25 ++++++++++++++----- 2 files changed, 73 insertions(+), 20 deletions(-) diff --git a/libhttp/ssl.c b/libhttp/ssl.c index 9a62726..28229cf 100644 --- a/libhttp/ssl.c +++ b/libhttp/ssl.c @@ -102,15 +102,18 @@ BIO * (*BIO_new)(BIO_METHOD *); BIO * (*BIO_new_socket)(int, int); BIO * (*BIO_pop)(BIO *); BIO * (*BIO_push)(BIO *, BIO *); +#if defined(HAVE_OPENSSL_EC) +void (*EC_KEY_free)(EC_KEY *); +EC_KEY * (*EC_KEY_new_by_curve_name)(int); +#endif void (*ERR_clear_error)(void); -void (*ERR_clear_error)(void); -unsigned long (*ERR_peek_error)(void); unsigned long (*ERR_peek_error)(void); long (*SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(void)); int (*SSL_CTX_check_private_key)(const SSL_CTX *); long (*SSL_CTX_ctrl)(SSL_CTX *, int, long, void *); void (*SSL_CTX_free)(SSL_CTX *); SSL_CTX * (*SSL_CTX_new)(SSL_METHOD *); +int (*SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str); int (*SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int); int (*SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *, const unsigned char *, long); @@ -136,7 +139,6 @@ int (*SSL_write)(SSL *, const void *, int); SSL_METHOD * (*SSLv23_server_method)(void); X509 * (*d2i_X509)(X509 **px, const unsigned char **in, int len); void (*X509_free)(X509 *a); -int (*x_SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str); void (*x_sk_zero)(void *st); void * (*x_SSL_COMP_get_compression_methods)(void); #endif @@ -280,11 +282,16 @@ static void loadSSL(void) { { { &ERR_clear_error }, "ERR_clear_error" }, { { &ERR_peek_error }, "ERR_peek_error" }, { { &ERR_peek_error }, "ERR_peek_error" }, +#ifdef HAVE_OPENSSL_EC + { { &EC_KEY_free }, "EC_KEY_free" }, + { { &EC_KEY_new_by_curve_name }, "EC_KEY_new_by_curve_name" }, +#endif { { &SSL_CTX_callback_ctrl }, "SSL_CTX_callback_ctrl" }, { { &SSL_CTX_check_private_key }, "SSL_CTX_check_private_key" }, { { &SSL_CTX_ctrl }, "SSL_CTX_ctrl" }, { { &SSL_CTX_free }, "SSL_CTX_free" }, { { &SSL_CTX_new }, "SSL_CTX_new" }, + { { &SSL_CTX_set_cipher_list }, "SSL_CTX_set_cipher_list" }, { { &SSL_CTX_use_PrivateKey_file }, "SSL_CTX_use_PrivateKey_file" }, { { &SSL_CTX_use_PrivateKey_ASN1 }, "SSL_CTX_use_PrivateKey_ASN1" }, { { &SSL_CTX_use_certificate_file },"SSL_CTX_use_certificate_file"}, @@ -312,7 +319,6 @@ static void loadSSL(void) { { { &SSLv23_server_method }, "SSLv23_server_method" }, { { &d2i_X509 }, "d2i_X509" }, { { &X509_free }, "X509_free" }, - { { &x_SSL_CTX_set_cipher_list }, "SSL_CTX_set_cipher_list" }, { { &x_sk_zero }, "sk_zero" } }; for (unsigned i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) { @@ -329,6 +335,7 @@ static void loadSSL(void) { x_SSL_COMP_get_compression_methods = loadSymbol(path_libssl, "SSL_COMP_get_compression_methods"); // ends + SSL_library_init(); dcheck(!ERR_peek_error()); debug("Loaded SSL suppport"); @@ -596,28 +603,61 @@ static int sslSetCertificateFromFile(SSL_CTX *context, } static SSL_CTX *sslMakeContext(void) { + SSL_CTX *context; check(context = SSL_CTX_new(SSLv23_server_method())); - SSL_CTX_set_options(context, SSL_OP_ALL); - SSL_CTX_set_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + + long options = SSL_OP_ALL; + options |= SSL_OP_NO_SSLv2; + options |= SSL_OP_NO_SSLv3; + options |= SSL_OP_SINGLE_DH_USE; + #ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(context, SSL_OP_NO_COMPRESSION); + options |= SSL_OP_NO_COMPRESSION; #endif -#if defined(HAVE_DLOPEN) + +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + options |= SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; +#endif + + // Set default SSL options. + SSL_CTX_set_options(context, options); + + // Workaround for SSL_OP_NO_COMPRESSION with older OpenSSL versions. +#ifdef HAVE_DLOPEN if (SSL_COMP_get_compression_methods) { sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); } #elif OPENSSL_VERSION_NUMBER >= 0x00908000L sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); #endif - SSL_CTX_set_options(context, SSL_OP_SINGLE_DH_USE); -#ifdef SSL_OP_SINGLE_ECDH_USE + + // For Perfect Forward Secrecy (PFS) support we need to enable some additional + // SSL options, provide eliptic curve key object for handshake and add chipers + // suits with ECDHE handshake on top of the ciper list. +#ifdef HAVE_OPENSSL_EC SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); + SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE); + + EC_KEY *ecKey; + check(ecKey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + SSL_CTX_set_tmp_ecdh(context, ecKey); + EC_KEY_free(ecKey); + + debug("SSL: support for PFS enabled..."); #endif -#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION - SSL_CTX_set_options(context, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); -#endif - check(SSL_CTX_set_cipher_list(context, "HIGH:MEDIUM:!aNULL:!MD5")); + + check(SSL_CTX_set_cipher_list(context, + "ECDHE-RSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES256-SHA384:" + "ECDHE-RSA-AES128-SHA256:" + "ECDHE-RSA-AES256-SHA:" + "ECDHE-RSA-AES128-SHA:" + "ECDHE-RSA-DES-CBC3-SHA:" + "HIGH:MEDIUM:!RC4:!aNULL:!MD5")); + + debug("SSL: server context succesfully initialized..."); return context; } #endif diff --git a/libhttp/ssl.h b/libhttp/ssl.h index e1477ab..cd93aae 100644 --- a/libhttp/ssl.h +++ b/libhttp/ssl.h @@ -61,6 +61,7 @@ #undef HAVE_OPENSSL typedef struct BIO BIO; typedef struct BIO_METHOD BIO_METHOD; +typedef struct EC_KEY EC_KEY; typedef struct SSL SSL; typedef struct SSL_CTX SSL_CTX; typedef struct SSL_METHOD SSL_METHOD; @@ -69,6 +70,12 @@ typedef struct X509 X509; #define SSL_ERROR_WANT_WRITE 3 #endif +// EC support was added to OpenSSL in 0.9.8, but it can be disabled in some +// distributions. +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_EC) +# define HAVE_OPENSSL_EC +#endif + #if defined(HAVE_DLOPEN) extern long (*x_BIO_ctrl)(BIO *, int, long, void *); extern BIO_METHOD *(*x_BIO_f_buffer)(void); @@ -77,15 +84,18 @@ extern BIO *(*x_BIO_new)(BIO_METHOD *); extern BIO *(*x_BIO_new_socket)(int, int); extern BIO *(*x_BIO_pop)(BIO *); extern BIO *(*x_BIO_push)(BIO *, BIO *); +#if defined(HAVE_OPENSSL_EC) +extern void (*x_EC_KEY_free)(EC_KEY *); +extern EC_KEY *(*x_EC_KEY_new_by_curve_name)(int); +#endif extern void (*x_ERR_clear_error)(void); -extern void (*x_ERR_clear_error)(void); -extern unsigned long (*x_ERR_peek_error)(void); extern unsigned long (*x_ERR_peek_error)(void); extern long (*x_SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(void)); extern int (*x_SSL_CTX_check_private_key)(const SSL_CTX *); extern long (*x_SSL_CTX_ctrl)(SSL_CTX *, int, long, void *); extern void (*x_SSL_CTX_free)(SSL_CTX *); extern SSL_CTX*(*x_SSL_CTX_new)(SSL_METHOD *); +extern int (*x_SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str); extern int (*x_SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int); extern int (*x_SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *, const unsigned char *, long); @@ -111,7 +121,6 @@ extern int (*x_SSL_write)(SSL *, const void *, int); extern SSL_METHOD *(*x_SSLv23_server_method)(void); extern X509 * (*x_d2i_X509)(X509 **px, const unsigned char **in, int len); extern void (*x_X509_free)(X509 *a); -extern int (*x_SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str); extern void (*x_sk_zero)(void *st); extern void *(*x_SSL_COMP_get_compression_methods)(void); @@ -122,15 +131,16 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void); #define BIO_new_socket x_BIO_new_socket #define BIO_pop x_BIO_pop #define BIO_push x_BIO_push +#define EC_KEY_free x_EC_KEY_free +#define EC_KEY_new_by_curve_name x_EC_KEY_new_by_curve_name #define ERR_clear_error x_ERR_clear_error -#define ERR_clear_error x_ERR_clear_error -#define ERR_peek_error x_ERR_peek_error #define ERR_peek_error x_ERR_peek_error #define SSL_CTX_callback_ctrl x_SSL_CTX_callback_ctrl #define SSL_CTX_check_private_key x_SSL_CTX_check_private_key #define SSL_CTX_ctrl x_SSL_CTX_ctrl #define SSL_CTX_free x_SSL_CTX_free #define SSL_CTX_new x_SSL_CTX_new +#define SSL_CTX_set_cipher_list x_SSL_CTX_set_cipher_list #define SSL_CTX_use_PrivateKey_file x_SSL_CTX_use_PrivateKey_file #define SSL_CTX_use_PrivateKey_ASN1 x_SSL_CTX_use_PrivateKey_ASN1 #define SSL_CTX_use_certificate_file x_SSL_CTX_use_certificate_file @@ -154,13 +164,13 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void); #define SSLv23_server_method x_SSLv23_server_method #define d2i_X509 x_d2i_X509 #define X509_free x_X509_free -#define SSL_CTX_set_cipher_list x_SSL_CTX_set_cipher_list #define sk_zero x_sk_zero #define SSL_COMP_get_compression_methods x_SSL_COMP_get_compression_methods #undef BIO_set_buffer_read_data #undef SSL_CTX_set_tlsext_servername_arg #undef SSL_CTX_set_tlsext_servername_callback +#undef SSL_CTX_set_tmp_ecdh #undef SSL_get_app_data #undef SSL_set_app_data #undef SSL_set_mode @@ -175,6 +185,9 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void); (x_SSL_CTX_callback_ctrl(ctx, \ SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \ (void (*)(void))cb)) +#define SSL_CTX_set_tmp_ecdh(ctx, ecdh) \ + (x_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_ECDH, \ + 0, (char *)ecdh)) #define SSL_get_app_data(s) (x_SSL_get_ex_data(s, 0)) #define SSL_set_app_data(s, arg) (x_SSL_set_ex_data(s, 0, (char *)arg)) #define SSL_set_mode(ssl, op) (x_SSL_ctrl((ssl), SSL_CTRL_MODE, (op), NULL))