Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 561C2200C4A for ; Sat, 25 Feb 2017 08:17:59 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 52440160B7B; Sat, 25 Feb 2017 07:17:59 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 8A4D4160B8B for ; Sat, 25 Feb 2017 08:17:57 +0100 (CET) Received: (qmail 14795 invoked by uid 500); 25 Feb 2017 07:17:56 -0000 Mailing-List: contact commits-help@drill.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: commits@drill.apache.org Delivered-To: mailing list commits@drill.apache.org Received: (qmail 13593 invoked by uid 99); 25 Feb 2017 07:17:54 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 25 Feb 2017 07:17:54 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 7CB7BDFDAC; Sat, 25 Feb 2017 07:17:54 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: sudheesh@apache.org To: commits@drill.apache.org Date: Sat, 25 Feb 2017 07:18:20 -0000 Message-Id: <3b370af79fc64ddf8b373e8a5e0504db@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [27/29] drill git commit: DRILL-4280: CORE (user to bit authentication, C++) archived-at: Sat, 25 Feb 2017 07:17:59 -0000 DRILL-4280: CORE (user to bit authentication, C++) closes #578 Project: http://git-wip-us.apache.org/repos/asf/drill/repo Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/3c3b08c5 Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/3c3b08c5 Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/3c3b08c5 Branch: refs/heads/master Commit: 3c3b08c5abbd4e9e11dadb8e97367db0c13b3243 Parents: f9f99e0 Author: Sudheesh Katkam Authored: Thu Feb 23 18:47:04 2017 -0800 Committer: Sudheesh Katkam Committed: Fri Feb 24 20:01:18 2017 -0800 ---------------------------------------------------------------------- contrib/native/client/CMakeLists.txt | 4 + .../native/client/cmakeModules/FindSASL.cmake | 49 +++++ .../native/client/example/querySubmitter.cpp | 9 +- .../native/client/src/clientlib/CMakeLists.txt | 7 +- .../native/client/src/clientlib/drillClient.cpp | 16 +- .../client/src/clientlib/drillClientImpl.cpp | 201 +++++++++++++++--- .../client/src/clientlib/drillClientImpl.hpp | 27 ++- .../src/clientlib/saslAuthenticatorImpl.cpp | 207 +++++++++++++++++++ .../src/clientlib/saslAuthenticatorImpl.hpp | 65 ++++++ .../native/client/src/include/drill/common.hpp | 3 + .../client/src/include/drill/drillClient.hpp | 4 + 11 files changed, 552 insertions(+), 40 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/contrib/native/client/CMakeLists.txt b/contrib/native/client/CMakeLists.txt index 65e3b85..7b54b00 100644 --- a/contrib/native/client/CMakeLists.txt +++ b/contrib/native/client/CMakeLists.txt @@ -125,6 +125,8 @@ include_directories(${PROTOBUF_INCLUDE_DIR}) #Find Zookeeper find_package(Zookeeper REQUIRED ) +# Find Cyrus SASL +find_package(SASL REQUIRED) # Generated sources configure_file( @@ -152,6 +154,8 @@ add_subdirectory("${CMAKE_SOURCE_DIR}/src/clientlib/y2038") add_subdirectory("${CMAKE_SOURCE_DIR}/src/clientlib") include_directories(${CMAKE_SOURCE_DIR}/src/include ${Zookeeper_INCLUDE_DIRS}) +include_directories(${SASL_INCLUDE_DIRS}) + add_subdirectory("${CMAKE_SOURCE_DIR}/src/test") # add a DEBUG preprocessor macro http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/cmakeModules/FindSASL.cmake ---------------------------------------------------------------------- diff --git a/contrib/native/client/cmakeModules/FindSASL.cmake b/contrib/native/client/cmakeModules/FindSASL.cmake new file mode 100644 index 0000000..35d91c7 --- /dev/null +++ b/contrib/native/client/cmakeModules/FindSASL.cmake @@ -0,0 +1,49 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# - Try to find Cyrus SASL + +if (MSVC) + if("${SASL_HOME}_" MATCHES "^_$") + message(" ") + message("- Please set the cache variable SASL_HOME to point to the directory with the Cyrus SASL source.") + message("- CMAKE will look for Cyrus SASL include files in $SASL_HOME/include or $SASL_HOME/win32/include.") + message("- CMAKE will look for Cyrus SASL library files in $SASL_HOME/lib.") + else() + FILE(TO_CMAKE_PATH ${SASL_HOME} SASL_HomePath) + set(SASL_LIB_PATHS ${SASL_HomePath}/lib) + + find_path(SASL_INCLUDE_DIR sasl.h ${SASL_HomePath}/include ${SASL_HomePath}/win32/include) + find_library(SASL_LIBRARY NAMES "libsasl2${CMAKE_SHARED_LIBRARY_SUFFIX}" PATHS ${SASL_LIB_PATHS}) + endif() +else() + set(SASL_LIB_PATHS /usr/local/lib /opt/local/lib) + find_path(SASL_INCLUDE_DIR sasl/sasl.h /usr/local/include /opt/local/include) + find_library(SASL_LIBRARY NAMES "libsasl2${CMAKE_SHARED_LIBRARY_SUFFIX}" PATHS ${SASL_LIB_PATHS}) +endif() + + +set(SASL_LIBRARIES ${SASL_LIBRARY}) +set(SASL_INCLUDE_DIRS ${SASL_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set SASL_FOUND to TRUE if all listed variables are valid +find_package_handle_standard_args(SASL DEFAULT_MSG + SASL_LIBRARY SASL_INCLUDE_DIR) + +mark_as_advanced(SASL_INCLUDE_DIR SASL_LIBRARY) http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/example/querySubmitter.cpp ---------------------------------------------------------------------- diff --git a/contrib/native/client/example/querySubmitter.cpp b/contrib/native/client/example/querySubmitter.cpp index 60f7b8a..5b85a3e 100644 --- a/contrib/native/client/example/querySubmitter.cpp +++ b/contrib/native/client/example/querySubmitter.cpp @@ -23,7 +23,7 @@ #include #include "drill/drillc.hpp" -int nOptions=13; +int nOptions=15; struct Option{ char name[32]; @@ -43,7 +43,8 @@ struct Option{ {"queryTimeout", "Query timeout (second).", false}, {"heartbeatFrequency", "Heartbeat frequency (second). Disabled if set to 0.", false}, {"user", "Username", false}, - {"password", "Password", false} + {"password", "Password", false}, + {"saslPluginPath", "Path to where SASL plugins are installed", false} }; std::map qsOptionValues; @@ -286,6 +287,7 @@ int main(int argc, char* argv[]) { std::string heartbeatFrequency=qsOptionValues["heartbeatFrequency"]; std::string user=qsOptionValues["user"]; std::string password=qsOptionValues["password"]; + std::string saslPluginPath=qsOptionValues["saslPluginPath"]; Drill::QueryType type; @@ -348,6 +350,9 @@ int main(int argc, char* argv[]) { if(!heartbeatFrequency.empty()) { Drill::DrillClientConfig::setHeartbeatFrequency(atoi(heartbeatFrequency.c_str())); } + if (!saslPluginPath.empty()){ + Drill::DrillClientConfig::setSaslPluginPath(saslPluginPath.c_str()); + } Drill::DrillUserProperties props; if(schema.length()>0){ http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/src/clientlib/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/contrib/native/client/src/clientlib/CMakeLists.txt b/contrib/native/client/src/clientlib/CMakeLists.txt index 68326e2..343bb4d 100644 --- a/contrib/native/client/src/clientlib/CMakeLists.txt +++ b/contrib/native/client/src/clientlib/CMakeLists.txt @@ -29,12 +29,13 @@ set (CLIENTLIB_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/errmsgs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/logger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/zookeeperClient.cpp - ) + ${CMAKE_CURRENT_SOURCE_DIR}/saslAuthenticatorImpl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zookeeperClient.cpp) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../include ) include_directories(${PROTOBUF_INCLUDE_DIR}) include_directories(${Zookeeper_INCLUDE_DIRS}) +include_directories(${SASL_INCLUDE_DIRS}) link_directories(/usr/local/lib) @@ -49,4 +50,4 @@ if(MSVC) endif() add_library(drillClient SHARED ${CLIENTLIB_SRC_FILES} ) -target_link_libraries(drillClient ${Boost_LIBRARIES} ${PROTOBUF_LIBRARY} ${Zookeeper_LIBRARIES} protomsgs y2038) +target_link_libraries(drillClient ${Boost_LIBRARIES} ${PROTOBUF_LIBRARY} ${Zookeeper_LIBRARIES} ${SASL_LIBRARIES} protomsgs y2038) http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/src/clientlib/drillClient.cpp ---------------------------------------------------------------------- diff --git a/contrib/native/client/src/clientlib/drillClient.cpp b/contrib/native/client/src/clientlib/drillClient.cpp index f97a25c..7000272 100644 --- a/contrib/native/client/src/clientlib/drillClient.cpp +++ b/contrib/native/client/src/clientlib/drillClient.cpp @@ -47,6 +47,7 @@ DrillClientInitializer::~DrillClientInitializer(){ // Initialize static member of DrillClientConfig logLevel_t DrillClientConfig::s_logLevel=LOG_ERROR; +const char* DrillClientConfig::s_saslPluginPath = NULL; uint64_t DrillClientConfig::s_bufferLimit=MAX_MEM_ALLOC_SIZE; int32_t DrillClientConfig::s_socketTimeout=0; int32_t DrillClientConfig::s_handshakeTimeout=5; @@ -77,6 +78,16 @@ void DrillClientConfig::setLogLevel(logLevel_t l){ //boost::log::core::get()->set_filter(boost::log::trivial::severity >= s_logLevel); } +void DrillClientConfig::setSaslPluginPath(const char *path){ + boost::lock_guard configLock(DrillClientConfig::s_mutex); + s_saslPluginPath = path; +} + +const char* DrillClientConfig::getSaslPluginPath(){ + boost::lock_guard configLock(DrillClientConfig::s_mutex); + return s_saslPluginPath; +} + void DrillClientConfig::setBufferLimit(uint64_t l){ boost::lock_guard configLock(DrillClientConfig::s_mutex); s_bufferLimit=l; @@ -164,6 +175,9 @@ const std::map DrillUserProperties::USER_PROPERTIES=boos ( USERPROP_PASSWORD, USERPROP_FLAGS_SERVERPROP|USERPROP_FLAGS_PASSWORD) ( USERPROP_SCHEMA, USERPROP_FLAGS_SERVERPROP|USERPROP_FLAGS_STRING) ( USERPROP_IMPERSONATION_TARGET, USERPROP_FLAGS_SERVERPROP|USERPROP_FLAGS_STRING) + ( USERPROP_AUTH_MECHANISM, USERPROP_FLAGS_STRING) + ( USERPROP_SERVICE_NAME, USERPROP_FLAGS_STRING) + ( USERPROP_SERVICE_HOST, USERPROP_FLAGS_STRING) ( USERPROP_USESSL, USERPROP_FLAGS_BOOLEAN|USERPROP_FLAGS_SSLPROP) ( USERPROP_FILEPATH, USERPROP_FLAGS_STRING|USERPROP_FLAGS_SSLPROP|USERPROP_FLAGS_FILEPATH) ( USERPROP_FILENAME, USERPROP_FLAGS_STRING|USERPROP_FLAGS_SSLPROP|USERPROP_FLAGS_FILENAME) @@ -365,7 +379,7 @@ connectionStatus_t DrillClient::connect(const char* connectStr, const char* defa connectionStatus_t DrillClient::connect(const char* connectStr, DrillUserProperties* properties){ connectionStatus_t ret=CONN_SUCCESS; - ret=this->m_pImpl->connect(connectStr); + ret=this->m_pImpl->connect(connectStr, properties); if(ret==CONN_SUCCESS){ ret=this->m_pImpl->validateHandshake(properties); } http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/src/clientlib/drillClientImpl.cpp ---------------------------------------------------------------------- diff --git a/contrib/native/client/src/clientlib/drillClientImpl.cpp b/contrib/native/client/src/clientlib/drillClientImpl.cpp index 05171e5..4486068 100644 --- a/contrib/native/client/src/clientlib/drillClientImpl.cpp +++ b/contrib/native/client/src/clientlib/drillClientImpl.cpp @@ -20,6 +20,7 @@ #include "drill/common.hpp" #include #include +#include #include #include #include @@ -43,6 +44,7 @@ #include "GeneralRPC.pb.h" #include "UserBitShared.pb.h" #include "zookeeperClient.hpp" +#include "saslAuthenticatorImpl.hpp" namespace Drill{ @@ -58,7 +60,7 @@ static std::string debugPrintQid(const exec::shared::QueryId& qid){ return std::string("[")+boost::lexical_cast(qid.part1()) +std::string(":") + boost::lexical_cast(qid.part2())+std::string("] "); } -connectionStatus_t DrillClientImpl::connect(const char* connStr){ +connectionStatus_t DrillClientImpl::connect(const char* connStr, DrillUserProperties* props){ std::string pathToDrill, protocol, hostPortStr; std::string host; std::string port; @@ -103,6 +105,15 @@ connectionStatus_t DrillClientImpl::connect(const char* connStr){ return handleConnError(CONN_INVALID_INPUT, getMessage(ERR_CONN_UNKPROTO, protocol.c_str())); } DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Connecting to endpoint: " << host << ":" << port << std::endl;) + std::string serviceHost; + for (size_t i = 0; i < props->size(); i++) { + if (props->keyAt(i) == USERPROP_SERVICE_HOST) { + serviceHost = props->valueAt(i); + } + } + if (serviceHost.empty()) { + props->setProperty(USERPROP_SERVICE_HOST, host); + } connectionStatus_t ret = this->connect(host.c_str(), port.c_str()); return ret; } @@ -308,6 +319,11 @@ void DrillClientImpl::handleHandshake(ByteBuf_t _buf, this->m_handshakeErrorId=b2u.errorid(); this->m_handshakeErrorMsg=b2u.errormessage(); this->m_serverInfos = b2u.server_infos(); + for (int i=0; im_serverAuthMechanisms.push_back(mechanism); + } }else{ // boost error @@ -348,6 +364,7 @@ connectionStatus_t DrillClientImpl::validateHandshake(DrillUserProperties* prope u2b.set_rpc_version(DRILL_RPC_VERSION); u2b.set_support_listening(true); u2b.set_support_timeout(DrillClientConfig::getHeartbeatFrequency() > 0); + u2b.set_sasl_support(exec::user::SASL_AUTH); // Adding version info exec::user::RpcEndpointInfos* infos = u2b.mutable_client_infos(); @@ -412,37 +429,155 @@ connectionStatus_t DrillClientImpl::validateHandshake(DrillUserProperties* prope if(ret!=CONN_SUCCESS){ return ret; } - if(this->m_handshakeStatus != exec::user::SUCCESS){ - switch(this->m_handshakeStatus){ - case exec::user::RPC_VERSION_MISMATCH: - DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Invalid rpc version. Expected " - << DRILL_RPC_VERSION << ", actual "<< m_handshakeVersion << "." << std::endl;) - return handleConnError(CONN_BAD_RPC_VER, - getMessage(ERR_CONN_BAD_RPC_VER, DRILL_RPC_VERSION, - m_handshakeVersion, - this->m_handshakeErrorId.c_str(), - this->m_handshakeErrorMsg.c_str())); - case exec::user::AUTH_FAILED: - DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Authentication failed." << std::endl;) - return handleConnError(CONN_AUTH_FAILED, - getMessage(ERR_CONN_AUTHFAIL, - this->m_handshakeErrorId.c_str(), - this->m_handshakeErrorMsg.c_str())); - case exec::user::UNKNOWN_FAILURE: - DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Unknown error during handshake." << std::endl;) - return handleConnError(CONN_HANDSHAKE_FAILED, - getMessage(ERR_CONN_UNKNOWN_ERR, - this->m_handshakeErrorId.c_str(), - this->m_handshakeErrorMsg.c_str())); - default: - break; + + switch(this->m_handshakeStatus) { + case exec::user::SUCCESS: + // reset io_service after handshake is validated before running queries + m_io_service.reset(); + return CONN_SUCCESS; + case exec::user::RPC_VERSION_MISMATCH: + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Invalid rpc version. Expected " + << DRILL_RPC_VERSION << ", actual "<< m_handshakeVersion << "." << std::endl;) + return handleConnError(CONN_BAD_RPC_VER, getMessage(ERR_CONN_BAD_RPC_VER, DRILL_RPC_VERSION, + m_handshakeVersion, + this->m_handshakeErrorId.c_str(), + this->m_handshakeErrorMsg.c_str())); + case exec::user::AUTH_FAILED: + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Authentication failed." << std::endl;) + return handleConnError(CONN_AUTH_FAILED, getMessage(ERR_CONN_AUTHFAIL, + this->m_handshakeErrorId.c_str(), + this->m_handshakeErrorMsg.c_str())); + case exec::user::UNKNOWN_FAILURE: + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Unknown error during handshake." << std::endl;) + return handleConnError(CONN_HANDSHAKE_FAILED, getMessage(ERR_CONN_UNKNOWN_ERR, + this->m_handshakeErrorId.c_str(), + this->m_handshakeErrorMsg.c_str())); + case exec::user::AUTH_REQUIRED: + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Server requires SASL authentication." << std::endl;) + return handleAuthentication(properties); + default: + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Unknown return status." << std::endl;) + return handleConnError(CONN_HANDSHAKE_FAILED, getMessage(ERR_CONN_UNKNOWN_ERR, + this->m_handshakeErrorId.c_str(), + this->m_handshakeErrorMsg.c_str())); + } +} + +connectionStatus_t DrillClientImpl::handleAuthentication(const DrillUserProperties *userProperties) { + try { + m_saslAuthenticator = new SaslAuthenticatorImpl(userProperties); + } catch (std::runtime_error& e) { + return handleConnError(CONN_AUTH_FAILED, e.what()); + } + + startMessageListener(); + initiateAuthentication(); + + { // block until SASL exchange is complete + boost::mutex::scoped_lock lock(m_saslMutex); + while (!m_saslDone) { + m_saslCv.wait(lock); } } - // reset io_service after handshake is validated before running queries - m_io_service.reset(); - return CONN_SUCCESS; + + if (SASL_OK == m_saslResultCode) { + DRILL_MT_LOG(DRILL_LOG(LOG_DEBUG) << "DrillClientImpl::handleAuthentication: Successfully authenticated!" + << std::endl;) + + // in future, negotiated security layers are known here.. + + m_io_service.reset(); + return CONN_SUCCESS; + } else { + DRILL_MT_LOG(DRILL_LOG(LOG_DEBUG) << "DrillClientImpl::handleAuthentication: Authentication failed: " + << m_saslResultCode << std::endl;) + // shuts down socket as well + return handleConnError(CONN_AUTH_FAILED, "Authentication failed. Check connection parameters?"); + } } +void DrillClientImpl::initiateAuthentication() { + exec::shared::SaslMessage response; + m_saslResultCode = m_saslAuthenticator->init(m_serverAuthMechanisms, response); + + + switch (m_saslResultCode) { + case SASL_CONTINUE: + case SASL_OK: { + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "DrillClientImpl::initiateAuthentication: initiated. " << std::endl;) + boost::lock_guard prLock(m_prMutex); + sendSaslResponse(response); // the challenge returned by server is handled by processSaslChallenge + break; + } + case SASL_NOMECH: + DRILL_MT_LOG(DRILL_LOG(LOG_DEBUG) << "DrillClientImpl::initiateAuthentication: " + << "Mechanism is not supported (by server/client)." << std::endl;) + default: + DRILL_MT_LOG(DRILL_LOG(LOG_DEBUG) << "DrillClientImpl::initiateAuthentication: " + << "Failed to initiate authentication." << std::endl;) + finishAuthentication(); + break; + } +} + +void DrillClientImpl::sendSaslResponse(const exec::shared::SaslMessage& response) { + boost::lock_guard lock(m_dcMutex); + const int32_t coordId = getNextCoordinationId(); + rpc::OutBoundRpcMessage msg(exec::rpc::REQUEST, exec::user::SASL_MESSAGE, coordId, &response); + sendSync(msg); + if (m_pendingRequests++ == 0) { + getNextResult(); + } +} + +void DrillClientImpl::processSaslChallenge(AllocatedBufferPtr allocatedBuffer, const rpc::InBoundRpcMessage& msg) { + boost::shared_ptr deallocationGuard(allocatedBuffer); + assert(m_saslAuthenticator != NULL); + + // parse challenge + exec::shared::SaslMessage challenge; + const bool parseStatus = challenge.ParseFromArray(msg.m_pbody.data(), msg.m_pbody.size()); + if (!parseStatus) { + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "Failed to parse challenge." << std::endl;) + m_saslResultCode = SASL_FAIL; + finishAuthentication(); + m_pendingRequests--; + return; + } + + // respond accordingly + exec::shared::SaslMessage response; + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "DrillClientImpl::processSaslChallenge: status: " + << exec::shared::SaslStatus_Name(challenge.status()) << std::endl;) + switch (challenge.status()) { + case exec::shared::SASL_IN_PROGRESS: + m_saslResultCode = m_saslAuthenticator->step(challenge, response); + if (m_saslResultCode == SASL_CONTINUE || m_saslResultCode == SASL_OK) { + sendSaslResponse(response); + } else { // failure + finishAuthentication(); + } + break; + case exec::shared::SASL_SUCCESS: + if (SASL_CONTINUE == m_saslResultCode) { // client may need to evaluate once more + m_saslResultCode = m_saslAuthenticator->step(challenge, response); + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "SASL succeeded on client? " << m_saslResultCode << std::endl;) + } + finishAuthentication(); + break; + default: + m_saslResultCode = SASL_FAIL; + finishAuthentication(); + break; + } + m_pendingRequests--; +} + +void DrillClientImpl::finishAuthentication() { + boost::mutex::scoped_lock lock(m_saslMutex); + m_saslDone = true; + m_saslCv.notify_one(); +} FieldDefPtr DrillClientQueryResult::s_emptyColDefs( new (std::vector)); @@ -1369,6 +1504,10 @@ void DrillClientImpl::handleRead(ByteBuf_t _buf, delete allocatedBuffer; break; + case exec::user::SASL_MESSAGE: + processSaslChallenge(allocatedBuffer, msg); + break; + case exec::user::ACK: // Cancel requests will result in an ACK sent back. // Consume silently @@ -1859,7 +1998,7 @@ void DrillClientPrepareHandle::clearAndDestroy(){ } } -connectionStatus_t PooledDrillClientImpl::connect(const char* connStr){ +connectionStatus_t PooledDrillClientImpl::connect(const char* connStr, DrillUserProperties* props){ connectionStatus_t stat = CONN_SUCCESS; std::string pathToDrill, protocol, hostPortStr; std::string host; @@ -2062,7 +2201,7 @@ DrillClientImpl* PooledDrillClientImpl::getOneConnection(){ DrillClientImpl* pDrillClientImpl = NULL; while(pDrillClientImpl==NULL){ if(m_queriesExecuted == 0){ - // First query ever sent can use the connection already established to authenticate the user + // First query ever sent can use the connection already established to handleAuthentication the user boost::lock_guard lock(m_poolMutex); pDrillClientImpl=m_clientConnections[0];// There should be one connection in the list when the first query is executed }else if(m_clientConnections.size() == m_maxConcurrentConnections){ @@ -2077,7 +2216,7 @@ DrillClientImpl* PooledDrillClientImpl::getOneConnection(){ int tries=0; connectionStatus_t ret=CONN_SUCCESS; while(pDrillClientImpl==NULL && tries++ < 3){ - if((ret=connect(m_connectStr.c_str()))==CONN_SUCCESS){ + if((ret=connect(m_connectStr.c_str(), m_pUserProperties.get()))==CONN_SUCCESS){ boost::lock_guard lock(m_poolMutex); pDrillClientImpl=m_clientConnections.back(); ret=pDrillClientImpl->validateHandshake(m_pUserProperties.get()); http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/src/clientlib/drillClientImpl.hpp ---------------------------------------------------------------------- diff --git a/contrib/native/client/src/clientlib/drillClientImpl.hpp b/contrib/native/client/src/clientlib/drillClientImpl.hpp index 22e34af..262edc9 100644 --- a/contrib/native/client/src/clientlib/drillClientImpl.hpp +++ b/contrib/native/client/src/clientlib/drillClientImpl.hpp @@ -50,6 +50,7 @@ #include "utils.hpp" #include "User.pb.h" #include "UserBitShared.pb.h" +#include "saslAuthenticatorImpl.hpp" namespace Drill { @@ -73,7 +74,7 @@ class DrillClientImplBase{ //Connect via Zookeeper or directly. //Makes an initial connection to a drillbit. successful connect adds the first drillbit to the pool. - virtual connectionStatus_t connect(const char* connStr)=0; + virtual connectionStatus_t connect(const char* connStr, DrillUserProperties* props)=0; // Test whether the client is active. Returns true if any one of the underlying connections is active virtual bool Active()=0; @@ -362,6 +363,8 @@ class DrillClientImpl : public DrillClientImplBase{ m_handshakeVersion(0), m_handshakeStatus(exec::user::SUCCESS), m_bIsConnected(false), + m_saslAuthenticator(NULL), + m_saslDone(false), m_pendingRequests(0), m_pError(NULL), m_pListenerThread(NULL), @@ -385,6 +388,10 @@ class DrillClientImpl : public DrillClientImplBase{ delete this->m_pWork; this->m_pWork = NULL; } + if(this->m_saslAuthenticator!=NULL){ + delete this->m_saslAuthenticator; + this->m_saslAuthenticator = NULL; + } m_heartbeatTimer.cancel(); m_deadlineTimer.cancel(); @@ -415,7 +422,7 @@ class DrillClientImpl : public DrillClientImplBase{ }; //Connect via Zookeeper or directly - connectionStatus_t connect(const char* connStr); + connectionStatus_t connect(const char* connStr, DrillUserProperties* props); // test whether the client is active bool Active(); void Close() ; @@ -511,6 +518,13 @@ class DrillClientImpl : public DrillClientImplBase{ DrillClientTableResult* getTables(const std::string& catalogPattern, const std::string& schemaPattern, const std::string& tablePattern, const std::vector* tableTypes, Metadata::pfnTableMetadataListener listener, void* listenerCtx); DrillClientColumnResult* getColumns(const std::string& catalogPattern, const std::string& schemaPattern, const std::string& tablePattern, const std::string& columnPattern, Metadata::pfnColumnMetadataListener listener, void* listenerCtx); + // SASL exchange + connectionStatus_t handleAuthentication(const DrillUserProperties *userProperties); + void initiateAuthentication(); + void sendSaslResponse(const exec::shared::SaslMessage& response); + void processSaslChallenge(AllocatedBufferPtr allocatedBuffer, const rpc::InBoundRpcMessage& msg); + void finishAuthentication(); + void shutdownSocket(); int32_t m_coordinationId; @@ -521,6 +535,13 @@ class DrillClientImpl : public DrillClientImplBase{ exec::user::RpcEndpointInfos m_serverInfos; bool m_bIsConnected; + std::vector m_serverAuthMechanisms; + SaslAuthenticatorImpl* m_saslAuthenticator; + int m_saslResultCode; + bool m_saslDone; + boost::mutex m_saslMutex; // mutex to protect m_saslDone + boost::condition_variable m_saslCv; // to signal completion of SASL exchange + std::string m_connectStr; // @@ -605,7 +626,7 @@ class PooledDrillClientImpl : public DrillClientImplBase{ //Connect via Zookeeper or directly. //Makes an initial connection to a drillbit. successful connect adds the first drillbit to the pool. - connectionStatus_t connect(const char* connStr); + connectionStatus_t connect(const char* connStr, DrillUserProperties* props); // Test whether the client is active. Returns true if any one of the underlying connections is active bool Active(); http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/src/clientlib/saslAuthenticatorImpl.cpp ---------------------------------------------------------------------- diff --git a/contrib/native/client/src/clientlib/saslAuthenticatorImpl.cpp b/contrib/native/client/src/clientlib/saslAuthenticatorImpl.cpp new file mode 100644 index 0000000..e7e2ba5 --- /dev/null +++ b/contrib/native/client/src/clientlib/saslAuthenticatorImpl.cpp @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "saslAuthenticatorImpl.hpp" + +#include "drillClientImpl.hpp" +#include "logger.hpp" + +namespace Drill { + +static const std::string DEFAULT_SERVICE_NAME = "drill"; + +static const std::string KERBEROS_SIMPLE_NAME = "kerberos"; +static const std::string KERBEROS_SASL_NAME = "gssapi"; +static const std::string PLAIN_NAME = "plain"; + +const std::map SaslAuthenticatorImpl::MECHANISM_MAPPING = boost::assign::map_list_of + (KERBEROS_SIMPLE_NAME, KERBEROS_SASL_NAME) + (PLAIN_NAME, PLAIN_NAME) +; + +boost::mutex SaslAuthenticatorImpl::s_mutex; +bool SaslAuthenticatorImpl::s_initialized = false; + +SaslAuthenticatorImpl::SaslAuthenticatorImpl(const DrillUserProperties* const properties) : + m_pUserProperties(properties), m_pConnection(NULL), m_ppwdSecret(NULL) { + + if (!s_initialized) { + boost::lock_guard lock(SaslAuthenticatorImpl::s_mutex); + if (!s_initialized) { + // set plugin path if provided + if (DrillClientConfig::getSaslPluginPath()) { + char *saslPluginPath = const_cast(DrillClientConfig::getSaslPluginPath()); + sasl_set_path(0, saslPluginPath); + } + + // loads all the available mechanism and factories in the sasl_lib referenced by the path + const int err = sasl_client_init(NULL); + if (0 != err) { + std::stringstream errMsg; + errMsg << "Failed to load authentication libraries. code: " << err; + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << errMsg.str() << std::endl;) + throw std::runtime_error(errMsg.str().c_str()); + } + { // for debugging purposes + const char **mechanisms = sasl_global_listmech(); + int i = 0; + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "SASL mechanisms available on client: " << std::endl;) + while (mechanisms[i] != NULL) { + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << i << " : " << mechanisms[i] << std::endl;) + i++; + } + } + s_initialized = true; + } + } +} + +SaslAuthenticatorImpl::~SaslAuthenticatorImpl() { + if (m_ppwdSecret) { + free(m_ppwdSecret); + } + m_ppwdSecret = NULL; + // may be used to negotiated security layers before disposing in the future + if (m_pConnection) { + sasl_dispose(&m_pConnection); + } + m_pConnection = NULL; +} + +typedef int (*sasl_callback_proc_t)(void); // see sasl_callback_ft + +int SaslAuthenticatorImpl::userNameCallback(void *context, int id, const char **result, unsigned *len) { + const std::string* const username = static_cast(context); + + if ((SASL_CB_USER == id || SASL_CB_AUTHNAME == id) + && username != NULL) { + *result = username->c_str(); + // *len = (unsigned int) username->length(); + } + return SASL_OK; +} + +int SaslAuthenticatorImpl::passwordCallback(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret) { + const SaslAuthenticatorImpl* const authenticator = static_cast(context); + + if (SASL_CB_PASS == id) { + *psecret = authenticator->m_ppwdSecret; + } + return SASL_OK; +} + +int SaslAuthenticatorImpl::init(const std::vector& mechanisms, exec::shared::SaslMessage& response) { + // find and set parameters + std::string authMechanismToUse; + std::string serviceName; + std::string serviceHost; + for (size_t i = 0; i < m_pUserProperties->size(); i++) { + const std::string key = m_pUserProperties->keyAt(i); + const std::string value = m_pUserProperties->valueAt(i); + + if (USERPROP_SERVICE_HOST == key) { + serviceHost = value; + } else if (USERPROP_SERVICE_NAME == key) { + serviceName = value; + } else if (USERPROP_PASSWORD == key) { + const size_t length = value.length(); + m_ppwdSecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + length); + std::memcpy(m_ppwdSecret->data, value.c_str(), length); + m_ppwdSecret->len = length; + authMechanismToUse = PLAIN_NAME; + } else if (USERPROP_USERNAME == key) { + m_username = value; + } else if (USERPROP_AUTH_MECHANISM == key) { + authMechanismToUse = value; + } + } + if (authMechanismToUse.empty()) return SASL_NOMECH; + + // check if requested mechanism is supported by server + boost::algorithm::to_lower(authMechanismToUse); + if (std::find(mechanisms.begin(), mechanisms.end(), authMechanismToUse) == mechanisms.end()) return SASL_NOMECH; + + // find the SASL name + const std::map::const_iterator it = + SaslAuthenticatorImpl::MECHANISM_MAPPING.find(authMechanismToUse); + if (it == SaslAuthenticatorImpl::MECHANISM_MAPPING.end()) return SASL_NOMECH; + + const std::string saslMechanismToUse = it->second; + + // setup callbacks and parameters + const sasl_callback_t callbacks[] = { + { SASL_CB_USER, (sasl_callback_proc_t) &userNameCallback, static_cast(&m_username) }, + { SASL_CB_AUTHNAME, (sasl_callback_proc_t) &userNameCallback, static_cast(&m_username) }, + { SASL_CB_PASS, (sasl_callback_proc_t) &passwordCallback, static_cast(this) }, + { SASL_CB_LIST_END, NULL, NULL } + }; + if (serviceName.empty()) serviceName = DEFAULT_SERVICE_NAME; + + // create SASL client + int saslResult = sasl_client_new(serviceName.c_str(), serviceHost.c_str(), NULL /** iplocalport */, + NULL /** ipremoteport */, callbacks, 0 /** sec flags */, &m_pConnection); + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "SaslAuthenticatorImpl::init: sasl_client_new code: " + << saslResult << std::endl;) + if (saslResult != SASL_OK) return saslResult; + + // initiate; for now, pass in only one mechanism + const char *out; + unsigned outlen; + const char *mech; + saslResult = sasl_client_start(m_pConnection, saslMechanismToUse.c_str(), NULL /** no prompt */, &out, &outlen, + &mech); + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "SaslAuthenticatorImpl::init: sasl_client_start code: " + << saslResult << std::endl;) + if (saslResult != SASL_OK && saslResult != SASL_CONTINUE) return saslResult; + + // prepare response + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "SaslAuthenticatorImpl::init: chosen: " << authMechanismToUse << std::endl;) + response.set_mechanism(authMechanismToUse); + response.set_data(NULL == out ? "" : out, outlen); + response.set_status(exec::shared::SASL_START); + return saslResult; +} + +int SaslAuthenticatorImpl::step(const exec::shared::SaslMessage& challenge, exec::shared::SaslMessage& response) const { + const char *in = challenge.data().c_str(); + const unsigned inlen = challenge.data().length(); + const char *out; + unsigned outlen; + const int saslResult = sasl_client_step(m_pConnection, in, inlen, NULL /** no prompt */, &out, &outlen); + switch (saslResult) { + case SASL_CONTINUE: + response.set_data(out, outlen); + response.set_status(exec::shared::SASL_IN_PROGRESS); + break; + case SASL_OK: + response.set_data(out, outlen); + response.set_status(exec::shared::SASL_SUCCESS); + break; + default: + response.set_status(exec::shared::SASL_FAILED); + break; + } + DRILL_MT_LOG(DRILL_LOG(LOG_TRACE) << "SaslAuthenticatorImpl::step: result: " << saslResult << std::endl;) + return saslResult; +} + +} /* namespace Drill */ http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/src/clientlib/saslAuthenticatorImpl.hpp ---------------------------------------------------------------------- diff --git a/contrib/native/client/src/clientlib/saslAuthenticatorImpl.hpp b/contrib/native/client/src/clientlib/saslAuthenticatorImpl.hpp new file mode 100644 index 0000000..5e36ee1 --- /dev/null +++ b/contrib/native/client/src/clientlib/saslAuthenticatorImpl.hpp @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DRILLCLIENT_SASLAUTHENTICATORIMPL_HPP +#define DRILLCLIENT_SASLAUTHENTICATORIMPL_HPP + +#include +#include +#include +#include "drill/drillClient.hpp" +#include "UserBitShared.pb.h" + +#include "sasl/sasl.h" +#include "sasl/saslplug.h" + +namespace Drill { + +class SaslAuthenticatorImpl { + +public: + + SaslAuthenticatorImpl(const DrillUserProperties *const properties); + + ~SaslAuthenticatorImpl(); + + int init(const std::vector& mechanisms, exec::shared::SaslMessage& response); + + int step(const exec::shared::SaslMessage& challenge, exec::shared::SaslMessage& response) const; + +private: + + static const std::map MECHANISM_MAPPING; + + static boost::mutex s_mutex; + static bool s_initialized; + + const DrillUserProperties *const m_pUserProperties; + sasl_conn_t *m_pConnection; + std::string m_username; + sasl_secret_t *m_ppwdSecret; + + static int passwordCallback(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret); + + static int userNameCallback(void *context, int id, const char **result, unsigned int *len); + +}; + +} /* namespace Drill */ + +#endif //DRILLCLIENT_SASLAUTHENTICATORIMPL_HPP http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/src/include/drill/common.hpp ---------------------------------------------------------------------- diff --git a/contrib/native/client/src/include/drill/common.hpp b/contrib/native/client/src/include/drill/common.hpp index 6d3816e..ed0a1ed 100644 --- a/contrib/native/client/src/include/drill/common.hpp +++ b/contrib/native/client/src/include/drill/common.hpp @@ -166,6 +166,9 @@ typedef enum{ #define USERPROP_FILEPATH "pemLocation" // Not implemented yet #define USERPROP_FILENAME "pemFile" // Not implemented yet #define USERPROP_IMPERSONATION_TARGET "impersonation_target" +#define USERPROP_AUTH_MECHANISM "auth" +#define USERPROP_SERVICE_NAME "service_name" +#define USERPROP_SERVICE_HOST "service_host" // Bitflags to describe user properties // Used in DrillUserProperties::USER_PROPERTIES http://git-wip-us.apache.org/repos/asf/drill/blob/3c3b08c5/contrib/native/client/src/include/drill/drillClient.hpp ---------------------------------------------------------------------- diff --git a/contrib/native/client/src/include/drill/drillClient.hpp b/contrib/native/client/src/include/drill/drillClient.hpp index 00ff723..01c9f67 100644 --- a/contrib/native/client/src/include/drill/drillClient.hpp +++ b/contrib/native/client/src/include/drill/drillClient.hpp @@ -80,6 +80,8 @@ class DECLSPEC_DRILL_CLIENT DrillClientConfig{ ~DrillClientConfig(); static void initLogging(const char* path); static void setLogLevel(logLevel_t l); + static void setSaslPluginPath(const char* path); + static const char* getSaslPluginPath(); static void setBufferLimit(uint64_t l); static uint64_t getBufferLimit(); static void setSocketTimeout(int32_t l); @@ -135,6 +137,8 @@ class DECLSPEC_DRILL_CLIENT DrillClientConfig{ // For future use. Currently, not enforced. static uint64_t s_bufferLimit; + static const char* s_saslPluginPath; + /** * DrillClient configures timeout (in seconds) in a fine granularity. * Disabled by setting the value to zero.