1#include "MySQLHandlerFactory.h"
2#include <Common/OpenSSLHelpers.h>
3#include <Poco/Net/TCPServerConnectionFactory.h>
4#include <Poco/Util/Application.h>
5#include <common/logger_useful.h>
6#include <ext/scope_guard.h>
7#include "IServer.h"
8#include "MySQLHandler.h"
9
10#if USE_POCO_NETSSL
11#include <Poco/Net/SSLManager.h>
12#endif
13
14namespace DB
15{
16
17namespace ErrorCodes
18{
19 extern const int CANNOT_OPEN_FILE;
20 extern const int NO_ELEMENTS_IN_CONFIG;
21 extern const int OPENSSL_ERROR;
22 extern const int SYSTEM_ERROR;
23}
24
25MySQLHandlerFactory::MySQLHandlerFactory(IServer & server_)
26 : server(server_)
27 , log(&Logger::get("MySQLHandlerFactory"))
28{
29
30#if USE_POCO_NETSSL
31 try
32 {
33 Poco::Net::SSLManager::instance().defaultServerContext();
34 }
35 catch (...)
36 {
37 LOG_INFO(log, "Failed to create SSL context. SSL will be disabled. Error: " << getCurrentExceptionMessage(false));
38 ssl_enabled = false;
39 }
40#endif
41
42#if USE_SSL
43 /// Reading rsa keys for SHA256 authentication plugin.
44 try
45 {
46 readRSAKeys();
47 }
48 catch (...)
49 {
50 LOG_WARNING(log, "Failed to read RSA keys. Error: " << getCurrentExceptionMessage(false));
51 generateRSAKeys();
52 }
53#endif
54}
55
56#if USE_SSL
57void MySQLHandlerFactory::readRSAKeys()
58{
59 const Poco::Util::LayeredConfiguration & config = Poco::Util::Application::instance().config();
60 String certificateFileProperty = "openSSL.server.certificateFile";
61 String privateKeyFileProperty = "openSSL.server.privateKeyFile";
62
63 if (!config.has(certificateFileProperty))
64 throw Exception("Certificate file is not set.", ErrorCodes::NO_ELEMENTS_IN_CONFIG);
65
66 if (!config.has(privateKeyFileProperty))
67 throw Exception("Private key file is not set.", ErrorCodes::NO_ELEMENTS_IN_CONFIG);
68
69 {
70 String certificateFile = config.getString(certificateFileProperty);
71 FILE * fp = fopen(certificateFile.data(), "r");
72 if (fp == nullptr)
73 throw Exception("Cannot open certificate file: " + certificateFile + ".", ErrorCodes::CANNOT_OPEN_FILE);
74 SCOPE_EXIT(fclose(fp));
75
76 X509 * x509 = PEM_read_X509(fp, nullptr, nullptr, nullptr);
77 SCOPE_EXIT(X509_free(x509));
78 if (x509 == nullptr)
79 throw Exception("Failed to read PEM certificate from " + certificateFile + ". Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
80
81 EVP_PKEY * p = X509_get_pubkey(x509);
82 if (p == nullptr)
83 throw Exception("Failed to get RSA key from X509. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
84 SCOPE_EXIT(EVP_PKEY_free(p));
85
86 public_key.reset(EVP_PKEY_get1_RSA(p));
87 if (public_key.get() == nullptr)
88 throw Exception("Failed to get RSA key from ENV_PKEY. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
89 }
90
91 {
92 String privateKeyFile = config.getString(privateKeyFileProperty);
93
94 FILE * fp = fopen(privateKeyFile.data(), "r");
95 if (fp == nullptr)
96 throw Exception ("Cannot open private key file " + privateKeyFile + ".", ErrorCodes::CANNOT_OPEN_FILE);
97 SCOPE_EXIT(fclose(fp));
98
99 private_key.reset(PEM_read_RSAPrivateKey(fp, nullptr, nullptr, nullptr));
100 if (!private_key)
101 throw Exception("Failed to read RSA private key from " + privateKeyFile + ". Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
102 }
103}
104
105void MySQLHandlerFactory::generateRSAKeys()
106{
107 LOG_INFO(log, "Generating new RSA key.");
108 public_key.reset(RSA_new());
109 if (!public_key)
110 throw Exception("Failed to allocate RSA key. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
111
112 BIGNUM * e = BN_new();
113 if (!e)
114 throw Exception("Failed to allocate BIGNUM. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
115 SCOPE_EXIT(BN_free(e));
116
117 if (!BN_set_word(e, 65537) || !RSA_generate_key_ex(public_key.get(), 2048, e, nullptr))
118 throw Exception("Failed to generate RSA key. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
119
120 private_key.reset(RSAPrivateKey_dup(public_key.get()));
121 if (!private_key)
122 throw Exception("Failed to copy RSA key. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
123}
124#endif
125
126Poco::Net::TCPServerConnection * MySQLHandlerFactory::createConnection(const Poco::Net::StreamSocket & socket)
127{
128 size_t connection_id = last_connection_id++;
129 LOG_TRACE(log, "MySQL connection. Id: " << connection_id << ". Address: " << socket.peerAddress().toString());
130#if USE_POCO_NETSSL && USE_SSL
131 return new MySQLHandlerSSL(server, socket, ssl_enabled, connection_id, *public_key, *private_key);
132#else
133 return new MySQLHandler(server, socket, ssl_enabled, connection_id);
134#endif
135
136}
137
138}
139