JBoss7: Custom Login Module not working

2019-08-08 18:46发布

问题:

I am in the process of migrating from JBoss 5.0.1 to JBoss 7.2.

I am calling EJBs with a remote EJB client. I have to migrate a custom login module that derives from AbstractServerLoginModule.

I managed to configure JBoss so that the custom login module is actually called.

In the custom login module I implement a javax.security.auth.callback.NameCallback and a javax.security.auth.callback.ObjectCallback like that:

NameCallback ncb = new NameCallback("Username:");
PasswordCallback pcb = new PasswordCallback("Password:", false);
try
{
    callbackHandler.handle(new Callback[] { ncb, pcb });
}
catch (Exception e)
{
    if (e instanceof RuntimeException)
    {
        throw (RuntimeException) e;
    }
    return false; 
}
String name = ncb.getName();
String pwd = new String(pcb.getPassword());

I see that the user is passed as I expect it to the NameCallback.

But I observe that the password that is passed to the PasswordCallback is always a "random value" like "0299df2c-620a-4ac6-83d3-50daaa65fb90".

The client code I use to call the server looks like this:

Properties clientProp = new Properties();
clientProp.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
clientProp.put("remote.connections", "default");
clientProp.put("remote.connection.default.port", "4447");
clientProp.put("remote.connection.default.host", "localhost");
clientProp.put("remote.connection.default.username", "USER");
clientProp.put("remote.connection.default.password", "PASSWORD");
clientProp.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
clientProp.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");

EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(clientProp);
ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc);
EJBClientContext.setSelector(selector);

final Properties jndiProperties = new Properties();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
jndiProperties.put("jboss.naming.client.ejb.context", true);
jndiProperties.put(InitialContext.SECURITY_PRINCIPAL, "USER");
jndiProperties.put(InitialContext.SECURITY_CREDENTIALS, "PASSWORD");

I would like that the password arrives in plain text on the server in the custom login module. Is that possible? How can I achieve that?

回答1:

I finally solved the problem.

The reason why the password was set to a random value was that the password never arrived at the server. It seems that the security implementation in JBoss then sets the password to a random value if no password is passed.

With the following client code the password appeared as expected in plaintext in the login module:

final Properties jndiProperties = new Properties();

jndiProperties.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");

jndiProperties.put("remote.connections", "one");
jndiProperties.put("remote.connection.one.port", "4447");
jndiProperties.put("remote.connection.one.host", "localhost");

jndiProperties.put("remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
jndiProperties.put("remote.connection.one.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS", "JBOSS-LOCAL-USER");

jndiProperties.put("remote.clusters", "ejb");
jndiProperties.put("remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");

jndiProperties.put("remote.connection.one.username", username);
jndiProperties.put("remote.connection.one.password",password);
jndiProperties.put("remote.cluster.ejb.username", username);
jndiProperties.put("remote.cluster.ejb.password", password);

jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");

EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(jndiProperties);
ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc);
EJBClientContext.setSelector(selector);

final Context context = new InitialContext(jndiProperties);

MessageServieRemote msgService = (MessageServieRemote) context.lookup("ejb:TestEar/TestNopEjb//MessageService!net.jonasbandi.ejbremote.MessageServieRemote");
System.out.println(msgService.getMessage());

The important parts seem to be the following two properties:

jndiProperties.put("remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOPLAIN  TEXT", "false");
jndiProperties.put("remote.connection.one.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS", "JBOSS-LOCAL-USER");

The first of the above properties ensures that the password is passed in plaintext from the client to the server.

When you are running JBoss and the EJB client on the same machine, then the EJB client optimizes communication and calls the server "natively" and not over remoting. In my case this bypasses the login-module which is configured on the ApplicationRealm which is referenced by the remotinng subsystem. The second of the above properties disallows this behavior and forces the EJB client always to use remoting and therefore not to bypass my custom login module.

Just for completion: The relevant parts of the server configuration (in my case in the file standalone-ha.xml) look like this:

Remoting subsystem references AppplicationRealm:

<subsystem xmlns="urn:jboss:domain:remoting:1.1">
    <connector name="remoting-connector" socket-binding="remoting" security-realm="ApplicationRealm"/>
</subsystem>

ApplicationRealm references custom security domain

       <security-realm name="ApplicationRealm">
            <authentication>
                <jaas name="myDomain"/>
            </authentication>
            <authorization>
                <properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
            </authorization>
        </security-realm>

Custom security domain configures custom login module:

            <security-domain name="myDomain" cache-type="default">
                <authentication>
                    <login-module code="net.jonasbandi.ejbremote.CustomLoginModule" flag="required " module="deployment.TestEar.ear">
                        <module-option name="password-stacking" value="useFirstPass"/>
                        <module-option name="realm" value="ApplicationRealm"/>
                    </login-module>
                </authentication>
            </security-domain>

Note in the above case I deploy the CustomLoginModule as part of my EAR, therefore the attribute module="deployment.TestEar.ear" is necessary.

And finaly I set the default security domain to my custom security domain:

<default-security-domain value="myDomain"/>