Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
Expand Down Expand Up @@ -87,7 +88,6 @@ class SQLJDBCDriverConfig extends Configuration {
else {
Map<String, String> confDetails = new HashMap<String, String>();
confDetails.put("useTicketCache", "true");
confDetails.put("doNotPrompt", "true");
appConf = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, confDetails);
if (authLogger.isLoggable(Level.FINER))
Expand Down Expand Up @@ -140,18 +140,36 @@ private void intAuthInit() throws SQLServerException {
}
else {
Subject currentSubject = null;
KerbCallback callback = new KerbCallback(con);
try {
AccessControlContext context = AccessController.getContext();
currentSubject = Subject.getSubject(context);
if (null == currentSubject) {
lc = new LoginContext(CONFIGNAME);
lc = new LoginContext(CONFIGNAME, callback);
lc.login();
// per documentation LoginContext will instantiate a new subject.
currentSubject = lc.getSubject();
}
}
catch (LoginException le) {
con.terminate(SQLServerException.DRIVER_ERROR_NONE, SQLServerException.getErrString("R_integratedAuthenticationFailed"), le);
if (authLogger.isLoggable(Level.FINE)) {
authLogger.fine(toString() + "Failed to login using Kerberos due to " + le.getClass().getName() + ":" + le.getMessage());
}
try {
// Not very clean since it raises an Exception, but we are sure we are cleaning well everything
con.terminate(SQLServerException.DRIVER_ERROR_NONE, SQLServerException.getErrString("R_integratedAuthenticationFailed"), le);
} catch (SQLServerException alwaysTriggered) {
String message = MessageFormat.format(SQLServerException.getErrString("R_kerberosLoginFailed"),
alwaysTriggered.getMessage(), le.getClass().getName(), le.getMessage());
if (callback.getUsernameRequested() != null) {
message = MessageFormat.format(SQLServerException.getErrString("R_kerberosLoginFailedForUsername"),
callback.getUsernameRequested(), message);
}
// By throwing Exception with LOGON_FAILED -> we avoid looping for connection
// In this case, authentication will never work anyway -> fail fast
throw new SQLServerException(message, alwaysTriggered.getSQLState(), SQLServerException.LOGON_FAILED, le);
}
return;
}

if (authLogger.isLoggable(Level.FINER)) {
Expand Down
61 changes: 61 additions & 0 deletions src/main/java/com/microsoft/sqlserver/jdbc/KerbCallback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.microsoft.sqlserver.jdbc;

import java.io.IOException;
import java.util.Arrays;
import java.util.Properties;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

public class KerbCallback implements CallbackHandler {

private final SQLServerConnection con;
private String usernameRequested = null;

KerbCallback(SQLServerConnection con) {
this.con = con;
}

private static String getAnyOf(Callback callback,
Properties properties,
String... names) throws UnsupportedCallbackException {
for (String name : names) {
String val = properties.getProperty(name);
if (val != null && !val.trim().isEmpty()) {
return val;
}
}
throw new UnsupportedCallbackException(callback, "Cannot get any of properties: " + Arrays.toString(names) + " from con properties");
}

/**
* If a name was retrieved By Kerberos, return it.
*
* @return null if callback was not called or username was not provided
*/
public String getUsernameRequested() {
return usernameRequested;
}

@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
Callback callback = callbacks[i];
if (callback instanceof NameCallback) {
usernameRequested = getAnyOf(callback, con.activeConnectionProperties, "user", SQLServerDriverStringProperty.USER.name());
((NameCallback) callback).setName(usernameRequested);
}
else if (callback instanceof PasswordCallback) {
String password = getAnyOf(callback, con.activeConnectionProperties, "password", SQLServerDriverStringProperty.PASSWORD.name());
((PasswordCallback) callbacks[i]).setPassword(password.toCharArray());

}
else {
throw new UnsupportedCallbackException(callback, "Unrecognized Callback type: " + callback.getClass());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -380,5 +380,7 @@ protected Object[][] getContents() {
{"R_invalidFipsEncryptConfig", "Could not enable FIPS due to either encrypt is not true or using trusted certificate settings."},
{"R_invalidFipsProviderConfig", "Could not enable FIPS due to invalid FIPSProvider or TrustStoreType."},
{"R_serverPreparedStatementDiscardThreshold", "The serverPreparedStatementDiscardThreshold {0} is not valid."},
{"R_kerberosLoginFailedForUsername", "Cannot login with Kerberos principal {0}, check your credentials. {1}"},
{"R_kerberosLoginFailed", "Kerberos Login failed: {0} due to {1} ({2})"},
};
}