From 7819c9c298ddee1da300014fdbe3937b01b7dbc9 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 27 Jun 2019 15:38:56 -0700 Subject: [PATCH] Update SQLServerConnection to store password hash for NTLM (#1095) * update SQLServerConnection to store ntlm password hash --- .../sqlserver/jdbc/NTLMAuthentication.java | 20 +++++++++++++------ .../sqlserver/jdbc/SQLServerConnection.java | 12 ++++++++--- .../sqlserver/jdbc/NTLMConnectionTest.java | 2 +- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/NTLMAuthentication.java b/src/main/java/com/microsoft/sqlserver/jdbc/NTLMAuthentication.java index 01a1fe510..8edc8fd2f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/NTLMAuthentication.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/NTLMAuthentication.java @@ -283,7 +283,7 @@ private class NTLMContext { * if error occurs */ NTLMContext(final SQLServerConnection con, final String domainName, final String userName, - final String password, final String workstation) throws SQLServerException { + final byte[] passwordHash, final String workstation) throws SQLServerException { this.domainName = domainName.toUpperCase(); this.domainUbytes = unicode(this.domainName); @@ -291,7 +291,7 @@ private class NTLMContext { this.userNameUbytes = null != userName ? unicode(userName) : null; this.upperUserName = null != userName ? userName.toUpperCase() : null; - this.passwordHash = null != password ? md4(unicode(password)) : null; + this.passwordHash = passwordHash; this.workstation = workstation; @@ -332,9 +332,9 @@ private class NTLMContext { * if error occurs */ NTLMAuthentication(final SQLServerConnection con, final String domainName, final String userName, - final String password, final String workstation) throws SQLServerException { + final byte[] passwordHash, final String workstation) throws SQLServerException { if (null == context) { - this.context = new NTLMContext(con, domainName, userName, password, workstation); + this.context = new NTLMContext(con, domainName, userName, passwordHash, workstation); } } @@ -612,7 +612,7 @@ private byte[] hmacMD5(final byte[] key, final byte[] data) throws InvalidKeyExc * input string * @return MD4 hash */ - private byte[] md4(final byte[] str) { + private static byte[] md4(final byte[] str) { MD4 md = new MD4(); md.reset(); md.update(str); @@ -626,7 +626,7 @@ private byte[] md4(final byte[] str) { * string to convert to unicode * @return unicode of string */ - private byte[] unicode(final String str) { + private static byte[] unicode(final String str) { return (null != str) ? str.getBytes(java.nio.charset.StandardCharsets.UTF_16LE) : null; } @@ -890,4 +890,12 @@ private byte[] generateNtlmNegotiate() { return msg; } + + public static byte[] getNtlmPasswordHash(String password) throws SQLServerException { + if (null == password) { + throw new SQLServerException(SQLServerException.getErrString("R_NtlmNoUserPasswordDomain"), null); + } + + return md4(unicode(password)); + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index c9f2c3948..2fe9dfde0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -832,6 +832,8 @@ static synchronized List getColumnEncryptionTrustedMasterKeyPaths(String Properties activeConnectionProperties; // the active set of connection properties private boolean integratedSecurity = SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.getDefaultValue(); private boolean ntlmAuthentication = false; + private byte[] ntlmPasswordHash = null; + private AuthenticationScheme intAuthScheme = AuthenticationScheme.nativeAuthentication; private GSSCredential impersonatedUserCred; private boolean isUserCreatedCredential; @@ -3691,12 +3693,16 @@ private void logon(LogonCommand command) throws SQLServerException { currentConnectPlaceHolder.getPortNumber()); } } else if (ntlmAuthentication) { + if (null == ntlmPasswordHash) { + ntlmPasswordHash = NTLMAuthentication.getNtlmPasswordHash( + activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString())); + activeConnectionProperties.remove(SQLServerDriverStringProperty.PASSWORD.toString()); + } + authentication = new NTLMAuthentication(this, activeConnectionProperties.getProperty(SQLServerDriverStringProperty.DOMAIN.toString()), activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString()), - activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString()), - hostName); - activeConnectionProperties.remove(SQLServerDriverStringProperty.PASSWORD.toString()); + ntlmPasswordHash, hostName); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/NTLMConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/NTLMConnectionTest.java index c5c7cb0ca..69efd80fd 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/NTLMConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/NTLMConnectionTest.java @@ -326,7 +326,7 @@ private void getServerFqdn(SQLServerConnection con) { private void sendBadToken(byte[] badField, int offset) throws SQLException { try (SQLServerConnection con = (SQLServerConnection) PrepUtil.getConnection(connectionStringNTLM)) { getServerFqdn(con); - SSPIAuthentication auth = new NTLMAuthentication(con, "domainName", "userName", "password", "hostname"); + SSPIAuthentication auth = new NTLMAuthentication(con, "domainName", "userName", new byte[1], "hostname"); boolean[] done = {false}; byte[] badToken = getChallengeToken(offset, badField); auth.generateClientContext(badToken, done);