During LoginContext.login() JBoss EAP 5.2 throws LoginException on JDK7

Solution Unverified - Updated -

Environment

  • JBoss enterprise Application Platform (EAP)
    • 5.2
  • Java Development Kit (JDK)
    • 1.7.x

Issue

  • JBoss EAP 5.2 is throwing the following Exception when the JBoss Client running on JDK7
Exception in thread "main" javax.naming.AuthenticationException: Failed to login using protocol=jmx-console [Root exception is javax.security.auth.login.LoginException: No LoginModules configured for jmx-console]
    at org.jboss.naming.HttpNamingContextFactory.tryLogin(HttpNamingContextFactory.java:146)
    at org.jboss.naming.HttpNamingContextFactory.getInitialContext(HttpNamingContextFactory.java:84)
    at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
    at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305)
    at javax.naming.InitialContext.init(InitialContext.java:240)
    at javax.naming.InitialContext.<init>(InitialContext.java:214)
    at client.Main.main(Main.java:13)
Caused by: javax.security.auth.login.LoginException: No LoginModules configured for jmx-console
    at javax.security.auth.login.LoginContext.init(LoginContext.java:273)
    at javax.security.auth.login.LoginContext.<init>(LoginContext.java:514)
    at org.jboss.naming.HttpNamingContextFactory.tryLogin(HttpNamingContextFactory.java:141)
    ... 6 more
  • The Client code snippet is as following:
        Properties props = new Properties();
        props.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory");
        props.put(Context.PROVIDER_URL, "http://localhost:8080/invoker/JNDIFactory");
        //props.put(Context.PROVIDER_URL, "http://localhost:8080/invoker/ReadOnlyJNDIFactory/");
        props.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
        props.put(Context.SECURITY_PRINCIPAL, "admin");
        props.put(Context.SECURITY_CREDENTIALS, "admin");
        props.put(Context.SECURITY_PROTOCOL, "jmx-console");
        Context ctx = new InitialContext(props);
  • This issue gets resolved when setting the Following property in the Client code programatically, Where some file name need to be passed like "SOME_FILE_NAME" or Any other String.
java.security.Security.setProperty("login.config.url.1", "SOME_FILE_NAME");
  • NOTE: This issue does not occur when the same client code is executed on JDK 6.

Resolution

The HttpNamingContextFactory tries to use JAAS to setup the username/password. By default, the HttpNamingContextFactory will attempt to try to setup the JAAS configuration itself. This can be seen in the tryLogin() method and DummyConfiguration class. However, a change in behavior between Java 1.6 and 1.7 breaks the automatic JAAS configuration. See the Root Cause section for more information on the root cause of the issue.

The resolution is to define a login module stack that uses the org.jboss.security.ClientLoginModule, then use the the -Djava.security.auth.login.config to tell the JVM to use this login module stack.

  1. Create a file named auth.conf with the following contents:

    other {
      org.jboss.security.ClientLoginModule required;
    };
    
  2. Use the -Djava.security.auth.login.config=/path/to/auth.conf system property to tell the JVM the location of the auth.conf configuration file

Notice the login module stack defined in the auth.conf file is named other. This is the name that the HttpNamingContextFactory looks for by default. If you name this something other than other, then adjust the Context.SECURITY_PROTOCOL parameter that is passed in the hash map to the InitialContext constructor to use the same name.

Root Cause

The "automatic" configuration fails because the behavior of the JVM provided javax.security.auth.login.Configuration.getConfiguration() call has changed between Java 1.6 and 1.7.

Here is the HttpNamingContextFactory.getConfiguration() method:

   private Configuration getConfiguration() {
      Configuration conf = null;
      try {
        conf = Configuration.getConfiguration(); 
      } catch (Exception e) {
        if(e.getCause() instanceof IOException) { //no auth.conf or whatever so we make our own dummy
            conf = new DummyConfiguration();
        }
      }
      return conf;
   }

With Java 1.6, if there is no "auth.conf" configured (using the -Djava.security.auth.login.config system property), then an exception is thrown by the Configuration.getConfiguration() method. This triggers the "automatic" configuration of the ClientLoginModule (which sets the username/password so that it can get setup by the Authenticator later on).

With java 1.7, if there is no "auth.conf" configured (using the -Djava.security.auth.login.config system property), then NO exception is thrown. As a result, the automatic configuration of the ClientLoginModule is not triggered. This leads to "LoginContext.login()" failing with the "javax.security.auth.login.LoginException: No LoginModules configured for..." error.

  • Looks like In the JDK7 class "com.sun.security.auth.login.ConfigFile" is changed a bit. As it has a method with the following signature.

  • Changed code snippet below in JDK7 "com.sun.security.auth.login.ConfigFile" class.

126    private void init(URL url) throws IOException {
       .
       .
       .
254        if (initialized == false && n == 1 && config_url == null) {
253
255            // get the config from the user's home directory
256            if (debugConfig != null) {
257                debugConfig.println("\tReading Policy " +
258                                "from ~/.java.login.config");
259            }
260            config_url = System.getProperty("user.home");
261            String userConfigFile = config_url +
262                      File.separatorChar + ".java.login.config";
263
264            // No longer throws an exception when there's no config file
265            // at all. Returns an empty Configuration instead.
266            if (new File(userConfigFile).exists()) {
267                init(new File(userConfigFile).toURI().toURL(),
268                    newConfig);
269            }
270        }
271
272        configuration = newConfig;
273    }

NOTE: Hence the above init method "No longer throws an exception when there's no config file at all. Returns an empty Configuration instead." Which is causing the issue. Notice the Comment mentioned in the code in Line 264 and 265 .In previous releases of JDK (like JDK6) the behaviour was different.

Example:

  • The JDKs this behaviour and this issue can be explained with a very simple Example.
    • **NOTE: following simple code has nothing to do with JBoss EAP, Even if it is using package name containing word "jboss" **
    • Just run the following Simplest program using JDK6 and JDK7 and you will see the different behaviour.
package org.jboss.naming;
import java.io.IOException;
import java.net.URL;
import javax.security.auth.Subject;
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;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

public class HttpNamingContextFactory
  {
      public static void main(String ar[]) throws Exception
       {
            HttpNamingContextFactory test=new HttpNamingContextFactory();
            test.tryLogin();
       }

      public void tryLogin() throws LoginException {
        LoginContext context = new LoginContext("jmx-console", new CallbackHandler() {
            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
                for (int i = 0; i < callbacks.length; i++) {
                    if (callbacks[i] instanceof NameCallback) {
                        ((NameCallback) callbacks[i]).setName("first");
                    } else if (callbacks[i] instanceof PasswordCallback) {
                        ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray());
                    } else {
                        throw new UnsupportedCallbackException(callbacks[i]);
                    }
                }
            }
        });
        context.login();
       }
  }

  • Compile the code as following And then run it on JDK7 and JDK6 to see the behaviour difference:
javac -d . HttpNamingContextFactory.java

Results on JDK6

[jsenshar@localhost JayTest]$ /MyJdks/jdk1.6.0_21/bin/java org.jboss.naming.HttpNamingContextFactory 

Exception in thread "main" java.lang.SecurityException: Unable to locate a login configuration
    at com.sun.security.auth.login.ConfigFile.<init>(ConfigFile.java:93)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at java.lang.Class.newInstance0(Class.java:355)
    at java.lang.Class.newInstance(Class.java:308)
    at javax.security.auth.login.Configuration$3.run(Configuration.java:247)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.login.Configuration.getConfiguration(Configuration.java:242)
    at javax.security.auth.login.LoginContext$1.run(LoginContext.java:237)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.login.LoginContext.init(LoginContext.java:234)
    at javax.security.auth.login.LoginContext.<init>(LoginContext.java:403)
    at org.jboss.naming.HttpNamingContextFactory.tryLogin(HttpNamingContextFactory.java:24)
    at org.jboss.naming.HttpNamingContextFactory.main(HttpNamingContextFactory.java:20)
Caused by: java.io.IOException: Unable to locate a login configuration
    at com.sun.security.auth.login.ConfigFile.init(ConfigFile.java:250)
    at com.sun.security.auth.login.ConfigFile.<init>(ConfigFile.java:91)
    ... 15 more

Results on JDK7

[jsenshar@localhost EjbClient]$ /MyJdks/jdk1.7.0_05/bin/java org.jboss.naming.HttpNamingContextFactory 

Exception in thread "main" javax.security.auth.login.LoginException: No LoginModules configured for jmx-console
    at javax.security.auth.login.LoginContext.init(LoginContext.java:273)
    at javax.security.auth.login.LoginContext.<init>(LoginContext.java:418)
    at org.jboss.naming.HttpNamingContextFactory.tryLogin(HttpNamingContextFactory.java:24)
    at org.jboss.naming.HttpNamingContextFactory.main(HttpNamingContextFactory.java:20)

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.

Close

Welcome! Check out the Getting Started with Red Hat page for quick tours and guides for common tasks.