sohami closed pull request #1366: [DRILL-6581] C++ Client SSL Implementation
Fixes/Improvements
URL: https://github.com/apache/drill/pull/1366
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/contrib/native/client/example/querySubmitter.cpp
b/contrib/native/client/example/querySubmitter.cpp
index 519dd93d5eb..a84d1dbe4d7 100644
--- a/contrib/native/client/example/querySubmitter.cpp
+++ b/contrib/native/client/example/querySubmitter.cpp
@@ -54,7 +54,8 @@ struct Option{
{"certFilePath", "Path to SSL certificate file", false},
{"disableHostnameVerification", "disable host name verification", false},
{"disableCertVerification", "disable certificate verification", false},
- {"useSystemTrustStore", "[Windows only]. Use the system truststore.",
false }
+ {"useSystemTrustStore", "[Windows only]. Use the system truststore.",
false },
+ {"CustomSSLCtxOptions", "The custom SSL CTX Options", false}
};
@@ -315,6 +316,7 @@ int main(int argc, char* argv[]) {
std::string
disableHostnameVerification=qsOptionValues["disableHostnameVerification"];
std::string
disableCertVerification=qsOptionValues["disableCertVerification"];
std::string useSystemTrustStore =
qsOptionValues["useSystemTrustStore"];
+ std::string customSSLOptions = qsOptionValues["CustomSSLCtxOptions"];
Drill::QueryType type;
@@ -416,6 +418,9 @@ int main(int argc, char* argv[]) {
if (useSystemTrustStore.length() > 0){
props.setProperty(USERPROP_USESYSTEMTRUSTSTORE,
useSystemTrustStore);
}
+ if (customSSLOptions.length() > 0){
+ props.setProperty(USERPROP_CUSTOM_SSLCTXOPTIONS,
customSSLOptions);
+ }
}
if(client.connect(connectStr.c_str(), &props)!=Drill::CONN_SUCCESS){
diff --git a/contrib/native/client/src/clientlib/channel.cpp
b/contrib/native/client/src/clientlib/channel.cpp
index 535fad77ce7..bdc19f7ad33 100644
--- a/contrib/native/client/src/clientlib/channel.cpp
+++ b/contrib/native/client/src/clientlib/channel.cpp
@@ -210,7 +210,19 @@ ChannelContext*
ChannelFactory::getChannelContext(channelType_t t, DrillUserProp
verifyMode = boost::asio::ssl::context::verify_none;
}
- pChannelContext = new SSLChannelContext(props, tlsVersion,
verifyMode);
+ long customSSLCtxOptions = 0;
+ std::string sslOptions;
+ props->getProp(USERPROP_CUSTOM_SSLCTXOPTIONS, sslOptions);
+ if (!sslOptions.empty()){
+ try{
+ customSSLCtxOptions =
boost::lexical_cast<long>(sslOptions);
+ }
+ catch (...){
+ DRILL_LOG(LOG_ERROR) << "Unable to parse custom SSL CTX
options." << std::endl;
+ }
+ }
+
+ pChannelContext = new SSLChannelContext(props, tlsVersion,
verifyMode, customSSLCtxOptions);
}
break;
#endif
@@ -352,30 +364,32 @@ connectionStatus_t SSLStreamChannel::init(){
connectionStatus_t ret=CONN_SUCCESS;
const DrillUserProperties* props = m_pContext->getUserProperties();
- std::string useSystemTrustStore;
- props->getProp(USERPROP_USESYSTEMTRUSTSTORE, useSystemTrustStore);
- if (useSystemTrustStore != "true"){
- std::string certFile;
- props->getProp(USERPROP_CERTFILEPATH, certFile);
- try{
-
((SSLChannelContext_t*)m_pContext)->getSslContext().load_verify_file(certFile);
- }
- catch (boost::system::system_error e){
- DRILL_LOG(LOG_ERROR) << "Channel initialization
failure. Certificate file "
- << certFile
- << " could not be loaded."
- << std::endl;
- handleError(CONN_SSLERROR,
getMessage(ERR_CONN_SSLCERTFAIL, certFile.c_str(), e.what()));
- ret = CONN_FAILURE;
- }
- }
+ std::string useSystemTrustStore;
+ props->getProp(USERPROP_USESYSTEMTRUSTSTORE, useSystemTrustStore);
+ if (useSystemTrustStore != "true"){
+ std::string certFile;
+ props->getProp(USERPROP_CERTFILEPATH, certFile);
+ try{
+
((SSLChannelContext_t*)m_pContext)->getSslContext().load_verify_file(certFile);
+ }
+ catch (boost::system::system_error e){
+ DRILL_LOG(LOG_ERROR) << "Channel initialization failure.
Certificate file "
+ << certFile
+ << " could not be loaded."
+ << std::endl;
+ handleError(CONN_SSLERROR, getMessage(ERR_CONN_SSLCERTFAIL,
certFile.c_str(), e.what()));
+ ret = CONN_FAILURE;
+ // Stop here to propagate the load/verify certificate error.
+ return ret;
+ }
+ }
+ ((SSLChannelContext_t
*)m_pContext)->SetCertHostnameVerificationStatus(true);
std::string disableHostVerification;
props->getProp(USERPROP_DISABLE_HOSTVERIFICATION, disableHostVerification);
if (disableHostVerification != "true") {
- std::string hostPortStr = m_pEndpoint->getHost() + ":" +
m_pEndpoint->getPort();
((SSLChannelContext_t *)
m_pContext)->getSslContext().set_verify_callback(
- boost::asio::ssl::rfc2818_verification(hostPortStr.c_str()));
+ DrillSSLHostnameVerifier(this));
}
m_pSocket=new SslSocket(m_ioService,
((SSLChannelContext_t*)m_pContext)->getSslContext() );
diff --git a/contrib/native/client/src/clientlib/channel.hpp
b/contrib/native/client/src/clientlib/channel.hpp
index 73273aa7b34..fec4659ccb5 100644
--- a/contrib/native/client/src/clientlib/channel.hpp
+++ b/contrib/native/client/src/clientlib/channel.hpp
@@ -21,6 +21,12 @@
#include "drill/common.hpp"
#include "drill/drillClient.hpp"
#include "streamSocket.hpp"
+#include "errmsgs.hpp"
+
+#if defined(IS_SSL_ENABLED)
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
namespace Drill {
@@ -34,7 +40,7 @@ class UserProperties;
//parse the connection string and set up the host and port to
connect to
connectionStatus_t getDrillbitEndpoint();
-
+
const std::string& getProtocol() const {return m_protocol;}
const std::string& getHost() const {return m_host;}
const std::string& getPort() const {return m_port;}
@@ -83,21 +89,41 @@ class UserProperties;
SSLChannelContext(DrillUserProperties *props,
boost::asio::ssl::context::method tlsVersion,
- boost::asio::ssl::verify_mode verifyMode) :
- ChannelContext(props),
- m_SSLContext(tlsVersion) {
+ boost::asio::ssl::verify_mode verifyMode,
+ const long customSSLCtxOptions = 0) :
+ ChannelContext(props),
+ m_SSLContext(tlsVersion),
+ m_certHostnameVerificationStatus(true)
+ {
m_SSLContext.set_default_verify_paths();
m_SSLContext.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
+ | boost::asio::ssl::context::no_sslv3
| boost::asio::ssl::context::single_dh_use
+ | customSSLCtxOptions
);
m_SSLContext.set_verify_mode(verifyMode);
};
+
~SSLChannelContext(){};
boost::asio::ssl::context& getSslContext(){ return m_SSLContext;}
+
+ /// @brief Check the certificate host name verification status.
+ ///
+ /// @return FALSE if the verification has failed, TRUE otherwise.
+ const bool GetCertificateHostnameVerificationStatus() const {
return m_certHostnameVerificationStatus; }
+
+ /// @brief Set the certificate host name verification status.
+ ///
+ /// @param in_result The host name verification
status.
+ void SetCertHostnameVerificationStatus(bool in_result) {
m_certHostnameVerificationStatus = in_result; }
+
private:
boost::asio::ssl::context m_SSLContext;
+
+ // The flag to indicate the host name verification result.
+ bool m_certHostnameVerificationStatus;
};
typedef ChannelContext ChannelContext_t;
@@ -147,9 +173,20 @@ class UserProperties;
ConnectionEndpoint* getEndpoint(){return m_pEndpoint;}
+ ChannelContext_t* getChannelContext(){ return m_pContext; }
+
protected:
connectionStatus_t handleError(connectionStatus_t status,
std::string msg);
+ /// @brief Handle protocol handshake exceptions.
+ ///
+ /// @param in_err The error.
+ ///
+ /// @return the connectionStatus.
+ virtual connectionStatus_t HandleProtocolHandshakeException(const
boost::system::system_error& in_err){
+ return handleError(CONN_HANDSHAKE_FAILED, in_err.what());
+ }
+
boost::asio::io_service& m_ioService;
boost::asio::io_service m_ioServiceFallback; // used if
m_ioService is not provided
AsioStreamSocket* m_pSocket;
@@ -170,7 +207,7 @@ class UserProperties;
try{
m_pSocket->protocolHandshake(useSystemConfig);
} catch (boost::system::system_error e) {
- status = handleError(CONN_HANDSHAKE_FAILED, e.what());
+ status = HandleProtocolHandshakeException(e);
}
return status;
}
@@ -199,6 +236,33 @@ class UserProperties;
:Channel(ioService, host, port){
}
connectionStatus_t init();
+ protected:
+#if defined(IS_SSL_ENABLED)
+ /// @brief Handle protocol handshake exceptions for SSL specific
failures.
+ ///
+ /// @param in_err The error.
+ ///
+ /// @return the connectionStatus.
+ connectionStatus_t HandleProtocolHandshakeException(const
boost::system::system_error& in_err) {
+ const boost::system::error_code& errcode = in_err.code();
+ if (!(((SSLChannelContext_t
*)m_pContext)->GetCertificateHostnameVerificationStatus())){
+ return handleError(
+ CONN_HANDSHAKE_FAILED,
+ getMessage(ERR_CONN_SSL_CN, in_err.what()));
+ }
+ else if (boost::asio::error::get_ssl_category() ==
errcode.category() &&
+ SSL_R_CERTIFICATE_VERIFY_FAILED ==
ERR_GET_REASON(errcode.value())){
+ return handleError(
+ CONN_HANDSHAKE_FAILED,
+ getMessage(ERR_CONN_SSL_CERTVERIFY, in_err.what()));
+ }
+ else{
+ return handleError(
+ CONN_HANDSHAKE_FAILED,
+ getMessage(ERR_CONN_SSL_GENERAL, in_err.what()));
+ }
+ }
+#endif
};
class ChannelFactory{
@@ -215,6 +279,52 @@ class UserProperties;
static ChannelContext_t* getChannelContext(channelType_t t,
DrillUserProperties* props);
};
+ /// @brief Hostname verification callback wrapper.
+ class DrillSSLHostnameVerifier{
+ public:
+ /// @brief The constructor.
+ ///
+ /// @param in_channel The Channel.
+ DrillSSLHostnameVerifier(Channel* in_channel) :
m_channel(in_channel){
+ DRILL_LOG(LOG_INFO)
+ << "DrillSSLHostnameVerifier::DrillSSLHostnameVerifier:
+++++ Enter +++++"
+ << std::endl;
+ }
+
+ /// @brief Perform certificate verification.
+ ///
+ /// @param in_preverified Pre-verified indicator.
+ /// @param in_ctx Verify context.
+ bool operator()(
+ bool in_preverified,
+ boost::asio::ssl::verify_context& in_ctx){
+ DRILL_LOG(LOG_INFO) << "DrillSSLHostnameVerifier::operator():
+++++ Enter +++++" << std::endl;
+
+ // Gets the channel context.
+ SSLChannelContext_t* context =
(SSLChannelContext_t*)(m_channel->getChannelContext());
+
+ // Retrieve the host before we perform Host name verification.
+ // This is because host with ZK mode is selected after the
connect() function is called.
+ boost::asio::ssl::rfc2818_verification
verifier(m_channel->getEndpoint()->getHost().c_str());
+
+ // Perform verification.
+ bool verified = verifier(in_preverified, in_ctx);
+
+ DRILL_LOG(LOG_DEBUG)
+ << "DrillSSLHostnameVerifier::operator(): Verification
Result: "
+ << verified
+ << std::endl;
+
+ // Sets the result back to the context.
+ context->SetCertHostnameVerificationStatus(verified);
+ return verified;
+ }
+
+ private:
+
+ // The SSL channel.
+ Channel* m_channel;
+ };
} // namespace Drill
diff --git a/contrib/native/client/src/clientlib/errmsgs.cpp
b/contrib/native/client/src/clientlib/errmsgs.cpp
index c1ac80d0649..82f24fd202e 100644
--- a/contrib/native/client/src/clientlib/errmsgs.cpp
+++ b/contrib/native/client/src/clientlib/errmsgs.cpp
@@ -57,6 +57,9 @@ static Drill::ErrorMessages errorMessages[]={
{ERR_CONN_NOSERVERENC, ERR_CATEGORY_CONN, 0, "Client needs encryption but
encryption is disabled on the server."
" Please check connection parameters or contact administrator. [Warn:
This"
" could be due to a bad configuration or a security attack is in
progress.]"},
+ {ERR_CONN_SSL_GENERAL, ERR_CATEGORY_CONN, 0, "Encountered an exception
during SSL handshake. [Details: %s]"},
+ {ERR_CONN_SSL_CN, ERR_CATEGORY_CONN, 0, "SSL certificate host name
verification failure. [Details: %s]" },
+ {ERR_CONN_SSL_CERTVERIFY, ERR_CATEGORY_CONN, 0, "SSL certificate
verification failed. [Details: %s]"},
{ERR_QRY_OUTOFMEM, ERR_CATEGORY_QRY, 0, "Out of memory."},
{ERR_QRY_COMMERR, ERR_CATEGORY_QRY, 0, "Communication error. %s"},
{ERR_QRY_INVREADLEN, ERR_CATEGORY_QRY, 0, "Internal Error: Received a
message with an invalid read length."},
diff --git a/contrib/native/client/src/clientlib/errmsgs.hpp
b/contrib/native/client/src/clientlib/errmsgs.hpp
index fac646b815f..7bcb80579d8 100644
--- a/contrib/native/client/src/clientlib/errmsgs.hpp
+++ b/contrib/native/client/src/clientlib/errmsgs.hpp
@@ -55,7 +55,10 @@ namespace Drill{
#define ERR_CONN_NOSOCKET DRILL_ERR_START+23
#define ERR_CONN_NOSERVERAUTH DRILL_ERR_START+24
#define ERR_CONN_NOSERVERENC DRILL_ERR_START+25
-#define ERR_CONN_MAX DRILL_ERR_START+25
+#define ERR_CONN_SSL_GENERAL DRILL_ERR_START+26
+#define ERR_CONN_SSL_CN DRILL_ERR_START+27
+#define ERR_CONN_SSL_CERTVERIFY DRILL_ERR_START+28
+#define ERR_CONN_MAX DRILL_ERR_START+28
#define ERR_QRY_OUTOFMEM ERR_CONN_MAX+1
#define ERR_QRY_COMMERR ERR_CONN_MAX+2
diff --git a/contrib/native/client/src/clientlib/userProperties.cpp
b/contrib/native/client/src/clientlib/userProperties.cpp
index f1aa82fa3ca..0ad8af1dd73 100644
--- a/contrib/native/client/src/clientlib/userProperties.cpp
+++ b/contrib/native/client/src/clientlib/userProperties.cpp
@@ -35,6 +35,7 @@ const std::map<std::string, uint32_t>
DrillUserProperties::USER_PROPERTIES=boos
( USERPROP_DISABLE_HOSTVERIFICATION,
USERPROP_FLAGS_BOOLEAN|USERPROP_FLAGS_SSLPROP)
( USERPROP_DISABLE_CERTVERIFICATION,
USERPROP_FLAGS_BOOLEAN|USERPROP_FLAGS_SSLPROP)
( USERPROP_USESYSTEMTRUSTSTORE,
USERPROP_FLAGS_BOOLEAN|USERPROP_FLAGS_SSLPROP)
+ ( USERPROP_CUSTOM_SSLCTXOPTIONS,
USERPROP_FLAGS_STRING|USERPROP_FLAGS_SSLPROP)
( USERPROP_SASL_ENCRYPT, USERPROP_FLAGS_STRING)
;
diff --git a/contrib/native/client/src/include/drill/common.hpp
b/contrib/native/client/src/include/drill/common.hpp
index 18cfc69fff5..b5bb522bee0 100644
--- a/contrib/native/client/src/include/drill/common.hpp
+++ b/contrib/native/client/src/include/drill/common.hpp
@@ -173,7 +173,8 @@ typedef enum{
#define USERPROP_PASSWORD "password"
#define USERPROP_SCHEMA "schema"
#define USERPROP_USESSL "enableTLS"
-#define USERPROP_TLSPROTOCOL "TLSProtocol" //TLS version
+#define USERPROP_TLSPROTOCOL "TLSProtocol" //TLS version. The exact TLS
version.
+#define USERPROP_CUSTOM_SSLCTXOPTIONS "CustomSSLCtxOptions" // The custom SSL
CTX options.
#define USERPROP_CERTFILEPATH "certFilePath" // pem file path and name
// TODO: support truststore protected by password.
// #define USERPROP_CERTPASSWORD "certPassword" // Password for certificate
file.
diff --git a/contrib/native/client/src/include/drill/userProperties.hpp
b/contrib/native/client/src/include/drill/userProperties.hpp
index f5d6783ea33..fb6c764e723 100644
--- a/contrib/native/client/src/include/drill/userProperties.hpp
+++ b/contrib/native/client/src/include/drill/userProperties.hpp
@@ -29,8 +29,7 @@ class DECLSPEC_DRILL_CLIENT DrillUserProperties{
DrillUserProperties(){};
- /// @brief Update the property value associate with the property key
if the value is
- /// empty.
+ /// @brief Sets the default property value.
///
/// @param in_propName The property name.
/// @param in_propValue The property value.
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services