From d203e7af836ed4ed049b442877ad43a277e3156a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 8 May 2019 15:42:13 +0300 Subject: [PATCH] Move SSL related code into ssl.cc The code was in the listener sources but was shared by both listeners and servers. --- include/maxscale/listener.hh | 28 ---- include/maxscale/ssl.hh | 46 ++++++ server/core/listener.cc | 294 ----------------------------------- server/core/ssl.cc | 280 +++++++++++++++++++++++++++++++++ 4 files changed, 326 insertions(+), 322 deletions(-) diff --git a/include/maxscale/listener.hh b/include/maxscale/listener.hh index 1960ff009..97331e04e 100644 --- a/include/maxscale/listener.hh +++ b/include/maxscale/listener.hh @@ -359,31 +359,3 @@ SListener listener_find_by_socket(const std::string& socket); * @return The matching listener if one was found */ SListener listener_find_by_address(const std::string& address, unsigned short port); - -int listener_set_ssl_version(SSL_LISTENER* ssl_listener, const char* version); -void listener_set_certificates(SSL_LISTENER* ssl_listener, const std::string& cert, - const std::string& key, const std::string& ca_cert); - -/** - * Initialize SSL configuration - * - * This sets up the generated RSA encryption keys, chooses the listener - * encryption level and configures the listener certificate, private key and - * certificate authority file. - * - * @note This function should not be called directly, use config_create_ssl() instead - * - * @todo Combine this with config_create_ssl() into one function - * - * @param ssl SSL configuration to initialize - * - * @return True on success, false on error - */ -bool SSL_LISTENER_init(SSL_LISTENER* ssl); - -/** - * Free an SSL_LISTENER - * - * @param ssl SSL_LISTENER to free - */ -void SSL_LISTENER_free(SSL_LISTENER* ssl); diff --git a/include/maxscale/ssl.hh b/include/maxscale/ssl.hh index 5412be978..b8bc44ea6 100644 --- a/include/maxscale/ssl.hh +++ b/include/maxscale/ssl.hh @@ -93,3 +93,49 @@ int ssl_authenticate_check_status(DCB* dcb); // TODO: Move this to an internal ssl.h header void write_ssl_config(int fd, SSL_LISTENER* ssl); + +/** + * Set the maximum SSL/TLS version the listener will support + * + * @param ssl_listener Listener data to configure + * @param version SSL/TLS version string + * + * @return 0 on success, -1 on invalid version string + */ +int listener_set_ssl_version(SSL_LISTENER* ssl_listener, const char* version); + +/** + * Set the locations of the listener's SSL certificate, listener's private key + * and the CA certificate which both the client and the listener should trust. + * + * @param ssl_listener Listener data to configure + * @param cert SSL certificate + * @param key SSL private key + * @param ca_cert SSL CA certificate + */ +void listener_set_certificates(SSL_LISTENER* ssl_listener, const std::string& cert, + const std::string& key, const std::string& ca_cert); + +/** + * Initialize SSL configuration + * + * This sets up the generated RSA encryption keys, chooses the listener + * encryption level and configures the listener certificate, private key and + * certificate authority file. + * + * @note This function should not be called directly, use config_create_ssl() instead + * + * @todo Combine this with config_create_ssl() into one function + * + * @param ssl SSL configuration to initialize + * + * @return True on success, false on error + */ +bool SSL_LISTENER_init(SSL_LISTENER* ssl); + +/** + * Free an SSL_LISTENER + * + * @param ssl SSL_LISTENER to free + */ +void SSL_LISTENER_free(SSL_LISTENER* ssl); diff --git a/server/core/listener.cc b/server/core/listener.cc index 0f15c62a2..37f256b3d 100644 --- a/server/core/listener.cc +++ b/server/core/listener.cc @@ -49,10 +49,6 @@ using std::chrono::seconds; static std::list all_listeners; static std::mutex listener_lock; -static RSA* rsa_512 = NULL; -static RSA* rsa_1024 = NULL; -static RSA* tmp_rsa_callback(SSL* s, int is_export, int keylength); - constexpr int BLOCK_TIME = 60; namespace @@ -443,296 +439,6 @@ SListener listener_find_by_address(const std::string& address, unsigned short po return rval; } -/** - * Set the maximum SSL/TLS version the listener will support - * @param ssl_listener Listener data to configure - * @param version SSL/TLS version string - * @return 0 on success, -1 on invalid version string - */ -int listener_set_ssl_version(SSL_LISTENER* ssl_listener, const char* version) -{ - if (strcasecmp(version, "MAX") == 0) - { - ssl_listener->ssl_method_type = SERVICE_SSL_TLS_MAX; - } -#ifndef OPENSSL_1_1 - else if (strcasecmp(version, "TLSV10") == 0) - { - ssl_listener->ssl_method_type = SERVICE_TLS10; - } -#else -#endif -#ifdef OPENSSL_1_0 - else if (strcasecmp(version, "TLSV11") == 0) - { - ssl_listener->ssl_method_type = SERVICE_TLS11; - } - else if (strcasecmp(version, "TLSV12") == 0) - { - ssl_listener->ssl_method_type = SERVICE_TLS12; - } -#endif - else - { - return -1; - } - return 0; -} - -/** - * Set the locations of the listener's SSL certificate, listener's private key - * and the CA certificate which both the client and the listener should trust. - * @param ssl_listener Listener data to configure - * @param cert SSL certificate - * @param key SSL private key - * @param ca_cert SSL CA certificate - */ -void listener_set_certificates(SSL_LISTENER* ssl_listener, const std::string& cert, - const std::string& key, const std::string& ca_cert) -{ - MXS_FREE(ssl_listener->ssl_cert); - ssl_listener->ssl_cert = !cert.empty() ? MXS_STRDUP_A(cert.c_str()) : nullptr; - - MXS_FREE(ssl_listener->ssl_key); - ssl_listener->ssl_key = !key.empty() ? MXS_STRDUP_A(key.c_str()) : nullptr; - - MXS_FREE(ssl_listener->ssl_ca_cert); - ssl_listener->ssl_ca_cert = !ca_cert.empty() ? MXS_STRDUP_A(ca_cert.c_str()) : nullptr; -} - -RSA* create_rsa(int bits) -{ -#ifdef OPENSSL_1_1 - BIGNUM* bn = BN_new(); - BN_set_word(bn, RSA_F4); - RSA* rsa = RSA_new(); - RSA_generate_key_ex(rsa, bits, bn, NULL); - BN_free(bn); - return rsa; -#else - return RSA_generate_key(bits, RSA_F4, NULL, NULL); -#endif -} - -// thread-local non-POD types are not supported with older versions of GCC -static thread_local std::string* ssl_errbuf; - -static const char* get_ssl_errors() -{ - if (ssl_errbuf == NULL) - { - ssl_errbuf = new std::string; - } - - char errbuf[200]; // Enough space according to OpenSSL documentation - ssl_errbuf->clear(); - - for (int err = ERR_get_error(); err; err = ERR_get_error()) - { - if (!ssl_errbuf->empty()) - { - ssl_errbuf->append(", "); - } - ssl_errbuf->append(ERR_error_string(err, errbuf)); - } - - return ssl_errbuf->c_str(); -} - -bool SSL_LISTENER_init(SSL_LISTENER* ssl) -{ - mxb_assert(!ssl->ssl_init_done); - bool rval = true; - - switch (ssl->ssl_method_type) - { -#ifndef OPENSSL_1_1 - case SERVICE_TLS10: - ssl->method = (SSL_METHOD*)TLSv1_method(); - break; - -#endif -#ifdef OPENSSL_1_0 - case SERVICE_TLS11: - ssl->method = (SSL_METHOD*)TLSv1_1_method(); - break; - - case SERVICE_TLS12: - ssl->method = (SSL_METHOD*)TLSv1_2_method(); - break; - -#endif - /** Rest of these use the maximum available SSL/TLS methods */ - case SERVICE_SSL_MAX: - ssl->method = (SSL_METHOD*)SSLv23_method(); - break; - - case SERVICE_TLS_MAX: - ssl->method = (SSL_METHOD*)SSLv23_method(); - break; - - case SERVICE_SSL_TLS_MAX: - ssl->method = (SSL_METHOD*)SSLv23_method(); - break; - - default: - ssl->method = (SSL_METHOD*)SSLv23_method(); - break; - } - - SSL_CTX* ctx = SSL_CTX_new(ssl->method); - - if (ctx == NULL) - { - MXS_ERROR("SSL context initialization failed: %s", get_ssl_errors()); - return false; - } - - SSL_CTX_set_default_read_ahead(ctx, 0); - - /** Enable all OpenSSL bug fixes */ - SSL_CTX_set_options(ctx, SSL_OP_ALL); - - /** Disable SSLv3 */ - SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); - - // Disable session cache - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); - - // - // Note: This is not safe if SSL initialization is done concurrently - // - /** Generate the 512-bit and 1024-bit RSA keys */ - if (rsa_512 == NULL && (rsa_512 = create_rsa(512)) == NULL) - { - MXS_ERROR("512-bit RSA key generation failed."); - rval = false; - } - else if (rsa_1024 == NULL && (rsa_1024 = create_rsa(1024)) == NULL) - { - MXS_ERROR("1024-bit RSA key generation failed."); - rval = false; - } - else - { - mxb_assert(rsa_512 && rsa_1024); - SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_callback); - } - - mxb_assert(ssl->ssl_ca_cert); - - /* Load the CA certificate into the SSL_CTX structure */ - if (!SSL_CTX_load_verify_locations(ctx, ssl->ssl_ca_cert, NULL)) - { - MXS_ERROR("Failed to set Certificate Authority file"); - rval = false; - } - - if (ssl->ssl_cert && ssl->ssl_key) - { - /** Load the server certificate */ - if (SSL_CTX_use_certificate_chain_file(ctx, ssl->ssl_cert) <= 0) - { - MXS_ERROR("Failed to set server SSL certificate: %s", get_ssl_errors()); - rval = false; - } - - /* Load the private-key corresponding to the server certificate */ - if (SSL_CTX_use_PrivateKey_file(ctx, ssl->ssl_key, SSL_FILETYPE_PEM) <= 0) - { - MXS_ERROR("Failed to set server SSL key: %s", get_ssl_errors()); - rval = false; - } - - /* Check if the server certificate and private-key matches */ - if (!SSL_CTX_check_private_key(ctx)) - { - MXS_ERROR("Server SSL certificate and key do not match: %s", get_ssl_errors()); - rval = false; - } - } - - /* Set to require peer (client) certificate verification */ - if (ssl->ssl_verify_peer_certificate) - { - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); - } - - /* Set the verification depth */ - SSL_CTX_set_verify_depth(ctx, ssl->ssl_cert_verify_depth); - - if (rval) - { - ssl->ssl_init_done = true; - ssl->ctx = ctx; - } - else - { - SSL_CTX_free(ctx); - } - - return rval; -} - -void SSL_LISTENER_free(SSL_LISTENER* ssl) -{ - if (ssl) - { - SSL_CTX_free(ssl->ctx); - MXS_FREE(ssl->ssl_ca_cert); - MXS_FREE(ssl->ssl_cert); - MXS_FREE(ssl->ssl_key); - MXS_FREE(ssl); - } -} - -/** - * The RSA key generation callback function for OpenSSL. - * @param s SSL structure - * @param is_export Not used - * @param keylength Length of the key - * @return Pointer to RSA structure - */ -static RSA* tmp_rsa_callback(SSL* s, int is_export, int keylength) -{ - RSA* rsa_tmp = NULL; - - switch (keylength) - { - case 512: - if (rsa_512) - { - rsa_tmp = rsa_512; - } - else - { - /* generate on the fly, should not happen in this example */ - rsa_tmp = create_rsa(keylength); - rsa_512 = rsa_tmp; /* Remember for later reuse */ - } - break; - - case 1024: - if (rsa_1024) - { - rsa_tmp = rsa_1024; - } - break; - - default: - /* Generating a key on the fly is very costly, so use what is there */ - if (rsa_1024) - { - rsa_tmp = rsa_1024; - } - else - { - rsa_tmp = rsa_512; /* Use at least a shorter key */ - } - } - return rsa_tmp; -} - /** * Creates a listener configuration at the location pointed by @c filename * diff --git a/server/core/ssl.cc b/server/core/ssl.cc index b36521bf3..94ec7f9f4 100644 --- a/server/core/ssl.cc +++ b/server/core/ssl.cc @@ -34,6 +34,10 @@ #include #include +static RSA* rsa_512 = NULL; +static RSA* rsa_1024 = NULL; +static RSA* tmp_rsa_callback(SSL* s, int is_export, int keylength); + /** * @brief Check client's SSL capability and start SSL if appropriate. * @@ -315,3 +319,279 @@ int ssl_authenticate_check_status(DCB* dcb) } return rval; } + +int listener_set_ssl_version(SSL_LISTENER* ssl_listener, const char* version) +{ + if (strcasecmp(version, "MAX") == 0) + { + ssl_listener->ssl_method_type = SERVICE_SSL_TLS_MAX; + } +#ifndef OPENSSL_1_1 + else if (strcasecmp(version, "TLSV10") == 0) + { + ssl_listener->ssl_method_type = SERVICE_TLS10; + } +#else +#endif +#ifdef OPENSSL_1_0 + else if (strcasecmp(version, "TLSV11") == 0) + { + ssl_listener->ssl_method_type = SERVICE_TLS11; + } + else if (strcasecmp(version, "TLSV12") == 0) + { + ssl_listener->ssl_method_type = SERVICE_TLS12; + } +#endif + else + { + return -1; + } + return 0; +} + +void listener_set_certificates(SSL_LISTENER* ssl_listener, const std::string& cert, + const std::string& key, const std::string& ca_cert) +{ + MXS_FREE(ssl_listener->ssl_cert); + ssl_listener->ssl_cert = !cert.empty() ? MXS_STRDUP_A(cert.c_str()) : nullptr; + + MXS_FREE(ssl_listener->ssl_key); + ssl_listener->ssl_key = !key.empty() ? MXS_STRDUP_A(key.c_str()) : nullptr; + + MXS_FREE(ssl_listener->ssl_ca_cert); + ssl_listener->ssl_ca_cert = !ca_cert.empty() ? MXS_STRDUP_A(ca_cert.c_str()) : nullptr; +} + +RSA* create_rsa(int bits) +{ +#ifdef OPENSSL_1_1 + BIGNUM* bn = BN_new(); + BN_set_word(bn, RSA_F4); + RSA* rsa = RSA_new(); + RSA_generate_key_ex(rsa, bits, bn, NULL); + BN_free(bn); + return rsa; +#else + return RSA_generate_key(bits, RSA_F4, NULL, NULL); +#endif +} + +// thread-local non-POD types are not supported with older versions of GCC +static thread_local std::string* ssl_errbuf; + +static const char* get_ssl_errors() +{ + if (ssl_errbuf == NULL) + { + ssl_errbuf = new std::string; + } + + char errbuf[200]; // Enough space according to OpenSSL documentation + ssl_errbuf->clear(); + + for (int err = ERR_get_error(); err; err = ERR_get_error()) + { + if (!ssl_errbuf->empty()) + { + ssl_errbuf->append(", "); + } + ssl_errbuf->append(ERR_error_string(err, errbuf)); + } + + return ssl_errbuf->c_str(); +} + +/** + * The RSA key generation callback function for OpenSSL. + * @param s SSL structure + * @param is_export Not used + * @param keylength Length of the key + * @return Pointer to RSA structure + */ +static RSA* tmp_rsa_callback(SSL* s, int is_export, int keylength) +{ + RSA* rsa_tmp = NULL; + + switch (keylength) + { + case 512: + if (rsa_512) + { + rsa_tmp = rsa_512; + } + else + { + /* generate on the fly, should not happen in this example */ + rsa_tmp = create_rsa(keylength); + rsa_512 = rsa_tmp; /* Remember for later reuse */ + } + break; + + case 1024: + if (rsa_1024) + { + rsa_tmp = rsa_1024; + } + break; + + default: + /* Generating a key on the fly is very costly, so use what is there */ + if (rsa_1024) + { + rsa_tmp = rsa_1024; + } + else + { + rsa_tmp = rsa_512; /* Use at least a shorter key */ + } + } + return rsa_tmp; +} + +bool SSL_LISTENER_init(SSL_LISTENER* ssl) +{ + mxb_assert(!ssl->ssl_init_done); + bool rval = true; + + switch (ssl->ssl_method_type) + { +#ifndef OPENSSL_1_1 + case SERVICE_TLS10: + ssl->method = (SSL_METHOD*)TLSv1_method(); + break; + +#endif +#ifdef OPENSSL_1_0 + case SERVICE_TLS11: + ssl->method = (SSL_METHOD*)TLSv1_1_method(); + break; + + case SERVICE_TLS12: + ssl->method = (SSL_METHOD*)TLSv1_2_method(); + break; + +#endif + /** Rest of these use the maximum available SSL/TLS methods */ + case SERVICE_SSL_MAX: + ssl->method = (SSL_METHOD*)SSLv23_method(); + break; + + case SERVICE_TLS_MAX: + ssl->method = (SSL_METHOD*)SSLv23_method(); + break; + + case SERVICE_SSL_TLS_MAX: + ssl->method = (SSL_METHOD*)SSLv23_method(); + break; + + default: + ssl->method = (SSL_METHOD*)SSLv23_method(); + break; + } + + SSL_CTX* ctx = SSL_CTX_new(ssl->method); + + if (ctx == NULL) + { + MXS_ERROR("SSL context initialization failed: %s", get_ssl_errors()); + return false; + } + + SSL_CTX_set_default_read_ahead(ctx, 0); + + /** Enable all OpenSSL bug fixes */ + SSL_CTX_set_options(ctx, SSL_OP_ALL); + + /** Disable SSLv3 */ + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); + + // Disable session cache + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + + // + // Note: This is not safe if SSL initialization is done concurrently + // + /** Generate the 512-bit and 1024-bit RSA keys */ + if (rsa_512 == NULL && (rsa_512 = create_rsa(512)) == NULL) + { + MXS_ERROR("512-bit RSA key generation failed."); + rval = false; + } + else if (rsa_1024 == NULL && (rsa_1024 = create_rsa(1024)) == NULL) + { + MXS_ERROR("1024-bit RSA key generation failed."); + rval = false; + } + else + { + mxb_assert(rsa_512 && rsa_1024); + SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_callback); + } + + mxb_assert(ssl->ssl_ca_cert); + + /* Load the CA certificate into the SSL_CTX structure */ + if (!SSL_CTX_load_verify_locations(ctx, ssl->ssl_ca_cert, NULL)) + { + MXS_ERROR("Failed to set Certificate Authority file"); + rval = false; + } + + if (ssl->ssl_cert && ssl->ssl_key) + { + /** Load the server certificate */ + if (SSL_CTX_use_certificate_chain_file(ctx, ssl->ssl_cert) <= 0) + { + MXS_ERROR("Failed to set server SSL certificate: %s", get_ssl_errors()); + rval = false; + } + + /* Load the private-key corresponding to the server certificate */ + if (SSL_CTX_use_PrivateKey_file(ctx, ssl->ssl_key, SSL_FILETYPE_PEM) <= 0) + { + MXS_ERROR("Failed to set server SSL key: %s", get_ssl_errors()); + rval = false; + } + + /* Check if the server certificate and private-key matches */ + if (!SSL_CTX_check_private_key(ctx)) + { + MXS_ERROR("Server SSL certificate and key do not match: %s", get_ssl_errors()); + rval = false; + } + } + + /* Set to require peer (client) certificate verification */ + if (ssl->ssl_verify_peer_certificate) + { + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); + } + + /* Set the verification depth */ + SSL_CTX_set_verify_depth(ctx, ssl->ssl_cert_verify_depth); + + if (rval) + { + ssl->ssl_init_done = true; + ssl->ctx = ctx; + } + else + { + SSL_CTX_free(ctx); + } + + return rval; +} + +void SSL_LISTENER_free(SSL_LISTENER* ssl) +{ + if (ssl) + { + SSL_CTX_free(ssl->ctx); + MXS_FREE(ssl->ssl_ca_cert); + MXS_FREE(ssl->ssl_cert); + MXS_FREE(ssl->ssl_key); + MXS_FREE(ssl); + } +}