2019-10-05 19:25:31 +00:00
|
|
|
#include "MySQLHandlerFactory.h"
|
2019-05-26 06:52:29 +00:00
|
|
|
#include <Common/OpenSSLHelpers.h>
|
|
|
|
#include <Poco/Net/TCPServerConnectionFactory.h>
|
|
|
|
#include <Poco/Util/Application.h>
|
|
|
|
#include <common/logger_useful.h>
|
|
|
|
#include <ext/scope_guard.h>
|
|
|
|
#include "IServer.h"
|
|
|
|
#include "MySQLHandler.h"
|
|
|
|
|
2019-11-02 10:20:46 +00:00
|
|
|
#if USE_POCO_NETSSL
|
|
|
|
#include <Poco/Net/SSLManager.h>
|
|
|
|
#endif
|
|
|
|
|
2019-05-26 06:52:29 +00:00
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int CANNOT_OPEN_FILE;
|
|
|
|
extern const int NO_ELEMENTS_IN_CONFIG;
|
|
|
|
extern const int OPENSSL_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
MySQLHandlerFactory::MySQLHandlerFactory(IServer & server_)
|
|
|
|
: server(server_)
|
|
|
|
, log(&Logger::get("MySQLHandlerFactory"))
|
|
|
|
{
|
2019-11-02 10:20:46 +00:00
|
|
|
|
|
|
|
#if USE_POCO_NETSSL
|
2019-05-26 06:52:29 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
Poco::Net::SSLManager::instance().defaultServerContext();
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
2020-01-19 00:34:00 +00:00
|
|
|
LOG_TRACE(log, "Failed to create SSL context. SSL will be disabled. Error: " << getCurrentExceptionMessage(false));
|
2019-05-26 06:52:29 +00:00
|
|
|
ssl_enabled = false;
|
|
|
|
}
|
2019-11-02 10:20:46 +00:00
|
|
|
#endif
|
2019-05-26 06:52:29 +00:00
|
|
|
|
2019-11-02 10:20:46 +00:00
|
|
|
#if USE_SSL
|
2019-05-26 06:52:29 +00:00
|
|
|
/// Reading rsa keys for SHA256 authentication plugin.
|
|
|
|
try
|
|
|
|
{
|
|
|
|
readRSAKeys();
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
2020-01-19 00:34:00 +00:00
|
|
|
LOG_TRACE(log, "Failed to read RSA key pair from server certificate. Error: " << getCurrentExceptionMessage(false));
|
2019-05-26 06:52:29 +00:00
|
|
|
generateRSAKeys();
|
|
|
|
}
|
2019-11-02 10:20:46 +00:00
|
|
|
#endif
|
2019-05-26 06:52:29 +00:00
|
|
|
}
|
|
|
|
|
2019-11-02 10:20:46 +00:00
|
|
|
#if USE_SSL
|
2019-05-26 06:52:29 +00:00
|
|
|
void MySQLHandlerFactory::readRSAKeys()
|
|
|
|
{
|
|
|
|
const Poco::Util::LayeredConfiguration & config = Poco::Util::Application::instance().config();
|
2020-03-23 02:12:31 +00:00
|
|
|
String certificate_file_property = "openSSL.server.certificateFile";
|
|
|
|
String private_key_file_property = "openSSL.server.privateKeyFile";
|
2019-05-26 06:52:29 +00:00
|
|
|
|
2020-03-23 02:12:31 +00:00
|
|
|
if (!config.has(certificate_file_property))
|
2019-05-26 06:52:29 +00:00
|
|
|
throw Exception("Certificate file is not set.", ErrorCodes::NO_ELEMENTS_IN_CONFIG);
|
|
|
|
|
2020-03-23 02:12:31 +00:00
|
|
|
if (!config.has(private_key_file_property))
|
2019-05-26 06:52:29 +00:00
|
|
|
throw Exception("Private key file is not set.", ErrorCodes::NO_ELEMENTS_IN_CONFIG);
|
|
|
|
|
|
|
|
{
|
2020-03-23 02:12:31 +00:00
|
|
|
String certificate_file = config.getString(certificate_file_property);
|
|
|
|
FILE * fp = fopen(certificate_file.data(), "r");
|
2019-05-26 06:52:29 +00:00
|
|
|
if (fp == nullptr)
|
2020-03-23 02:12:31 +00:00
|
|
|
throw Exception("Cannot open certificate file: " + certificate_file + ".", ErrorCodes::CANNOT_OPEN_FILE);
|
2019-05-26 06:52:29 +00:00
|
|
|
SCOPE_EXIT(fclose(fp));
|
|
|
|
|
|
|
|
X509 * x509 = PEM_read_X509(fp, nullptr, nullptr, nullptr);
|
|
|
|
SCOPE_EXIT(X509_free(x509));
|
|
|
|
if (x509 == nullptr)
|
2020-03-23 02:12:31 +00:00
|
|
|
throw Exception("Failed to read PEM certificate from " + certificate_file + ". Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
|
2019-05-26 06:52:29 +00:00
|
|
|
|
|
|
|
EVP_PKEY * p = X509_get_pubkey(x509);
|
|
|
|
if (p == nullptr)
|
|
|
|
throw Exception("Failed to get RSA key from X509. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
|
|
|
|
SCOPE_EXIT(EVP_PKEY_free(p));
|
|
|
|
|
|
|
|
public_key.reset(EVP_PKEY_get1_RSA(p));
|
|
|
|
if (public_key.get() == nullptr)
|
|
|
|
throw Exception("Failed to get RSA key from ENV_PKEY. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2020-03-23 02:12:31 +00:00
|
|
|
String private_key_file = config.getString(private_key_file_property);
|
2019-05-26 06:52:29 +00:00
|
|
|
|
2020-03-23 02:12:31 +00:00
|
|
|
FILE * fp = fopen(private_key_file.data(), "r");
|
2019-05-26 06:52:29 +00:00
|
|
|
if (fp == nullptr)
|
2020-03-23 02:12:31 +00:00
|
|
|
throw Exception ("Cannot open private key file " + private_key_file + ".", ErrorCodes::CANNOT_OPEN_FILE);
|
2019-05-26 06:52:29 +00:00
|
|
|
SCOPE_EXIT(fclose(fp));
|
|
|
|
|
|
|
|
private_key.reset(PEM_read_RSAPrivateKey(fp, nullptr, nullptr, nullptr));
|
2019-05-26 19:30:23 +00:00
|
|
|
if (!private_key)
|
2020-03-23 02:12:31 +00:00
|
|
|
throw Exception("Failed to read RSA private key from " + private_key_file + ". Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
|
2019-05-26 06:52:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MySQLHandlerFactory::generateRSAKeys()
|
|
|
|
{
|
2020-01-19 00:34:00 +00:00
|
|
|
LOG_TRACE(log, "Generating new RSA key pair.");
|
2019-05-26 19:30:23 +00:00
|
|
|
public_key.reset(RSA_new());
|
|
|
|
if (!public_key)
|
2019-05-26 06:52:29 +00:00
|
|
|
throw Exception("Failed to allocate RSA key. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
|
|
|
|
|
|
|
|
BIGNUM * e = BN_new();
|
|
|
|
if (!e)
|
|
|
|
throw Exception("Failed to allocate BIGNUM. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
|
|
|
|
SCOPE_EXIT(BN_free(e));
|
|
|
|
|
2019-05-26 19:30:23 +00:00
|
|
|
if (!BN_set_word(e, 65537) || !RSA_generate_key_ex(public_key.get(), 2048, e, nullptr))
|
2019-05-26 06:52:29 +00:00
|
|
|
throw Exception("Failed to generate RSA key. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
|
|
|
|
|
|
|
|
private_key.reset(RSAPrivateKey_dup(public_key.get()));
|
|
|
|
if (!private_key)
|
|
|
|
throw Exception("Failed to copy RSA key. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
|
|
|
|
}
|
2019-11-02 10:20:46 +00:00
|
|
|
#endif
|
2019-05-26 06:52:29 +00:00
|
|
|
|
|
|
|
Poco::Net::TCPServerConnection * MySQLHandlerFactory::createConnection(const Poco::Net::StreamSocket & socket)
|
|
|
|
{
|
|
|
|
size_t connection_id = last_connection_id++;
|
|
|
|
LOG_TRACE(log, "MySQL connection. Id: " << connection_id << ". Address: " << socket.peerAddress().toString());
|
2019-11-02 10:20:46 +00:00
|
|
|
#if USE_POCO_NETSSL && USE_SSL
|
|
|
|
return new MySQLHandlerSSL(server, socket, ssl_enabled, connection_id, *public_key, *private_key);
|
|
|
|
#else
|
|
|
|
return new MySQLHandler(server, socket, ssl_enabled, connection_id);
|
|
|
|
#endif
|
|
|
|
|
2019-05-26 06:52:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|