Skip to content

Commit

Permalink
DRILL-5943: Avoid the strong check introduced by DRILL-5582 for PLAIN…
Browse files Browse the repository at this point in the history
… mechanism

This closes #1028
  • Loading branch information
Sorabh Hamirwasia authored and parthchandra committed Nov 22, 2017
1 parent f8bbb75 commit 0e4136f
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 31 deletions.
18 changes: 12 additions & 6 deletions contrib/native/client/src/clientlib/drillClientImpl.cpp
Expand Up @@ -520,17 +520,23 @@ bool DrillClientImpl::clientNeedsEncryption(const DrillUserProperties* userPrope


/* /*
* Checks if the client has explicitly expressed interest in authenticated connections only. * Checks if the client has explicitly expressed interest in authenticated connections only.
* If the USERPROP_PASSWORD or USERPROP_AUTH_MECHANISM connection string properties are * If the USERPROP_AUTH_MECHANISM connection string properties is non-empty and not equal to PLAIN,
* non-empty, then it is implied that the client wants authentication. * then it is implied that the client wants authentication.
*
* Explicitly skipping PLAIN to maintain forward compatibility with 1.9 Drillbit and it doesn't matter
* if this security check is not there for PLAIN mechanism
*/ */
bool DrillClientImpl::clientNeedsAuthentication(const DrillUserProperties* userProperties) { bool DrillClientImpl::clientNeedsAuthentication(const DrillUserProperties* userProperties) {
bool needsAuthentication = false; bool needsAuthentication = false;
if(!userProperties) { return false; } if(!userProperties) { return needsAuthentication; }
std::string passwd = "";
userProperties->getProp(USERPROP_PASSWORD, passwd);
std::string authMech = ""; std::string authMech = "";
userProperties->getProp(USERPROP_AUTH_MECHANISM, authMech); userProperties->getProp(USERPROP_AUTH_MECHANISM, authMech);
return !passwd.empty() || !authMech.empty(); boost::algorithm::to_lower(authMech);

if(!authMech.empty() && SaslAuthenticatorImpl::PLAIN_NAME != authMech) {
needsAuthentication = true;
}
return needsAuthentication;
} }


connectionStatus_t DrillClientImpl::validateHandshake(DrillUserProperties* properties){ connectionStatus_t DrillClientImpl::validateHandshake(DrillUserProperties* properties){
Expand Down
22 changes: 12 additions & 10 deletions contrib/native/client/src/clientlib/saslAuthenticatorImpl.cpp
Expand Up @@ -28,16 +28,16 @@


namespace Drill { namespace Drill {


static const std::string DEFAULT_SERVICE_NAME = "drill"; const std::string DEFAULT_SERVICE_NAME = "drill";
const int PREFERRED_MIN_SSF = 56;
const std::string SaslAuthenticatorImpl::KERBEROS_SIMPLE_NAME = "kerberos";
const std::string SaslAuthenticatorImpl::PLAIN_NAME = "plain";


static const std::string KERBEROS_SIMPLE_NAME = "kerberos"; const std::string KERBEROS_SASL_NAME = "gssapi";
static const std::string KERBEROS_SASL_NAME = "gssapi";
static const std::string PLAIN_NAME = "plain";
static const int PREFERRED_MIN_SSF = 56;


const std::map<std::string, std::string> SaslAuthenticatorImpl::MECHANISM_MAPPING = boost::assign::map_list_of const std::map<std::string, std::string> SaslAuthenticatorImpl::MECHANISM_MAPPING = boost::assign::map_list_of
(KERBEROS_SIMPLE_NAME, KERBEROS_SASL_NAME) (SaslAuthenticatorImpl::KERBEROS_SIMPLE_NAME, KERBEROS_SASL_NAME)
(PLAIN_NAME, PLAIN_NAME) (SaslAuthenticatorImpl::PLAIN_NAME, SaslAuthenticatorImpl::PLAIN_NAME)
; ;


boost::mutex SaslAuthenticatorImpl::s_mutex; boost::mutex SaslAuthenticatorImpl::s_mutex;
Expand Down Expand Up @@ -138,15 +138,17 @@ int SaslAuthenticatorImpl::init(const std::vector<std::string>& mechanisms, exec
m_ppwdSecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + length); m_ppwdSecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + length);
std::memcpy(m_ppwdSecret->data, value.c_str(), length); std::memcpy(m_ppwdSecret->data, value.c_str(), length);
m_ppwdSecret->len = length; m_ppwdSecret->len = length;
authMechanismToUse = PLAIN_NAME; authMechanismToUse = SaslAuthenticatorImpl::PLAIN_NAME;
} else if (USERPROP_USERNAME == key) { } else if (USERPROP_USERNAME == key) {
m_username = value; m_username = value;
} else if (USERPROP_AUTH_MECHANISM == key) { } else if (USERPROP_AUTH_MECHANISM == key) {
authMechanismToUse = value; authMechanismToUse = value;
} }
} }
// clientNeedsAuthentication() cannot be false if the code above picks an authMechanism // clientNeedsAuthentication() cannot be false if the code above picks an authMechanism other than PLAIN
assert (authMechanismToUse.empty() || DrillClientImpl::clientNeedsAuthentication(m_pUserProperties)); assert (authMechanismToUse.empty() || authMechanismToUse == SaslAuthenticatorImpl::PLAIN_NAME ||
DrillClientImpl::clientNeedsAuthentication(m_pUserProperties));

m_authMechanismName = authMechanismToUse; m_authMechanismName = authMechanismToUse;
if (authMechanismToUse.empty()) return SASL_NOMECH; if (authMechanismToUse.empty()) return SASL_NOMECH;


Expand Down
4 changes: 4 additions & 0 deletions contrib/native/client/src/clientlib/saslAuthenticatorImpl.hpp
Expand Up @@ -59,6 +59,10 @@ class SaslAuthenticatorImpl {


const char *getErrorMessage(int errorCode); const char *getErrorMessage(int errorCode);


static const std::string KERBEROS_SIMPLE_NAME;

static const std::string PLAIN_NAME;

private: private:


static const std::map<std::string, std::string> MECHANISM_MAPPING; static const std::map<std::string, std::string> MECHANISM_MAPPING;
Expand Down
Expand Up @@ -251,7 +251,7 @@ private void validateSaslCompatibility(DrillProperties properties) throws NonTra
} }


// Check if client needs authentication and server doesn't support any security mechanisms. // Check if client needs authentication and server doesn't support any security mechanisms.
if (clientNeedsAuth(properties) && serverAuthMechanisms == null) { if (clientNeedsAuthExceptPlain(properties) && serverAuthMechanisms == null) {
throw new NonTransientRpcException( throw new NonTransientRpcException(
"Client needs authentication but server doesn't support any security mechanisms." + "Client needs authentication but server doesn't support any security mechanisms." +
" Please contact your administrator. [Warn: It may be due to wrong config or a security attack in" + " Please contact your administrator. [Warn: It may be due to wrong config or a security attack in" +
Expand All @@ -262,19 +262,22 @@ private void validateSaslCompatibility(DrillProperties properties) throws NonTra
/** /**
* Determine if client needs authenticated connection or not. It checks for following as an indication of * Determine if client needs authenticated connection or not. It checks for following as an indication of
* requiring authentication from client side: * requiring authentication from client side:
* 1) Any auth mechanism is provided in connection properties with DrillProperties.AUTH_MECHANISM parameter. * 1) Auth mechanism except PLAIN is provided in connection properties with DrillProperties.AUTH_MECHANISM parameter.
* 2) A service principal is provided in connection properties in which case we treat AUTH_MECHANISM as Kerberos * 2) A service principal is provided in connection properties in which case we treat AUTH_MECHANISM as Kerberos
* 3) Username and Password is provided in connection properties in which case we treat AUTH_MECHANISM as Plain
* @param props - DrillClient connection parameters * @param props - DrillClient connection parameters
* @return - true - If any of above 3 checks is successful. * @return - true - If any of above 2 checks is successful.
* - false - If all the above 3 checks failed. * - false - If all the above 2 checks fails.
*/ */
private boolean clientNeedsAuth(DrillProperties props) { private boolean clientNeedsAuthExceptPlain(DrillProperties props) {


return !Strings.isNullOrEmpty(props.getProperty(DrillProperties.AUTH_MECHANISM)) || boolean clientNeedsAuth = false;
!Strings.isNullOrEmpty(props.getProperty(DrillProperties.SERVICE_PRINCIPAL)) || final String authMechanism = props.getProperty(DrillProperties.AUTH_MECHANISM);
(props.containsKey(DrillProperties.USER) && !Strings.isNullOrEmpty(props.getProperty(DrillProperties.PASSWORD))); if (!Strings.isNullOrEmpty(authMechanism) && !authMechanism.equalsIgnoreCase(PlainFactory.SIMPLE_NAME)) {
clientNeedsAuth = true;
}


clientNeedsAuth |= !Strings.isNullOrEmpty(props.getProperty(DrillProperties.SERVICE_PRINCIPAL));
return clientNeedsAuth;
} }


private CheckedFuture<Void, RpcException> connect(final UserToBitHandshake handshake, private CheckedFuture<Void, RpcException> connect(final UserToBitHandshake handshake,
Expand Down
Expand Up @@ -321,13 +321,17 @@ public BitToUserHandshake getHandshakeResponse(UserToBitHandshake inbound) throw
return respBuilder.build(); return respBuilder.build();
} }


final boolean clientSupportsSasl = inbound.hasSaslSupport() && // If sasl_support field is absent in handshake message then treat the client as < 1.10 client
(inbound.getSaslSupport().ordinal() > SaslSupport.UNKNOWN_SASL_SUPPORT.ordinal()); final boolean clientSupportsSasl = inbound.hasSaslSupport();


// saslSupportOrdinal will be set to UNKNOWN_SASL_SUPPORT, if sasl_support field in handshake is set to a
// value which is unknown to this server. We will treat those clients as one which knows SASL protocol.
final int saslSupportOrdinal = (clientSupportsSasl) ? inbound.getSaslSupport().ordinal() final int saslSupportOrdinal = (clientSupportsSasl) ? inbound.getSaslSupport().ordinal()
: SaslSupport.UNKNOWN_SASL_SUPPORT.ordinal(); : SaslSupport.UNKNOWN_SASL_SUPPORT.ordinal();


if (saslSupportOrdinal <= SaslSupport.SASL_AUTH.ordinal() && config.isEncryptionEnabled()) { // Check if client doesn't support SASL or only supports SASL_AUTH and server has encryption enabled
if ((!clientSupportsSasl || saslSupportOrdinal == SaslSupport.SASL_AUTH.ordinal())
&& config.isEncryptionEnabled()) {
throw new UserAuthenticationException("The server doesn't allow client without encryption support." + throw new UserAuthenticationException("The server doesn't allow client without encryption support." +
" Please upgrade your client or talk to your system administrator."); " Please upgrade your client or talk to your system administrator.");
} }
Expand Down
Expand Up @@ -62,12 +62,12 @@ public static void setup() {
} }


/** /**
* Test showing failure before SASL handshake when Drillbit is not configured for authentication whereas client * Test showing when Drillbit is not configured for authentication whereas client explicitly requested for PLAIN
* explicitly requested for authentication. * authentication then connection succeeds without authentication.
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void testDisableDrillbitAuth_EnableClientAuth() throws Exception { public void testDisableDrillbitAuth_EnableClientAuthPlain() throws Exception {


final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties()) final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED, .withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
Expand All @@ -78,6 +78,29 @@ public void testDisableDrillbitAuth_EnableClientAuth() throws Exception {
connectionProps.setProperty(DrillProperties.USER, "anonymous"); connectionProps.setProperty(DrillProperties.USER, "anonymous");
connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!"); connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!");


try {
updateTestCluster(1, newConfig, connectionProps);
} catch (Exception ex) {
fail();
}
}

/**
* Test showing when Drillbit is not configured for authentication whereas client explicitly requested for Kerberos
* authentication then connection fails due to new check before SASL Handshake.
* @throws Exception
*/
@Test
public void testDisableDrillbitAuth_EnableClientAuthKerberos() throws Exception {

final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(false)),
false);

final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.AUTH_MECHANISM, "kerberos");

try { try {
updateTestCluster(1, newConfig, connectionProps); updateTestCluster(1, newConfig, connectionProps);
fail(); fail();
Expand Down Expand Up @@ -193,6 +216,8 @@ public void testEnableDrillbitEncryption_DisableClientAuth() throws Exception {
false); false);


final Properties connectionProps = new Properties(); final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.USER, "anonymous");
connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!");


try { try {
updateTestCluster(1, newConfig, connectionProps); updateTestCluster(1, newConfig, connectionProps);
Expand Down

0 comments on commit 0e4136f

Please sign in to comment.