aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Server/MySQLHandlerFactory.cpp
blob: deadb10f9a95fa389dc663ccc9e6aff26f75b1fc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "MySQLHandlerFactory.h"
#include <Common/OpenSSLHelpers.h>
#include <Poco/Net/TCPServerConnectionFactory.h>
#include <Poco/Util/Application.h>
#include <Common/logger_useful.h>
#include <base/scope_guard.h>
#include <Server/MySQLHandler.h>

#if USE_SSL
#    include <Poco/Net/SSLManager.h>
#endif

namespace DB
{

namespace ErrorCodes
{
    extern const int CANNOT_OPEN_FILE;
    extern const int CANNOT_CLOSE_FILE;
    extern const int NO_ELEMENTS_IN_CONFIG;
    extern const int OPENSSL_ERROR;
}

MySQLHandlerFactory::MySQLHandlerFactory(IServer & server_)
    : server(server_)
    , log(&Poco::Logger::get("MySQLHandlerFactory"))
{
#if USE_SSL
    try
    {
        Poco::Net::SSLManager::instance().defaultServerContext();
    }
    catch (...)
    {
        LOG_TRACE(log, "Failed to create SSL context. SSL will be disabled. Error: {}", getCurrentExceptionMessage(false));
        ssl_enabled = false;
    }

    /// Reading rsa keys for SHA256 authentication plugin.
    try
    {
        readRSAKeys();
    }
    catch (...)
    {
        LOG_TRACE(log, "Failed to read RSA key pair from server certificate. Error: {}", getCurrentExceptionMessage(false));
        generateRSAKeys();
    }
#endif
}

#if USE_SSL
void MySQLHandlerFactory::readRSAKeys()
{
    const Poco::Util::LayeredConfiguration & config = Poco::Util::Application::instance().config();
    String certificate_file_property = "openSSL.server.certificateFile";
    String private_key_file_property = "openSSL.server.privateKeyFile";

    if (!config.has(certificate_file_property))
        throw Exception(ErrorCodes::NO_ELEMENTS_IN_CONFIG, "Certificate file is not set.");

    if (!config.has(private_key_file_property))
        throw Exception(ErrorCodes::NO_ELEMENTS_IN_CONFIG, "Private key file is not set.");

    {
        String certificate_file = config.getString(certificate_file_property);
        FILE * fp = fopen(certificate_file.data(), "r");
        if (fp == nullptr)
            throw Exception(ErrorCodes::CANNOT_OPEN_FILE, "Cannot open certificate file: {}.", certificate_file);
        SCOPE_EXIT(
            if (0 != fclose(fp))
                throwFromErrno("Cannot close file with the certificate in MySQLHandlerFactory", ErrorCodes::CANNOT_CLOSE_FILE);
        );

        X509 * x509 = PEM_read_X509(fp, nullptr, nullptr, nullptr);
        SCOPE_EXIT(X509_free(x509));
        if (x509 == nullptr)
            throw Exception(ErrorCodes::OPENSSL_ERROR, "Failed to read PEM certificate from {}. Error: {}", certificate_file, getOpenSSLErrors());

        EVP_PKEY * p = X509_get_pubkey(x509);
        if (p == nullptr)
            throw Exception(ErrorCodes::OPENSSL_ERROR, "Failed to get RSA key from X509. Error: {}", getOpenSSLErrors());
        SCOPE_EXIT(EVP_PKEY_free(p));

        public_key.reset(EVP_PKEY_get1_RSA(p));
        if (public_key.get() == nullptr)
            throw Exception(ErrorCodes::OPENSSL_ERROR, "Failed to get RSA key from ENV_PKEY. Error: {}", getOpenSSLErrors());
    }

    {
        String private_key_file = config.getString(private_key_file_property);

        FILE * fp = fopen(private_key_file.data(), "r");
        if (fp == nullptr)
            throw Exception(ErrorCodes::CANNOT_OPEN_FILE, "Cannot open private key file {}.", private_key_file);
        SCOPE_EXIT(
            if (0 != fclose(fp))
                throwFromErrno("Cannot close file with the certificate in MySQLHandlerFactory", ErrorCodes::CANNOT_CLOSE_FILE);
        );

        private_key.reset(PEM_read_RSAPrivateKey(fp, nullptr, nullptr, nullptr));
        if (!private_key)
            throw Exception(ErrorCodes::OPENSSL_ERROR, "Failed to read RSA private key from {}. Error: {}", private_key_file, getOpenSSLErrors());
    }
}

void MySQLHandlerFactory::generateRSAKeys()
{
    LOG_TRACE(log, "Generating new RSA key pair.");
    public_key.reset(RSA_new());
    if (!public_key)
        throw Exception(ErrorCodes::OPENSSL_ERROR, "Failed to allocate RSA key. Error: {}", getOpenSSLErrors());

    BIGNUM * e = BN_new();
    if (!e)
        throw Exception(ErrorCodes::OPENSSL_ERROR, "Failed to allocate BIGNUM. Error: {}", getOpenSSLErrors());
    SCOPE_EXIT(BN_free(e));

    if (!BN_set_word(e, 65537) || !RSA_generate_key_ex(public_key.get(), 2048, e, nullptr))
        throw Exception(ErrorCodes::OPENSSL_ERROR, "Failed to generate RSA key. Error: {}", getOpenSSLErrors());

    private_key.reset(RSAPrivateKey_dup(public_key.get()));
    if (!private_key)
        throw Exception(ErrorCodes::OPENSSL_ERROR, "Failed to copy RSA key. Error: {}", getOpenSSLErrors());
}
#endif

Poco::Net::TCPServerConnection * MySQLHandlerFactory::createConnection(const Poco::Net::StreamSocket & socket, TCPServer & tcp_server)
{
    uint32_t connection_id = last_connection_id++;
    LOG_TRACE(log, "MySQL connection. Id: {}. Address: {}", connection_id, socket.peerAddress().toString());
#if USE_SSL
    return new MySQLHandlerSSL(server, tcp_server, socket, ssl_enabled, connection_id, *public_key, *private_key);
#else
    return new MySQLHandler(server, tcp_server, socket, ssl_enabled, connection_id);
#endif

}

}