Skip to content

Commit

Permalink
Ssl options (#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
proller committed Dec 27, 2018
1 parent 4ffbe84 commit 1676610
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 74 deletions.
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -97,13 +97,19 @@ Driver = $(PATH_OF_CLICKHOUSE_ODBC_SO)
#url = https://default:password@localhost:8443/query?database=default&max_result_bytes=4000000&buffer_size=3000000
# Minimal (will connect to port 8443 if https:// or 8123 if http:// ):
url = https://localhost
# Old way:
#server = localhost
#password = 123456
#database = default
#uid = default
#port = 8123
# sslmode variants: allow - ignore self-signed and bad certificates; require - check certificates (and fail connection if something wrong)
#sslmode = require
#privatekeyfile =
#certificatefile =
#calocation =
#trace=1
#tracefile=/tmp/chlickhouse-odbc.log
Expand Down
16 changes: 7 additions & 9 deletions driver/config.cpp
Expand Up @@ -4,10 +4,8 @@
#include <string.h>
#include "utils.h"

ConnInfo::ConnInfo()
{
#define ZERO_FIELD(name) \
memset(name, 0, sizeof(name));
ConnInfo::ConnInfo() {
#define ZERO_FIELD(name) memset(name, 0, sizeof(name));

ZERO_FIELD(dsn);
ZERO_FIELD(desc);
Expand All @@ -32,10 +30,11 @@ ConnInfo::ConnInfo()
#undef ZERO_FIELD
}

void getDSNinfo(ConnInfo * ci, bool overwrite)
{
#define GET_CONFIG(NAME, INI_NAME, DEFAULT) if (ci->NAME[0] == '\0' || overwrite) \
FUNCTION_MAYBE_W(SQLGetPrivateProfileString)(reinterpret_cast<LPTSTR>(ci->dsn), INI_NAME, TEXT(DEFAULT), reinterpret_cast<LPTSTR>(ci->NAME), sizeof(ci->NAME), ODBC_INI);
void getDSNinfo(ConnInfo * ci, bool overwrite) {
#define GET_CONFIG(NAME, INI_NAME, DEFAULT) \
if (ci->NAME[0] == '\0' || overwrite) \
FUNCTION_MAYBE_W(SQLGetPrivateProfileString) \
(reinterpret_cast<LPTSTR>(ci->dsn), INI_NAME, TEXT(DEFAULT), reinterpret_cast<LPTSTR>(ci->NAME), sizeof(ci->NAME), ODBC_INI);

GET_CONFIG(desc, INI_KDESC, "");
GET_CONFIG(url, INI_URL, "");
Expand All @@ -52,5 +51,4 @@ void getDSNinfo(ConnInfo * ci, bool overwrite)
GET_CONFIG(tracefile, INI_TRACEFILE, LOG_DEFAULT_FILE);

#undef GET_CONFIG

}
47 changes: 25 additions & 22 deletions driver/config.h
@@ -1,33 +1,36 @@
#pragma once

#include "platform.h"
#include "ini_defines.h"
#include "platform.h"

/**
* Structure to hold all the connection attributes for a specific
* connection (used for both registry and file, DSN and DRIVER)
*/
struct ConnInfo
{
MYTCHAR dsn[MEDIUM_REGISTRY_LEN];
MYTCHAR desc[MEDIUM_REGISTRY_LEN];
MYTCHAR drivername[MEDIUM_REGISTRY_LEN];
MYTCHAR url[LARGE_REGISTRY_LEN];
MYTCHAR server[MEDIUM_REGISTRY_LEN];
MYTCHAR database[MEDIUM_REGISTRY_LEN];
MYTCHAR username[MEDIUM_REGISTRY_LEN];
MYTCHAR password[MEDIUM_REGISTRY_LEN];
MYTCHAR port[SMALL_REGISTRY_LEN];
MYTCHAR sslmode[16];
MYTCHAR onlyread[SMALL_REGISTRY_LEN];
MYTCHAR timeout[SMALL_REGISTRY_LEN];
MYTCHAR stringmaxlength[SMALL_REGISTRY_LEN];
MYTCHAR show_system_tables[SMALL_REGISTRY_LEN];
MYTCHAR translation_dll[MEDIUM_REGISTRY_LEN];
MYTCHAR translation_option[SMALL_REGISTRY_LEN];
MYTCHAR conn_settings[MEDIUM_REGISTRY_LEN];
MYTCHAR trace[SMALL_REGISTRY_LEN];
MYTCHAR tracefile[MEDIUM_REGISTRY_LEN];
struct ConnInfo {
MYTCHAR dsn[MEDIUM_REGISTRY_LEN];
MYTCHAR desc[MEDIUM_REGISTRY_LEN];
MYTCHAR drivername[MEDIUM_REGISTRY_LEN];
MYTCHAR url[LARGE_REGISTRY_LEN];
MYTCHAR server[MEDIUM_REGISTRY_LEN];
MYTCHAR database[MEDIUM_REGISTRY_LEN];
MYTCHAR username[MEDIUM_REGISTRY_LEN];
MYTCHAR password[MEDIUM_REGISTRY_LEN];
MYTCHAR port[SMALL_REGISTRY_LEN];
MYTCHAR sslmode[16];
MYTCHAR onlyread[SMALL_REGISTRY_LEN];
MYTCHAR timeout[SMALL_REGISTRY_LEN];
MYTCHAR stringmaxlength[SMALL_REGISTRY_LEN];
MYTCHAR show_system_tables[SMALL_REGISTRY_LEN];
MYTCHAR translation_dll[MEDIUM_REGISTRY_LEN];
MYTCHAR translation_option[SMALL_REGISTRY_LEN];
MYTCHAR conn_settings[MEDIUM_REGISTRY_LEN];
MYTCHAR trace[SMALL_REGISTRY_LEN];
MYTCHAR tracefile[MEDIUM_REGISTRY_LEN];
MYTCHAR privateKeyFile[MEDIUM_REGISTRY_LEN];
MYTCHAR certificateFile[MEDIUM_REGISTRY_LEN];
MYTCHAR caLocation[MEDIUM_REGISTRY_LEN];

signed char disallow_premature = -1;
signed char allow_keyset = -1;
signed char updatable_cursors = 0;
Expand Down
106 changes: 67 additions & 39 deletions driver/connection.cpp
@@ -1,26 +1,57 @@
#include "connection.h"
#include <Poco/Net/HTTPClientSession.h>
#include <Poco/NumberParser.h> // TODO: switch to std
#include <Poco/URI.h>
#include "config.h"
#include "string_ref.h"
#include "utils.h"
#include <Poco/NumberParser.h> // TODO: switch to std
#include <Poco/URI.h>
#include <Poco/Net/HTTPClientSession.h>

//#if __has_include("config_cmake.h") // requre c++17

#if CMAKE_BUILD
#include "config_cmake.h"
# include "config_cmake.h"
#endif

#if USE_SSL
#include <Poco/Net/AcceptCertificateHandler.h>
#include <Poco/Net/HTTPSClientSession.h>
#include <Poco/Net/InvalidCertificateHandler.h>
#include <Poco/Net/PrivateKeyPassphraseHandler.h>
#include <Poco/Net/SSLManager.h>
# include <Poco/Net/AcceptCertificateHandler.h>
# include <Poco/Net/RejectCertificateHandler.h>

# include <Poco/Net/HTTPSClientSession.h>
# include <Poco/Net/InvalidCertificateHandler.h>
# include <Poco/Net/PrivateKeyPassphraseHandler.h>
# include <Poco/Net/SSLManager.h>
#endif


std::once_flag ssl_init_once;

void SSLInit(bool ssl_strict, const std::string & privateKeyFile, const std::string & certificateFile, const std::string & caLocation) {
// http://stackoverflow.com/questions/18315472/https-request-in-c-using-poco
#if USE_SSL
Poco::Net::initializeSSL();
Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> ptrHandler;
if (ssl_strict)
ptrHandler = new Poco::Net::RejectCertificateHandler(false);
else
ptrHandler = new Poco::Net::AcceptCertificateHandler(false);
Poco::Net::Context::Ptr ptrContext = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE,
privateKeyFile
# if !defined(SECURITY_WIN32)
// Do not work with poco/NetSSL_Win:
,
certificateFile,
caLocation,
ssl_strict ? Poco::Net::Context::VERIFY_STRICT : Poco::Net::Context::VERIFY_RELAXED,
9,
true,
"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"
# endif
);
Poco::Net::SSLManager::instance().initializeClient(0, ptrHandler, ptrContext);
#endif
}


Connection::Connection(Environment & env_) : environment(env_) {}

std::string Connection::connectionString() const {
Expand Down Expand Up @@ -57,7 +88,7 @@ void Connection::init() {
#if USE_SSL
bool is_ssl = proto == "https";

std::call_once(ssl_init_once, SSLInit);
std::call_once(ssl_init_once, SSLInit, ssl_strict, privateKeyFile, certificateFile, caLocation);
#endif

session = std::unique_ptr<Poco::Net::HTTPClientSession>(
Expand All @@ -69,7 +100,7 @@ void Connection::init() {
session->setHost(server);
session->setPort(port);
session->setKeepAlive(true);
session->setTimeout(Poco::Timespan(connection_timeout, 0), Poco::Timespan(timeout, 0),Poco::Timespan(timeout, 0) );
session->setTimeout(Poco::Timespan(connection_timeout, 0), Poco::Timespan(timeout, 0), Poco::Timespan(timeout, 0));
session->setKeepAliveTimeout(Poco::Timespan(86400, 0));
}

Expand Down Expand Up @@ -114,9 +145,11 @@ void Connection::init(const std::string & connection_string) {
password = current_value.toString();
else if (key_lower == "proto")
proto = current_value.toString();
else if (key_lower == "sslmode" && current_value == "require")
else if (key_lower == "sslmode" && (current_value == "allow" || current_value == "prefer" || current_value == "require")) {
proto = "https";
else if (key_lower == "url")
if (current_value == "require")
ssl_strict = true;
} else if (key_lower == "url")
url = current_value.toString();
else if (key_lower == "host" || key_lower == "server")
server = current_value.toString();
Expand All @@ -136,17 +169,21 @@ void Connection::init(const std::string & connection_string) {
else {
throw std::runtime_error("Cannot parse timeout.");
}
}
else if (key_lower == "stringmaxlength") {
} else if (key_lower == "stringmaxlength") {
int int_val = 0;
if (Poco::NumberParser::tryParse(current_value.toString(), int_val))
stringmaxlength = int_val;
else {
throw std::runtime_error("Cannot parse stringmaxlength.");
}
}
else if (key_lower == "dsn")
} else if (key_lower == "dsn")
data_source = current_value.toString();
else if (key_lower == "privatekeyfile")
privateKeyFile = current_value.toString();
else if (key_lower == "certificatefile")
certificateFile = current_value.toString();
else if (key_lower == "calocation")
caLocation = current_value.toString();
}

init();
Expand Down Expand Up @@ -205,8 +242,18 @@ void Connection::loadConfiguration() {
password = stringFromMYTCHAR(ci.password);
if (database.empty())
database = stringFromMYTCHAR(ci.database);
if (proto.empty() && (stringFromMYTCHAR(ci.sslmode) == "require" || port == 8443))
auto sslmode = stringFromMYTCHAR(ci.sslmode);
if (proto.empty() && (sslmode == "require" || sslmode == "prefer" || sslmode == "allow" || port == 8443))
proto = "https";
if (sslmode == "require")
ssl_strict = true;

if (privateKeyFile.empty())
privateKeyFile = stringFromMYTCHAR(ci.privateKeyFile);
if (certificateFile.empty())
certificateFile = stringFromMYTCHAR(ci.certificateFile);
if (caLocation.empty())
caLocation = stringFromMYTCHAR(ci.caLocation);
}

void Connection::setDefaults() {
Expand All @@ -230,9 +277,9 @@ void Connection::setDefaults() {
auto index = user_info.find(':');
if (index != std::string::npos) {
if (password.empty())
password = user_info.substr(index+1);
password = user_info.substr(index + 1);
if (user.empty())
user = user_info.substr(0,index);
user = user_info.substr(0, index);
}
}

Expand All @@ -257,22 +304,3 @@ void Connection::setDefaults() {
if (connection_timeout == 0)
connection_timeout = timeout;
}

std::once_flag ssl_init_once;

void SSLInit() {
// http://stackoverflow.com/questions/18315472/https-request-in-c-using-poco
#if USE_SSL
Poco::Net::initializeSSL();
// TODO: not accept invalid cert by some settings
Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> ptrHandler = new Poco::Net::AcceptCertificateHandler(false);
Poco::Net::Context::Ptr ptrContext = new Poco::Net::Context(
Poco::Net::Context::CLIENT_USE, ""
#if !defined(SECURITY_WIN32)
// Do not work with poco/NetSSL_Win:
, "", "", Poco::Net::Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"
#endif
);
Poco::Net::SSLManager::instance().initializeClient(0, ptrHandler, ptrContext);
#endif
}
8 changes: 5 additions & 3 deletions driver/connection.h
Expand Up @@ -22,6 +22,11 @@ struct Connection {
int timeout = 0;
int connection_timeout = 0;
int32_t stringmaxlength = 0;
bool ssl_strict = false;

std::string privateKeyFile;
std::string certificateFile;
std::string caLocation;

std::unique_ptr<Poco::Net::HTTPClientSession> session;
DiagnosticRecord diagnostic_record;
Expand Down Expand Up @@ -58,6 +63,3 @@ struct Connection {
private:
std::string database;
};

extern std::once_flag ssl_init_once;
void SSLInit();
7 changes: 6 additions & 1 deletion driver/odbc.ini
Expand Up @@ -12,14 +12,19 @@ description = Clickhouse driver
#url = https://default:password@localhost:8443/query?database=default&max_result_bytes=4000000&buffer_size=3000000
# Minimal (will connect to port 8443 if https:// or 8123 if http:// ):
#url = https://localhost

# Old way:
#server = localhost
#database = default
#uid = default
#port = 8123
#proto = http
# Use https with 8443 port:

# sslmode variants: allow - ignore self-signed and bad certificates; require - check certificates (and fail connection if something wrong)
#sslmode = require
#privatekeyfile =
#certificatefile =
#calocation =

#trace=1
#tracefile=/tmp/chlickhouse-odbc.log
Expand Down

0 comments on commit 1676610

Please sign in to comment.