Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using multiple JAAS configurations and override the configuration per connection properties #254

Merged
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
65 changes: 65 additions & 0 deletions src/main/java/com/microsoft/sqlserver/jdbc/JaasConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Microsoft JDBC Driver for SQL Server
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
package com.microsoft.sqlserver.jdbc;

import java.util.HashMap;
import java.util.Map;

import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;

/**
* This class overrides JAAS Configuration and always provide a configuration is not defined for default configuration.
*/
public class JaasConfiguration extends Configuration {

private final Configuration delegate;
private AppConfigurationEntry[] defaultValue;

private static AppConfigurationEntry[] generateDefaultConfiguration() {
if (Util.isIBM()) {
Map<String, String> confDetailsWithoutPassword = new HashMap<String, String>();
confDetailsWithoutPassword.put("useDefaultCcache", "true");
Map<String, String> confDetailsWithPassword = new HashMap<String, String>();
// We generated a two configurations fallback that is suitable for password and password-less authentication
// See https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.security.component.80.doc/security-component/jgssDocs/jaas_login_user.html
final String ibmLoginModule = "com.ibm.security.auth.module.Krb5LoginModule";
return new AppConfigurationEntry[] {
new AppConfigurationEntry(ibmLoginModule, AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT, confDetailsWithoutPassword),
new AppConfigurationEntry(ibmLoginModule, AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT, confDetailsWithPassword)};
}
else {
Map<String, String> confDetails = new HashMap<String, String>();
confDetails.put("useTicketCache", "true");
return new AppConfigurationEntry[] {new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, confDetails)};
}
}

/**
* Package protected constructor.
*
* @param delegate
* a possibly null delegate
*/
JaasConfiguration(Configuration delegate) {
this.delegate = delegate;
this.defaultValue = generateDefaultConfiguration();
}

@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
AppConfigurationEntry[] conf = delegate == null ? null : delegate.getAppConfigurationEntry(name);
// We return our configuration only if user requested default one
// In case where user did request another JAAS Configuration name, we expect he knows what he is doing.
if (conf == null && name.equals(SQLServerDriverStringProperty.JAAS_CONFIG_NAME.getDefaultValue())) {
return defaultValue;
}
return conf;
}
}
83 changes: 6 additions & 77 deletions src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.NamingException;
import javax.security.auth.Subject;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
Expand All @@ -44,7 +41,6 @@
* KerbAuthentication for int auth.
*/
final class KerbAuthentication extends SSPIAuthentication {
private final static String CONFIGNAME = "SQLJDBCDriver";
private final static java.util.logging.Logger authLogger = java.util.logging.Logger
.getLogger("com.microsoft.sqlserver.jdbc.internals.KerbAuthentication");

Expand All @@ -57,78 +53,9 @@ final class KerbAuthentication extends SSPIAuthentication {
private GSSContext peerContext = null;

static {
// The driver on load will look to see if there is a configuration set for the SQLJDBCDriver, if not it will install its
// own configuration. Note it is possible that there is a configuration exists but it does not contain a configuration entry
// for the driver in that case, we will override the configuration but will flow the configuration requests to existing
// config for anything other than SQLJDBCDriver
//
class SQLJDBCDriverConfig extends Configuration {
Configuration current = null;
AppConfigurationEntry[] driverConf;

SQLJDBCDriverConfig() {
try {
current = Configuration.getConfiguration();
}
catch (SecurityException e) {
// if we cant get the configuration, it is likely that no configuration has been specified. So go ahead and set the config
authLogger.finer(toString() + " No configurations provided, setting driver default");
}
AppConfigurationEntry[] config = null;

if (null != current) {
config = current.getAppConfigurationEntry(CONFIGNAME);
}
// If there is user provided configuration we leave use that and not install our configuration
if (null == config) {
if (authLogger.isLoggable(Level.FINER))
authLogger.finer(toString() + " SQLJDBCDriver configuration entry is not provided, setting driver default");

AppConfigurationEntry appConf;
if (Util.isIBM()) {
Map<String, String> confDetails = new HashMap<String, String>();
confDetails.put("useDefaultCcache", "true");
confDetails.put("moduleBanner", "false");
appConf = new AppConfigurationEntry("com.ibm.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, confDetails);
if (authLogger.isLoggable(Level.FINER))
authLogger.finer(toString() + " Setting IBM Krb5LoginModule");
}
else {
Map<String, String> confDetails = new HashMap<String, String>();
confDetails.put("useTicketCache", "true");
appConf = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, confDetails);
if (authLogger.isLoggable(Level.FINER))
authLogger.finer(toString() + " Setting Sun Krb5LoginModule");
}
driverConf = new AppConfigurationEntry[1];
driverConf[0] = appConf;
Configuration.setConfiguration(this);
}

}

public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
// we should only handle anything that is related to our part, everything else is handled by the configuration
// already existing configuration if there is one.
if (name.equals(CONFIGNAME)) {
return driverConf;
}
else {
if (null != current)
return current.getAppConfigurationEntry(name);
else
return null;
}
}

public void refresh() {
if (null != current)
current.refresh();
}
}
SQLJDBCDriverConfig driverconfig = new SQLJDBCDriverConfig();
// Overrides the default JAAS configuration loader.
// This one will forward to the default one in all cases but the default configuration is empty.
Configuration.setConfiguration(new JaasConfiguration(Configuration.getConfiguration()));
}

private void intAuthInit() throws SQLServerException {
Expand All @@ -148,13 +75,15 @@ private void intAuthInit() throws SQLServerException {
peerContext.requestInteg(true);
}
else {
String configName = con.activeConnectionProperties.getProperty(SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(),
SQLServerDriverStringProperty.JAAS_CONFIG_NAME.getDefaultValue());
Subject currentSubject = null;
KerbCallback callback = new KerbCallback(con);
try {
AccessControlContext context = AccessController.getContext();
currentSubject = Subject.getSubject(context);
if (null == currentSubject) {
lc = new LoginContext(CONFIGNAME, callback);
lc = new LoginContext(configName, callback);
lc.login();
// per documentation LoginContext will instantiate a new subject.
currentSubject = lc.getSubject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,27 @@ public int getSocketTimeout() {
return getIntProperty(connectionProps, SQLServerDriverIntProperty.SOCKET_TIMEOUT.toString(), defaultTimeOut);
}

/**
* Sets the login configuration file for Kerberos authentication. This
* overrides the default configuration <i> SQLJDBCDriver </i>
*
* @param configurationName
*/
public void setJASSConfigurationName(String configurationName) {
setStringProperty(connectionProps, SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(),
configurationName);
}

/**
* Retrieves the login configuration file for Kerberos authentication.
*
* @return
*/
public String getJASSConfigurationName() {
return getStringProperty(connectionProps, SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(),
SQLServerDriverStringProperty.JAAS_CONFIG_NAME.getDefaultValue());
}

// responseBuffering controls the driver's buffering of responses from SQL Server.
// Possible values are:
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ public String toString() {
}
}



enum SQLServerDriverStringProperty
{
APPLICATION_INTENT ("applicationIntent", ApplicationIntent.READ_WRITE.toString()),
Expand All @@ -226,6 +228,7 @@ enum SQLServerDriverStringProperty
FAILOVER_PARTNER ("failoverPartner", ""),
HOSTNAME_IN_CERTIFICATE ("hostNameInCertificate", ""),
INSTANCE_NAME ("instanceName", ""),
JAAS_CONFIG_NAME ("jaasConfigurationName", "SQLJDBCDriver"),
PASSWORD ("password", ""),
RESPONSE_BUFFERING ("responseBuffering", "adaptive"),
SELECT_METHOD ("selectMethod", "direct"),
Expand Down Expand Up @@ -377,6 +380,7 @@ public final class SQLServerDriver implements java.sql.Driver {
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.FIPS.toString(), Boolean.toString(SQLServerDriverBooleanProperty.FIPS.getDefaultValue()), false, TRUE_FALSE),
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.toString(), Boolean.toString(SQLServerConnection.getDefaultEnablePrepareOnFirstPreparedStatementCall()), false, TRUE_FALSE),
new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.toString(), Integer.toString(SQLServerConnection.getDefaultServerPreparedStatementDiscardThreshold()), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.JAAS_CONFIG_NAME.toString(), SQLServerDriverStringProperty.JAAS_CONFIG_NAME.getDefaultValue(), false, null),
};

// Properties that can only be set by using Properties.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,5 +381,6 @@ protected Object[][] getContents() {
{"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})"},
{"R_jaasConfigurationNamePropertyDescription", "Login configuration file for Kerberos authentication."},
};
}