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 | |
14 | namespace DB |
15 | { |
16 | |
17 | namespace 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 | |
25 | MySQLHandlerFactory::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 |
57 | void 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 | |
105 | void 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 | |
126 | Poco::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 | |