| 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 | |