Skip to content

Commit 3f0f120

Browse files
zeotuanJeffery-Wastytkyc
authored
add useDefaultGSSCredential property (#2177)
* add useDefaultGSSCredential property * Fix wrong expected useDefaultGSSCredential value * use default GSS if useDefaultGssCredential true * Fix awkward R_useDefaultGSSCredentialPropertyDescription SqlServerResource Co-authored-by: Jeffery Wasty <[email protected]> * Update ISQLServerDataSource useDefaultGSSCredential doc string Co-authored-by: Jeffery Wasty <[email protected]> * Revert white space changes * Added tests * Corrected credential cleanup; Addressed PR comments --------- Co-authored-by: Tuan Pham <[email protected]> Co-authored-by: Jeffery Wasty <[email protected]> Co-authored-by: Terry Chow <[email protected]>
1 parent a3178ba commit 3f0f120

13 files changed

+102
-46
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ allprojects {
2929

3030
test {
3131
useJUnitPlatform {
32-
excludeTags (hasProperty('excludedGroups') ? excludedGroups : 'xSQLv15','xGradle','reqExternalSetup','NTLM','MSI','clientCertAuth','fedAuth')
32+
excludeTags (hasProperty('excludedGroups') ? excludedGroups : 'xSQLv15','xGradle','reqExternalSetup','NTLM','MSI','clientCertAuth','fedAuth','kerberos')
3333
}
3434
}
3535

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
xAzureSQLDW - - - - For tests not compatible with Azure Data Warehouse -
4444
xAzureSQLMI - - - - For tests not compatible with Azure SQL Managed Instance
4545
NTLM - - - - - - - For tests using NTLM Authentication mode (excluded by default)
46-
Kerberos - - - - - For tests using Kerberos authentication (excluded by default)
46+
kerberos - - - - - For tests using Kerberos authentication (excluded by default)
4747
reqExternalSetup - For tests requiring external setup (excluded by default)
4848
clientCertAuth - - For tests requiring client certificate authentication
4949
setup (excluded by default) - - - - - - - - - - - - - - - - - - - - - - -

src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,17 +594,32 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource {
594594
*/
595595
String getServerSpn();
596596

597+
/**
598+
* Sets the value to indicate whether useDefaultGSSCredential is enabled.
599+
*
600+
* @param enable
601+
* true if useDefaultGSSCredential is enabled. Otherwise, false.
602+
*/
603+
void setUseDefaultGSSCredential(boolean enable);
604+
605+
/**
606+
* Returns the useDefaultGSSCredential.
607+
*
608+
* @return if enabled, return true. Otherwise, false.
609+
*/
610+
boolean getUseDefaultGSSCredential();
611+
597612
/**
598613
* Sets the GSSCredential.
599-
*
614+
*
600615
* @param userCredential
601616
* the credential
602617
*/
603618
void setGSSCredentials(GSSCredential userCredential);
604619

605620
/**
606621
* Returns the GSSCredential.
607-
*
622+
*
608623
* @return GSSCredential
609624
*/
610625
GSSCredential getGSSCredentials();

src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ final class KerbAuthentication extends SSPIAuthentication {
3939
private LoginContext lc = null;
4040
private boolean isUserCreatedCredential = false;
4141
private GSSCredential peerCredentials = null;
42+
private boolean useDefaultNativeGSSCredential = false;
4243
private GSSContext peerContext = null;
4344

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

67+
if (useDefaultNativeGSSCredential) {
68+
peerCredentials = manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, kerberos, GSSCredential.INITIATE_ONLY);
69+
}
70+
6671
if (null != peerCredentials) {
6772
peerContext = manager.createContext(remotePeerName, kerberos, peerCredentials,
6873
GSSContext.DEFAULT_LIFETIME);
@@ -220,10 +225,11 @@ private byte[] initAuthHandShake(byte[] pin, boolean[] done) throws SQLServerExc
220225
* @param impersonatedUserCred
221226
*/
222227
KerbAuthentication(SQLServerConnection con, String address, int port, GSSCredential impersonatedUserCred,
223-
boolean isUserCreated) {
228+
boolean isUserCreated, boolean useDefaultNativeGSSCredential) {
224229
this(con, address, port);
225230
this.peerCredentials = impersonatedUserCred;
226231
this.isUserCreatedCredential = isUserCreated;
232+
this.useDefaultNativeGSSCredential = useDefaultNativeGSSCredential;
227233
}
228234

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

236242
void releaseClientContext() {
237243
try {
238-
if (null != peerCredentials && !isUserCreatedCredential) {
244+
if (null != peerCredentials && !isUserCreatedCredential && !useDefaultNativeGSSCredential) {
239245
peerCredentials.dispose();
240-
} else if (null != peerCredentials && isUserCreatedCredential) {
246+
} else if (null != peerCredentials && (isUserCreatedCredential || useDefaultNativeGSSCredential)) {
241247
peerCredentials = null;
242248
}
243249
if (null != peerContext) {

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ public final class SQLServerColumnEncryptionCertificateStoreProvider extends SQL
1919
static final private java.util.logging.Logger windowsCertificateStoreLogger = java.util.logging.Logger
2020
.getLogger("com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionCertificateStoreProvider");
2121

22-
static boolean isWindows;
23-
2422
String name = "MSSQL_CERTIFICATE_STORE";
2523

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

3028
private static final Lock lock = new ReentrantLock();
3129

32-
static {
33-
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")) {
34-
isWindows = true;
35-
} else {
36-
isWindows = false;
37-
}
38-
}
39-
4030
/**
4131
* Constructs a SQLServerColumnEncryptionCertificateStoreProvider.
4232
*/
@@ -67,7 +57,7 @@ public byte[] decryptColumnEncryptionKey(String masterKeyPath, String encryption
6757
windowsCertificateStoreLogger.entering(SQLServerColumnEncryptionCertificateStoreProvider.class.getName(),
6858
"decryptColumnEncryptionKey", "Decrypting Column Encryption Key.");
6959
byte[] plainCek;
70-
if (isWindows) {
60+
if (SQLServerConnection.isWindows) {
7161
plainCek = decryptColumnEncryptionKeyWindows(masterKeyPath, encryptionAlgorithm,
7262
encryptedColumnEncryptionKey);
7363
} else {

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -983,8 +983,10 @@ IdleConnectionResiliency getSessionRecovery() {
983983
/** global system ColumnEncryptionKeyStoreProviders */
984984
static Map<String, SQLServerColumnEncryptionKeyStoreProvider> globalSystemColumnEncryptionKeyStoreProviders = new HashMap<>();
985985

986+
static boolean isWindows = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows");
987+
986988
static {
987-
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")) {
989+
if (isWindows) {
988990
SQLServerColumnEncryptionCertificateStoreProvider provider = new SQLServerColumnEncryptionCertificateStoreProvider();
989991
globalSystemColumnEncryptionKeyStoreProviders.put(provider.getName(), provider);
990992
}
@@ -1426,6 +1428,9 @@ public static void clearUserTokenCache() {
14261428
/** integrated authentication scheme */
14271429
private AuthenticationScheme intAuthScheme = AuthenticationScheme.NATIVE_AUTHENTICATION;
14281430

1431+
/** use default native GSS-API Credential flag */
1432+
private boolean useDefaultGSSCredential = SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.getDefaultValue();
1433+
14291434
/** impersonated user credential */
14301435
private transient GSSCredential impersonatedUserCred;
14311436

@@ -2480,6 +2485,11 @@ Connection connectInternal(Properties propsIn,
24802485
impersonatedUserCred = (GSSCredential) activeConnectionProperties.get(sPropKey);
24812486
isUserCreatedCredential = true;
24822487
}
2488+
sPropKey = SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString();
2489+
sPropValue = activeConnectionProperties.getProperty(sPropKey);
2490+
if(null != sPropValue && isWindows) {
2491+
useDefaultGSSCredential = isBooleanPropertyOn(sPropKey, sPropValue);
2492+
}
24832493
} else if (intAuthScheme == AuthenticationScheme.NTLM) {
24842494
String sPropKeyDomain = SQLServerDriverStringProperty.DOMAIN.toString();
24852495
String sPropValueDomain = activeConnectionProperties.getProperty(sPropKeyDomain);
@@ -5097,9 +5107,9 @@ private void logon(LogonCommand command) throws SQLServerException {
50975107
authentication = new AuthenticationJNI(this, currentConnectPlaceHolder.getServerName(),
50985108
currentConnectPlaceHolder.getPortNumber());
50995109
} else if (AuthenticationScheme.JAVA_KERBEROS == intAuthScheme) {
5100-
if (null != impersonatedUserCred) {
5110+
if (null != impersonatedUserCred || useDefaultGSSCredential) {
51015111
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(),
5102-
currentConnectPlaceHolder.getPortNumber(), impersonatedUserCred, isUserCreatedCredential);
5112+
currentConnectPlaceHolder.getPortNumber(), impersonatedUserCred, isUserCreatedCredential, useDefaultGSSCredential);
51035113
} else {
51045114
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(),
51055115
currentConnectPlaceHolder.getPortNumber());
@@ -5800,8 +5810,7 @@ private SqlAuthenticationToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throw
58005810
} else if (authenticationString
58015811
.equalsIgnoreCase(SqlAuthentication.ACTIVE_DIRECTORY_INTEGRATED.toString())) {
58025812
// If operating system is windows and mssql-jdbc_auth is loaded then choose the DLL authentication.
5803-
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")
5804-
&& AuthenticationJNI.isDllLoaded()) {
5813+
if (isWindows && AuthenticationJNI.isDllLoaded()) {
58055814
try {
58065815
FedAuthDllInfo dllInfo = AuthenticationJNI.getAccessTokenForWindowsIntegrated(
58075816
fedAuthInfo.stsurl, fedAuthInfo.spn, clientConnectionId.toString(),

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,17 @@ public GSSCredential getGSSCredentials() {
239239
SQLServerDriverObjectProperty.GSS_CREDENTIAL.getDefaultValue());
240240
}
241241

242+
@Override
243+
public void setUseDefaultGSSCredential(boolean enable) {
244+
setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString(), enable);
245+
}
246+
247+
@Override
248+
public boolean getUseDefaultGSSCredential() {
249+
return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString(),
250+
SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.getDefaultValue());
251+
}
252+
242253
@Override
243254
public void setAccessToken(String accessToken) {
244255
setStringProperty(connectionProps, SQLServerDriverStringProperty.ACCESS_TOKEN.toString(), accessToken);

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,8 @@ enum SQLServerDriverBooleanProperty {
694694
USE_FMT_ONLY("useFmtOnly", false),
695695
SEND_TEMPORAL_DATATYPES_AS_STRING_FOR_BULK_COPY("sendTemporalDataTypesAsStringForBulkCopy", true),
696696
DELAY_LOADING_LOBS("delayLoadingLobs", true),
697-
USE_DEFAULT_JAAS_CONFIG("useDefaultJaasConfig", false);
697+
USE_DEFAULT_JAAS_CONFIG("useDefaultJaasConfig", false),
698+
USE_DEFAULT_GSS_CREDENTIAL("useDefaultGSSCredential", false);
698699

699700
private final String name;
700701
private final boolean defaultValue;
@@ -748,7 +749,7 @@ public final class SQLServerDriver implements java.sql.Driver {
748749
SQLServerDriverStringProperty.DATABASE_NAME.getDefaultValue(), false, null),
749750
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.toString(),
750751
Boolean.toString(SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.getDefaultValue()), false,
751-
new String[] {"true", "false"}),
752+
TRUE_FALSE),
752753
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.ENCRYPT.toString(),
753754
SQLServerDriverStringProperty.ENCRYPT.getDefaultValue(), false,
754755
new String[] {EncryptOption.FALSE.toString(), EncryptOption.NO.toString(),
@@ -768,6 +769,9 @@ public final class SQLServerDriver implements java.sql.Driver {
768769
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.toString(),
769770
Boolean.toString(SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.getDefaultValue()), false,
770771
TRUE_FALSE),
772+
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString(),
773+
Boolean.toString(SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.getDefaultValue()), false,
774+
TRUE_FALSE),
771775
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.toString(),
772776
SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.getDefaultValue(), false,
773777
new String[] {KeyStoreAuthentication.JAVA_KEYSTORE_PASSWORD.toString()}),

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ protected Object[][] getContents() {
197197
{"R_lastUpdateCountPropertyDescription", "Ensures that only the last update count is returned from an SQL statement passed to the server."},
198198
{"R_disableStatementPoolingPropertyDescription", "Disables the statement pooling feature."},
199199
{"R_integratedSecurityPropertyDescription", "Indicates whether Windows authentication will be used to connect to SQL Server."},
200+
{"R_useDefaultGSSCredentialPropertyDescription", "Indicates whether GSSCredential will be created using native GSS-API."},
200201
{"R_authenticationSchemePropertyDescription", "The authentication scheme to be used for integrated authentication."},
201202
{"R_lockTimeoutPropertyDescription", "The number of milliseconds to wait before the database reports a lock time-out."},
202203
{"R_connectRetryCountPropertyDescription", "The number of reconnection attempts if there is a connection failure."},

src/test/java/com/microsoft/sqlserver/jdbc/KerberosTest.java

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,65 +16,81 @@
1616
import java.util.HashMap;
1717
import java.util.Map;
1818

19+
@Tag(Constants.kerberos)
1920
@RunWith(JUnitPlatform.class)
2021
public class KerberosTest extends AbstractTest {
2122

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

2426
@BeforeAll
2527
public static void setupTests() throws Exception {
2628
setConnection();
2729
}
2830

29-
@Tag(Constants.Kerberos)
3031
@Test
3132
public void testUseDefaultJaasConfigConnectionStringPropertyTrue() throws Exception {
3233
String connectionStringUseDefaultJaasConfig = connectionStringKerberos + ";useDefaultJaasConfig=true;";
3334

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

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

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

53-
@Tag(Constants.Kerberos)
5446
@Test
5547
public void testUseDefaultJaasConfigConnectionStringPropertyFalse() throws Exception {
5648

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

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

6957
// New connection should fail as it is attempting to connect using an overwritten JAAS config.
70-
try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionStringKerberos)) {
58+
try {
59+
createKerberosConnection(connectionStringKerberos);
7160
Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown"));
7261
} catch (SQLServerException e) {
7362
Assertions.assertTrue(e.getMessage()
7463
.contains(TestResource.getResource("R_noLoginModulesConfiguredForJdbcDriver")));
7564
}
7665
}
7766

67+
@Test
68+
public void testUseDefaultNativeGSSCredential() throws Exception {
69+
// This is a negative test. This test should fail as expected as the JVM arg "-Dsun.security.jgss.native=true"
70+
// isn't provided.
71+
String connectionString = connectionStringKerberos + ";useDefaultGSSCredential=true;";
72+
73+
try {
74+
createKerberosConnection(connectionString);
75+
Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown"));
76+
} catch (SQLServerException e) {
77+
Assertions.assertEquals(e.getCause().getMessage(), TestResource.getResource("R_kerberosNativeGSSFailure"));
78+
}
79+
}
80+
81+
@Test
82+
public void testBasicKerberosAuth() throws Exception {
83+
createKerberosConnection(connectionStringKerberos);
84+
}
85+
86+
private static void createKerberosConnection(String connectionString) throws Exception {
87+
try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString)) {
88+
ResultSet rs = conn.createStatement().executeQuery(authSchemeQuery);
89+
rs.next();
90+
Assertions.assertEquals(kerberosAuth, rs.getString(1));
91+
}
92+
}
93+
7894
/**
7995
* Overwrites the default JAAS config. Call before making a connection.
8096
*/
@@ -84,7 +100,7 @@ private static void overwriteJaasConfig() {
84100
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
85101
new HashMap<>());
86102
Map<String, AppConfigurationEntry[]> configurationEntries = new HashMap<>();
87-
configurationEntries.put("KAFKA_CLIENT_CONTEXT_NAME",
103+
configurationEntries.put("CLIENT_CONTEXT_NAME",
88104
new AppConfigurationEntry[] { kafkaClientConfigurationEntry });
89105
Configuration.setConfiguration(new InternalConfiguration(configurationEntries));
90106
}
@@ -100,6 +116,5 @@ private static class InternalConfiguration extends Configuration {
100116
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
101117
return this.configurationEntries.get(name);
102118
}
103-
104119
}
105120
}

0 commit comments

Comments
 (0)