diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/JaasConfiguration.java b/src/main/java/com/microsoft/sqlserver/jdbc/JaasConfiguration.java index 2f8dda592..6f23b965e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/JaasConfiguration.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/JaasConfiguration.java @@ -19,26 +19,36 @@ public class JaasConfiguration extends Configuration { private final Configuration delegate; private AppConfigurationEntry[] defaultValue; - private static AppConfigurationEntry[] generateDefaultConfiguration() { - if (Util.isIBM()) { + private static AppConfigurationEntry[] generateDefaultConfiguration() throws SQLServerException { + try { + if (Util.isIBM()) { + return loadIbmModule(); + } + Class.forName("com.sun.security.auth.module.Krb5LoginModule"); + Map confDetails = new HashMap<>(); + confDetails.put("useTicketCache", "true"); + return new AppConfigurationEntry[] { + new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, confDetails)}; + } catch (ClassNotFoundException e) { + return loadIbmModule(); + } + } + + private static AppConfigurationEntry[] loadIbmModule() throws SQLServerException { + try { + Class.forName("com.ibm.security.auth.module.Krb5LoginModule"); Map confDetailsWithoutPassword = new HashMap<>(); confDetailsWithoutPassword.put("useDefaultCcache", "true"); Map confDetailsWithPassword = new HashMap<>(); - // 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 confDetails = new HashMap<>(); - confDetails.put("useTicketCache", "true"); - return new AppConfigurationEntry[] { - new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", - AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, confDetails)}; + } catch (ClassNotFoundException ex) { + throw new SQLServerException(SQLServerException.getErrString("R_moduleNotFound"), null); } } @@ -47,8 +57,10 @@ private static AppConfigurationEntry[] generateDefaultConfiguration() { * * @param delegate * a possibly null delegate + * @throws SQLServerException + * if neither Kerberos module is found: com.sun.security.auth.module.Krb5LoginModule or com.ibm.security.auth.module.Krb5LoginModule */ - JaasConfiguration(Configuration delegate) { + JaasConfiguration(Configuration delegate) throws SQLServerException { this.delegate = delegate; this.defaultValue = generateDefaultConfiguration(); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java b/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java index d02238196..1541d007f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java @@ -43,9 +43,13 @@ final class KerbAuthentication extends SSPIAuthentication { private GSSContext peerContext = null; static { - // 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())); + try { + // 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())); + } catch (SQLServerException e) { + throw new RuntimeException("Failed to set JAAS configuration: " + e.getMessage(), e); + } } /** diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 9d09e1edd..2abbbb68f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -557,6 +557,7 @@ protected Object[][] getContents() { {"R_InvalidRuleFormat", "Wrong number of parameters supplied to rule. Number of parameters: {0}, expected: 2 or 3."}, {"R_InvalidRetryInterval", "Current retry interval: {0}, is longer than queryTimeout: {1}."}, {"R_UnableToFindClass", "Unable to locate specified class: {0}"}, + {"R_moduleNotFound", "Neither com.sun.security.auth.module.Krb5LoginModule nor com.ibm.security.auth.module.Krb5LoginModule was found."}, }; } // @formatter:on diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java index 374626cbe..551dd55d7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java @@ -47,9 +47,26 @@ private Util() { static final String SYSTEM_JRE = System.getProperty("java.vendor") + " " + System.getProperty("java.version"); private static final Lock LOCK = new ReentrantLock(); + private static Boolean isIBM = null; + static boolean isIBM() { + if (isIBM != null) { + return isIBM; + } + String vmName = System.getProperty("java.vm.name"); - return SYSTEM_JRE.startsWith("IBM") && vmName.startsWith("IBM"); + if (vmName != null && vmName.startsWith("IBM")) { + isIBM = true; + return isIBM; + } + + try { + Class.forName("com.ibm.security.auth.module.Krb5LoginModule"); + isIBM = true; + } catch (ClassNotFoundException ex) { + isIBM = false; + } + return isIBM; } static String getJVMArchOnWindows() { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/KerberosTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/KerberosTest.java index 47f0a9252..50ecaa768 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/KerberosTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/KerberosTest.java @@ -92,6 +92,45 @@ private static void createKerberosConnection(String connectionString) throws Exc } } + /** + * Test to verify the Kerberos module used + */ + @Test + public void testKerberosConnectionWithDefaultJaasConfig() { + try { + // Set a mock JAAS configuration using the existing method + overwriteJaasConfig(); + + String connectionString = connectionStringKerberos + ";useDefaultJaasConfig=true;"; + createKerberosConnection(connectionString); + + Configuration config = Configuration.getConfiguration(); + AppConfigurationEntry[] entries = config.getAppConfigurationEntry("CLIENT_CONTEXT_NAME"); + Assertions.assertNotNull(entries); + Assertions.assertTrue(entries.length > 0); + if (Util.isIBM()) { + Assertions.assertEquals("com.ibm.security.auth.module.Krb5LoginModule", entries[0].getLoginModuleName()); + } else { + Assertions.assertEquals("com.sun.security.auth.module.Krb5LoginModule", entries[0].getLoginModuleName()); + } + } catch (Exception e) { + Assertions.fail("Exception was thrown: " + e.getMessage()); + } + } + + /** + * Test to verify the JaasConfiguration constructor + */ + @Test + public void testJaasConfigurationConstructor() { + try { + JaasConfiguration config = new JaasConfiguration(Configuration.getConfiguration()); + Assertions.assertNotNull(config); + } catch (SQLServerException e) { + Assertions.fail("Exception was thrown: " + e.getMessage()); + } + } + /** * Overwrites the default JAAS config. Call before making a connection. */