Mapping Kerberos client principal name to WebSphere user registry ID for SPNEGO TAI (deprecated)


Use a system programming interface to customize the behavior of the SPNEGO trust association interceptor (TAI) by implementing arbitrary mappings of the end-user's identity, which is retrieved from Microsoft Active Directory to the identity used in the WAS security registry. we need to perform some admin tasks in the WAS environment to use SPNEGO TAI and to verify the requester's identity matches the identity in the WAS user registry.

Deprecated feature:

In WAS V6.1, a trust association interceptor (TAI) that uses the SPNEGO to securely negotiate and authenticate HTTP requests for secured resources was introduced. In WAS 7.0, this function is now deprecated. SPNEGO Web authentication has taken its place to provide dynamic reload of the SPNEGO filters and to enable fallback to the application login method. depfeat

Make sure the following tasks have been performed successfully:

  1. Set the Web browser to use SPNEGO. See Set the client browser to use SPNEGO TAI (deprecated)

  2. Set JVM properties, custom SPNEGO TAI properties, and enabling the SPNEGO TAI. See Set JVM custom properties, filtering HTTP requests, and enabling SPNEGO TAI in WAS (deprecated)

In the simplest deployment of the SPNEGO TAI, it is assumed that the requester's identity in the WAS user registry is identical to the identity retrieved. This is the case when Microsoft Windows Active Directory server is the lightweight directory access protocol (LDAP) server used in WAS. This is default behavior of the SPNEGO TAI.

You do not need to use this simple deployment of the SPNEGO TAI. WAS can use a different registry, such as a local OS, LDAP, or custom registry instead of the Microsoft Active Directory. If WAS uses a different registry than the Microsoft Active Directory, then a mapping from the Microsoft Windows user Id to a WAS user Id is necessary.

  Use the JAAS custom login module to perform any custom mapping of a client Kerberos principal name from the Microsoft Active Directory to the WebSphere user registry identity. The JAAS custom login module is a plug-in mechanism that is defined for authenticating incoming and outgoing requests in WAS and is inserted before the ltpaLoginModule.

The JAAS custom login module retrieves a client Kerberos principal name in the javax.security.auth.Subject using subject.getPrincipals(KerberosPrincipal.class) method, maps the client Kerberos principal name to the WebSphere user registry identity, and inserts the mapping identity in the hash table property com.ibm.wsspi.security.cred.userId. The ltpaLoginModule then uses the mapped identity to create a WSCredential.

The custom login module can also supply the full set of security properties in the javax.security.auth.Subject in the com.ibm.wsspi.security.tai.TAIResult to fully assert the mapped identity. When the identity is fully asserted, the wsMapDefaultInboundLoginModule maps those security properties to a WSCredential. A sample of the custom login module follows:

package com.ibm.ws.security.server.lm;
 import java.util.Map;
 import java.lang.reflect.Array;
 import javax.security.auth.Subject;
 import javax.security.auth.callback.*;
 import javax.security.auth.login.LoginException;
 import javax.security.auth.spi.LoginModule;
 import javax.security.auth.kerberos.*;
 import com.ibm.websphere.security.auth.WSLoginFailedException;
 import com.ibm.wsspi.security.token.AttributeNameConstants;

/**
 * 
 * @author IBM Corporation
 * @version 1.0
 * @since 1.0    
 *
 */
 public class sampleSpnegoMappingLoginModule implements LoginModule {
    /*
     * 
     * Constant that represents the name of this mapping module.  Whenever this sample
     * code is used to create a class with a different name, this value should be changed.
     * 
     */
    private final static String MAPPING_MODULE_NAME = "com.ibm.websphere.security.sampleSpnegoMappingLoginModule";

    private String mapUid = null;
    /**
     * Construct an uninitialized WSLoginModuleImpl object.
     */
    public sampleSpnegoMappingLoginModule() {
        debugOut("sampleSpnegoMappingLoginModule() entry");
        debugOut("sampleSpnegoMappingLoginModule() exit");
    }

    /**
     * Initialize this login module.
     *
     * 
     * This is called by the  LoginContext after this login module is
     * instantiated. The relevant information is passed from the LoginContext
     * to this login module. If the login module does not understands any of the data
     * stored in the sharedState and options parameters,      * they can be ignored.
     * 
     *
     * @param subject The subject to be authenticated.
     * @param callbackHandler
     *                A  CallbackHandler for communicating with the end user to gather 
                      login information (e.g., username and password).
     * @param sharedState
     *                The state shared with other configured login modules.
     * @param options The options specified in the login configuration for this particular login module.
     */
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
        debugOut("initialize(subject = \"" + subject.toString() +
                 "\", callbackHandler = \"" + callbackHandler.toString() +
                 "\", sharedState = \"" + sharedState.toString() +
                 "\", options = \"" + options.toString() + "\")");

        this.subject = subject;
        this.callbackHandler = callbackHandler;
        this.sharedState = sharedState;
        this.options = options;

        debug = "true".equalsIgnoreCase((String)this.options.get("debug"));

        debugOut("initialize() exit");
    }

    /**
     * 
     * Method to authenticate a Subject (phase 1).
     * 
     *
     * 
     * This method authenticates a Subject. It uses CallbackHandler to gather
     * the Subject information, like username and password for example, and verify these
     * information. The result of the authentication is saved in the private state within
     * this login module.
     * 
     *
     * @return  true if the authentication succeeded, or false
     *         if this login module should be ignored.
     * @exception LoginException
     *                   If the authentication fails.
     */
    public boolean login() throws LoginException
    {
        debugOut("sampleSpnegoMappingLoginModule.login() entry"); 

        boolean succeeded = false;
        java.util.Set krb5Principals= subject.getPrincipals(KerberosPrincipal.class);
        java.util.Iterator krb5PrincIter = krb5Principals.iterator();

        while (krb5PrincIter.hasNext()) {
            Object princObj = krb5PrincIter.next();
            debugOut("Kerberos principal name: "+ princObj.toString());
            
            if (princObj != null && princObj.toString().equals("utle@WSSEC.AUSTIN.IBM.COM")){
                mapUid = "user1";
                debugOut("mapUid: "+mapUid);

                                 java.util.Hashtable customProperties = (java.util.Hashtable)
                                 sharedState.get(AttributeNameConstants.WSCREDENTIAL_PROPERTIES_KEY);
                                 if (customProperties == null) {
                                     customProperties = new java.util.Hashtable();
                                 }
                                 succeeded = true;
                                 customProperties.put(AttributeNameConstants.WSCREDENTIAL_USERID, mapUid);

                                 Map<String,java.util.Hashtable)>
                                 mySharedState=(Map<String,java.util.Hashtable>)sharedState;
                                 mySharedState.put((AttributeNameConstants.WSCREDENTIAL_PROPERTIES_KEY.customProperties);

                                 debugOut("Add a mapping user ID to Hashtable, mapping ID = "+mapUid); 

                                    }
                                 debugOut("login() custom properties = " + customProperties);
            }
        }

        succeeded = true;
        debugOut("sampleSpnegoMappingLoginModule.login() exit"); 

        return succeeded;
    }

    /**
     * 
     * Method to commit the authentication result (phase 2).
     * 
     *
     * 
     * This method is called if the LoginContext's overall authentication
     * succeeded (the revelant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL login module
     * succeeded).
     * 
     *
     * @return true if the commit succeeded, or false
     *         if this login module should be ignored.
     * @exception LoginException
     *                   If the commit fails.
     */
    public boolean commit() throws LoginException              
    {
        debugOut("commit()");

        debugOut("commit()");

        return true;
    }

    /**
     * Method to abort the authentication process (phase 2).
     *
     * 
     * This method is called if the LoginContext's overall authentication
     * failed (the revelant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL login module
     * did not succeed).
     * 
     *
     * 
     * If this login module's authentication attempt succeeded, then this method cleans
     * up the previous state saved in phase 1.
     * 
     *
     * @return true if the abort succeeded, or false
     *         if this login module should be ignored.
     * @exception LoginException
     *                   If the abort fails.
     */
    public boolean abort() throws LoginException {
        debugOut("abort() entry");
        debugOut("abort() exit");
        return true;
    }

    /**
     * Method which logs out a Subject.
     *
     * @return true if the logout succeeded, or false
     *         if this login module should be ignored.
     * @exception LoginException
     *                   If the logout fails.
     */
    public boolean logout() throws LoginException
    {
        debugOut("logout() entry");
        debugOut("logout() exit");

        return true;
    }

    private void cleanup()
    {
        debugOut("cleanup() entry");
        debugOut("cleanup() exit");
    }

    /*
     * 
     * Private method to print trace information.  This implementation uses System.out
     * to print trace information to standard output, but a custom tracing system can
     * be implemented here as well.
     * 
     */
    private void debugOut(Object o)
    {
        System.out.println("Debug: " + MAPPING_MODULE_NAME);
        if (o != null) {
            if (o.getClass().isArray()) {
                int length = Array.getLength(o);
                for (int i = 0; i < length; i++) {
                    System.out.println("\t" + Array.get(o, i));
                }
            } else {
                System.out.println("\t" + o);
            }
        }
    }
    private Subject subject;
    private CallbackHandler callbackHandler;
    private Map sharedState;
    private Map options;

    protected boolean debug = false;
}

 

Results

Use the custom login module, Microsoft Active Directory identities are mapped to the WAS's security registry and the behavior of the SPNEGO TAI is customized.

 

Related concepts


Single sign-on for HTTP requests using SPNEGO TAI (deprecated)

 

Related tasks


Develop custom login modules for a system login configuration for JAAS
Create a single sign-on for HTTP requests using the SPNEGO TAI (deprecated)

 

Related


SPNEGO TAI custom properties configuration (deprecated)