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

add useDefaultGSSCredential property #2177

Merged
merged 8 commits into from
Oct 16, 2023
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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ allprojects {

test {
useJUnitPlatform {
excludeTags (hasProperty('excludedGroups') ? excludedGroups : 'xSQLv15','xGradle','reqExternalSetup','NTLM','MSI','clientCertAuth','fedAuth')
excludeTags (hasProperty('excludedGroups') ? excludedGroups : 'xSQLv15','xGradle','reqExternalSetup','NTLM','MSI','clientCertAuth','fedAuth','kerberos')
}
}

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
xAzureSQLDW - - - - For tests not compatible with Azure Data Warehouse -
xAzureSQLMI - - - - For tests not compatible with Azure SQL Managed Instance
NTLM - - - - - - - For tests using NTLM Authentication mode (excluded by default)
Kerberos - - - - - For tests using Kerberos authentication (excluded by default)
kerberos - - - - - For tests using Kerberos authentication (excluded by default)
reqExternalSetup - For tests requiring external setup (excluded by default)
clientCertAuth - - For tests requiring client certificate authentication
setup (excluded by default) - - - - - - - - - - - - - - - - - - - - - - -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,17 +594,32 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource {
*/
String getServerSpn();

/**
* Sets the value to indicate whether useDefaultGSSCredential is enabled.
*
* @param enable
* true if useDefaultGSSCredential is enabled. Otherwise, false.
*/
void setUseDefaultGSSCredential(boolean enable);

/**
* Returns the useDefaultGSSCredential.
*
* @return if enabled, return true. Otherwise, false.
*/
boolean getUseDefaultGSSCredential();

/**
* Sets the GSSCredential.
*
*
* @param userCredential
* the credential
*/
void setGSSCredentials(GSSCredential userCredential);

/**
* Returns the GSSCredential.
*
*
* @return GSSCredential
*/
GSSCredential getGSSCredentials();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ final class KerbAuthentication extends SSPIAuthentication {
private LoginContext lc = null;
private boolean isUserCreatedCredential = false;
private GSSCredential peerCredentials = null;
private boolean useDefaultNativeGSSCredential = false;
private GSSContext peerContext = null;

static {
Expand All @@ -63,6 +64,10 @@ private void initAuthInit() throws SQLServerException {
// as it is.
GSSName remotePeerName = manager.createName(spn, null);

if (useDefaultNativeGSSCredential) {
peerCredentials = manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, kerberos, GSSCredential.INITIATE_ONLY);
}

if (null != peerCredentials) {
peerContext = manager.createContext(remotePeerName, kerberos, peerCredentials,
GSSContext.DEFAULT_LIFETIME);
Expand Down Expand Up @@ -220,10 +225,11 @@ private byte[] initAuthHandShake(byte[] pin, boolean[] done) throws SQLServerExc
* @param impersonatedUserCred
*/
KerbAuthentication(SQLServerConnection con, String address, int port, GSSCredential impersonatedUserCred,
boolean isUserCreated) {
boolean isUserCreated, boolean useDefaultNativeGSSCredential) {
this(con, address, port);
this.peerCredentials = impersonatedUserCred;
this.isUserCreatedCredential = isUserCreated;
this.useDefaultNativeGSSCredential = useDefaultNativeGSSCredential;
}

byte[] generateClientContext(byte[] pin, boolean[] done) throws SQLServerException {
Expand All @@ -235,9 +241,9 @@ byte[] generateClientContext(byte[] pin, boolean[] done) throws SQLServerExcepti

void releaseClientContext() {
try {
if (null != peerCredentials && !isUserCreatedCredential) {
if (null != peerCredentials && !isUserCreatedCredential && !useDefaultNativeGSSCredential) {
peerCredentials.dispose();
} else if (null != peerCredentials && isUserCreatedCredential) {
} else if (null != peerCredentials && (isUserCreatedCredential || useDefaultNativeGSSCredential)) {
peerCredentials = null;
}
if (null != peerContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ public final class SQLServerColumnEncryptionCertificateStoreProvider extends SQL
static final private java.util.logging.Logger windowsCertificateStoreLogger = java.util.logging.Logger
.getLogger("com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionCertificateStoreProvider");

static boolean isWindows;

String name = "MSSQL_CERTIFICATE_STORE";

static final String LOCAL_MACHINE_DIRECTORY = "LocalMachine";
Expand All @@ -29,14 +27,6 @@ public final class SQLServerColumnEncryptionCertificateStoreProvider extends SQL

private static final Lock lock = new ReentrantLock();

static {
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")) {
isWindows = true;
} else {
isWindows = false;
}
}

/**
* Constructs a SQLServerColumnEncryptionCertificateStoreProvider.
*/
Expand Down Expand Up @@ -67,7 +57,7 @@ public byte[] decryptColumnEncryptionKey(String masterKeyPath, String encryption
windowsCertificateStoreLogger.entering(SQLServerColumnEncryptionCertificateStoreProvider.class.getName(),
"decryptColumnEncryptionKey", "Decrypting Column Encryption Key.");
byte[] plainCek;
if (isWindows) {
if (SQLServerConnection.isWindows) {
plainCek = decryptColumnEncryptionKeyWindows(masterKeyPath, encryptionAlgorithm,
encryptedColumnEncryptionKey);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -983,8 +983,10 @@ IdleConnectionResiliency getSessionRecovery() {
/** global system ColumnEncryptionKeyStoreProviders */
static Map<String, SQLServerColumnEncryptionKeyStoreProvider> globalSystemColumnEncryptionKeyStoreProviders = new HashMap<>();

static boolean isWindows = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows");

static {
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")) {
if (isWindows) {
SQLServerColumnEncryptionCertificateStoreProvider provider = new SQLServerColumnEncryptionCertificateStoreProvider();
globalSystemColumnEncryptionKeyStoreProviders.put(provider.getName(), provider);
}
Expand Down Expand Up @@ -1426,6 +1428,9 @@ public static void clearUserTokenCache() {
/** integrated authentication scheme */
private AuthenticationScheme intAuthScheme = AuthenticationScheme.NATIVE_AUTHENTICATION;

/** use default native GSS-API Credential flag */
private boolean useDefaultGSSCredential = SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.getDefaultValue();

/** impersonated user credential */
private transient GSSCredential impersonatedUserCred;

Expand Down Expand Up @@ -2480,6 +2485,11 @@ Connection connectInternal(Properties propsIn,
impersonatedUserCred = (GSSCredential) activeConnectionProperties.get(sPropKey);
isUserCreatedCredential = true;
}
sPropKey = SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if(null != sPropValue && isWindows) {
useDefaultGSSCredential = isBooleanPropertyOn(sPropKey, sPropValue);
}
} else if (intAuthScheme == AuthenticationScheme.NTLM) {
String sPropKeyDomain = SQLServerDriverStringProperty.DOMAIN.toString();
String sPropValueDomain = activeConnectionProperties.getProperty(sPropKeyDomain);
Expand Down Expand Up @@ -5097,9 +5107,9 @@ private void logon(LogonCommand command) throws SQLServerException {
authentication = new AuthenticationJNI(this, currentConnectPlaceHolder.getServerName(),
currentConnectPlaceHolder.getPortNumber());
} else if (AuthenticationScheme.JAVA_KERBEROS == intAuthScheme) {
if (null != impersonatedUserCred) {
if (null != impersonatedUserCred || useDefaultGSSCredential) {
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(),
currentConnectPlaceHolder.getPortNumber(), impersonatedUserCred, isUserCreatedCredential);
currentConnectPlaceHolder.getPortNumber(), impersonatedUserCred, isUserCreatedCredential, useDefaultGSSCredential);
} else {
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(),
currentConnectPlaceHolder.getPortNumber());
Expand Down Expand Up @@ -5800,8 +5810,7 @@ private SqlAuthenticationToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throw
} else if (authenticationString
.equalsIgnoreCase(SqlAuthentication.ACTIVE_DIRECTORY_INTEGRATED.toString())) {
// If operating system is windows and mssql-jdbc_auth is loaded then choose the DLL authentication.
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")
&& AuthenticationJNI.isDllLoaded()) {
if (isWindows && AuthenticationJNI.isDllLoaded()) {
try {
FedAuthDllInfo dllInfo = AuthenticationJNI.getAccessTokenForWindowsIntegrated(
fedAuthInfo.stsurl, fedAuthInfo.spn, clientConnectionId.toString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,17 @@ public GSSCredential getGSSCredentials() {
SQLServerDriverObjectProperty.GSS_CREDENTIAL.getDefaultValue());
}

@Override
public void setUseDefaultGSSCredential(boolean enable) {
setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString(), enable);
}

@Override
public boolean getUseDefaultGSSCredential() {
return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString(),
SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.getDefaultValue());
}

@Override
public void setAccessToken(String accessToken) {
setStringProperty(connectionProps, SQLServerDriverStringProperty.ACCESS_TOKEN.toString(), accessToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,8 @@ enum SQLServerDriverBooleanProperty {
USE_FMT_ONLY("useFmtOnly", false),
SEND_TEMPORAL_DATATYPES_AS_STRING_FOR_BULK_COPY("sendTemporalDataTypesAsStringForBulkCopy", true),
DELAY_LOADING_LOBS("delayLoadingLobs", true),
USE_DEFAULT_JAAS_CONFIG("useDefaultJaasConfig", false);
USE_DEFAULT_JAAS_CONFIG("useDefaultJaasConfig", false),
USE_DEFAULT_GSS_CREDENTIAL("useDefaultGSSCredential", false);

private final String name;
private final boolean defaultValue;
Expand Down Expand Up @@ -748,7 +749,7 @@ public final class SQLServerDriver implements java.sql.Driver {
SQLServerDriverStringProperty.DATABASE_NAME.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.toString(),
Boolean.toString(SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.getDefaultValue()), false,
new String[] {"true", "false"}),
TRUE_FALSE),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.ENCRYPT.toString(),
SQLServerDriverStringProperty.ENCRYPT.getDefaultValue(), false,
new String[] {EncryptOption.FALSE.toString(), EncryptOption.NO.toString(),
Expand All @@ -768,6 +769,9 @@ public final class SQLServerDriver implements java.sql.Driver {
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.toString(),
Boolean.toString(SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.getDefaultValue()), false,
TRUE_FALSE),
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString(),
Boolean.toString(SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.getDefaultValue()), false,
TRUE_FALSE),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.toString(),
SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.getDefaultValue(), false,
new String[] {KeyStoreAuthentication.JAVA_KEYSTORE_PASSWORD.toString()}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ protected Object[][] getContents() {
{"R_lastUpdateCountPropertyDescription", "Ensures that only the last update count is returned from an SQL statement passed to the server."},
{"R_disableStatementPoolingPropertyDescription", "Disables the statement pooling feature."},
{"R_integratedSecurityPropertyDescription", "Indicates whether Windows authentication will be used to connect to SQL Server."},
{"R_useDefaultGSSCredentialPropertyDescription", "Indicates whether GSSCredential will be created using native GSS-API."},
{"R_authenticationSchemePropertyDescription", "The authentication scheme to be used for integrated authentication."},
{"R_lockTimeoutPropertyDescription", "The number of milliseconds to wait before the database reports a lock time-out."},
{"R_connectRetryCountPropertyDescription", "The number of reconnection attempts if there is a connection failure."},
Expand Down
55 changes: 35 additions & 20 deletions src/test/java/com/microsoft/sqlserver/jdbc/KerberosTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,65 +16,81 @@
import java.util.HashMap;
import java.util.Map;

@Tag(Constants.kerberos)
@RunWith(JUnitPlatform.class)
public class KerberosTest extends AbstractTest {

private static String kerberosAuth = "KERBEROS";
private static String authSchemeQuery = "select auth_scheme from sys.dm_exec_connections where session_id=@@spid";

@BeforeAll
public static void setupTests() throws Exception {
setConnection();
}

@Tag(Constants.Kerberos)
@Test
public void testUseDefaultJaasConfigConnectionStringPropertyTrue() throws Exception {
String connectionStringUseDefaultJaasConfig = connectionStringKerberos + ";useDefaultJaasConfig=true;";

// Initial connection should succeed with default JAAS config
try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionStringUseDefaultJaasConfig)) {
ResultSet rs = conn.createStatement().executeQuery("select auth_scheme from sys.dm_exec_connections where session_id=@@spid");
rs.next();
Assertions.assertEquals(kerberosAuth, rs.getString(1));
}
createKerberosConnection(connectionStringUseDefaultJaasConfig);

// Attempt to overwrite JAAS config. Since useDefaultJaasConfig=true, this should have no effect
// and subsequent connections should succeed.
overwriteJaasConfig();

// New connection should successfully connect and continue to use the default JAAS config.
try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionStringUseDefaultJaasConfig)) {
ResultSet rs = conn.createStatement().executeQuery("select auth_scheme from sys.dm_exec_connections where session_id=@@spid");
rs.next();
Assertions.assertEquals(kerberosAuth, rs.getString(1));
}
createKerberosConnection(connectionStringUseDefaultJaasConfig);
}

@Tag(Constants.Kerberos)
@Test
public void testUseDefaultJaasConfigConnectionStringPropertyFalse() throws Exception {

// useDefaultJaasConfig=false by default
// Initial connection should succeed with default JAAS config
try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionStringKerberos)) {
ResultSet rs = conn.createStatement().executeQuery("select auth_scheme from sys.dm_exec_connections where session_id=@@spid");
rs.next();
Assertions.assertEquals(kerberosAuth, rs.getString(1));
}
createKerberosConnection(connectionStringKerberos);

// Overwrite JAAS config. Since useDefaultJaasConfig=false, overwriting should succeed and have an effect.
// Subsequent connections will fail.
overwriteJaasConfig();

// New connection should fail as it is attempting to connect using an overwritten JAAS config.
try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionStringKerberos)) {
try {
createKerberosConnection(connectionStringKerberos);
Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown"));
} catch (SQLServerException e) {
Assertions.assertTrue(e.getMessage()
.contains(TestResource.getResource("R_noLoginModulesConfiguredForJdbcDriver")));
}
}

@Test
public void testUseDefaultNativeGSSCredential() throws Exception {
// This is a negative test. This test should fail as expected as the JVM arg "-Dsun.security.jgss.native=true"
// isn't provided.
String connectionString = connectionStringKerberos + ";useDefaultGSSCredential=true;";

try {
createKerberosConnection(connectionString);
Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown"));
} catch (SQLServerException e) {
Assertions.assertEquals(e.getCause().getMessage(), TestResource.getResource("R_kerberosNativeGSSFailure"));
}
}

@Test
public void testBasicKerberosAuth() throws Exception {
createKerberosConnection(connectionStringKerberos);
}

private static void createKerberosConnection(String connectionString) throws Exception {
try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString)) {
ResultSet rs = conn.createStatement().executeQuery(authSchemeQuery);
rs.next();
Assertions.assertEquals(kerberosAuth, rs.getString(1));
}
}

/**
* Overwrites the default JAAS config. Call before making a connection.
*/
tkyc marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -84,7 +100,7 @@ private static void overwriteJaasConfig() {
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
new HashMap<>());
Map<String, AppConfigurationEntry[]> configurationEntries = new HashMap<>();
configurationEntries.put("KAFKA_CLIENT_CONTEXT_NAME",
configurationEntries.put("CLIENT_CONTEXT_NAME",
new AppConfigurationEntry[] { kafkaClientConfigurationEntry });
Configuration.setConfiguration(new InternalConfiguration(configurationEntries));
}
Expand All @@ -100,6 +116,5 @@ private static class InternalConfiguration extends Configuration {
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
return this.configurationEntries.get(name);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ public void testDataSource() throws SQLServerException {
assertEquals(Boolean.toString(booleanPropValue), ds.getEncrypt(),
TestResource.getResource("R_valuesAreDifferent"));

ds.setUseDefaultGSSCredential(booleanPropValue);
assertEquals(booleanPropValue, ds.getUseDefaultGSSCredential(),
TestResource.getResource("R_valuesAreDifferent"));

ds.setServerCertificate(stringPropValue);
assertEquals(stringPropValue, ds.getServerCertificate(), TestResource.getResource("R_valuesAreDifferent"));

Expand Down
Loading
Loading