Thursday, August 28, 2008

JNDI Application Client in WAS 6.1 - Part 2




Continued from my previous post, this post attempts to address some of technical intricacies that you might face when try to authenticate yourself outside Java EE containers when doing naming operations.

Previously, I simply assigned the necessary rights to EVERYONE in Websphere Application Server V6.1 Administrative Console to enable anyone (Including those Unauthenticated) to perform naming operations.

However, this setting is not appropriate in production environment because some operations such as removing bindings and create new bindings are considered as privileged operations that require thoughtful considerations.

Assuming that you are created a new WAS User named "NamingUser1" and assigned this user with relevant rights (i.e. CosNaming Delete, etc)

Now the trick is to pass these credentials to WAS from the Java program.

So, the first mistake that might happened is you assumed the following codes will work:


Hashtable env = new Hashtable();

env.put(Context.PROVIDER_URL, "corbaloc:iiop:localhost:2810/NameService");
env.put(Context.INITIAL_CONTEXT_FACTORY
,"com.ibm.websphere.naming.WsnInitialContextFactory");

env.put(Context.SECURITY_PRINCIPAL, "NamingUser1");
env.put(Context.SECURITY_CREDENTIALS, "password1");



No, this will not work. You will shoot by the below exception:


javax.naming.NoPermissionException: NO_PERMISSION exception caught [Root exception is org.omg.CORBA.NO_PERMISSION:
>> SERVER (id=11c328fe, host=eddy) TRACE START:
>> org.omg.CORBA.NO_PERMISSION: Caught WSSecurityContextException in WSSecurityContext.acceptSecContext(), reason: Major Code[0] Minor Code[0] Message[ null] vmcid: 0x49424000 minor code: 300 completed: No
>> at com.ibm.ISecurityLocalObjectBaseL13Impl.PrincipalAuthFailReason.map_auth_fail_to_minor_code(PrincipalAuthFailReason.java:83)
>> at com.ibm.ISecurityLocalObjectBaseL13Impl.CSIServerRIBase.authenticateSecurityTokens(CSIServerRIBase.java:2575)
>> at com.ibm.ISecurityLocalObjectBaseL13Impl.CSIServerRI.receive_request(CSIServerRI.java:485)
>> at com.ibm.rmi.pi.InterceptorManager.invokeInterceptor(InterceptorManager.java:592)
>> at com.ibm.rmi.pi.InterceptorManager.iterateServerInterceptors(InterceptorManager.java:507)
>> at com.ibm.rmi.pi.InterceptorManager.iterateReceiveRequest(InterceptorManager.java:738)
>> at com.ibm.CORBA.iiop.ServerDelegate.dispatchInvokeHandler(ServerDelegate.java:602)

...
...



You need to use JAAS to perform the authentication.

To make the case clearer, let us focus on the following source codes:


package lab.namespace;

import java.rmi.RMISecurityManager;
import java.security.PrivilegedAction;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.security.auth.login.LoginContext;

import com.ibm.ejs.models.base.bindings.applicationbnd.Subject;
import com.ibm.websphere.naming.PROPS;
import com.ibm.websphere.security.auth.WSSubject;
import com.ibm.websphere.security.auth.callback.WSCallbackHandlerImpl;
import com.ibm.ws.security.auth.callback.WSCallbackHandler;

public class Connect {
public static void main(String[] args) throws Exception {
Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, "corbaloc:iiop:localhost:2810/NameService");
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
final Context initialContext = new InitialContext(env);
initialContext.lookup("");

LoginContext loginContext =
new LoginContext("WSLogin",new WSCallbackHandlerImpl("NamingUser1","password1"));

loginContext.login();

javax.security.auth.Subject s = loginContext.getSubject();

WSSubject.doAs(s, new PrivilegedAction(){
public Object run() {
try{
initialContext.bind("hello", "1234");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});

System.out.println(loginContext.getSubject());

}

}



You also need to add the following JARs to the class path:


${WAS_INSTALLED_FOLDER}/com.ibm.ws.webservices.thinclient_6.1.0.jar


Then you crossed your finger and execute the program again. And yet it still fails.


Exception in thread "P=172625:O=0:CT" java.lang.SecurityException: Unable to locate a login configuration
at com.ibm.security.auth.login.ConfigFile.(ConfigFile.java:129)
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1263)
at javax.security.auth.login.Configuration$3.run(Configuration.java:239)
at java.security.AccessController.doPrivileged(AccessController.java:241)
at javax.security.auth.login.Configuration.getConfiguration(Configuration.java:233)
at javax.security.auth.login.LoginContext$1.run(LoginContext.java:260)
at java.security.AccessController.doPrivileged(AccessController.java:192)
at javax.security.auth.login.LoginContext.init(LoginContext.java:257)
at javax.security.auth.login.LoginContext.(LoginContext.java:426)
at lab.namespace.Connect.main(Connect.java:38)
Caused by: java.io.IOException: Unable to locate a login configuration
at com.ibm.security.auth.login.ConfigFile.init(ConfigFile.java:238)
at com.ibm.security.auth.login.ConfigFile.(ConfigFile.java:127)
... 10 more


Now you shall setup JAAS specific environment.

Copy the following files to your workspace:


${PROFILE_HOME}\properties\sas.client.props
${PROFILE_HOME}\properties\sas.client.props
${PROFILE_HOME}\properties\wsjaas.client.conf


Modify the sas.client.props


com.ibm.CORBA.validateBasicAuth=false
com.ibm.CORBA.securityServerHost=localhost
com.ibm.CORBA.securityServerPort=2809
com.ibm.CORBA.loginSource=none
com.ibm.CORBA.loginUserid=NamingUser1
com.ibm.CORBA.loginPassword=password1


Note: Here I assume that the bootstrap port is 2809.


Modify the ssl.client.props


user.root=C:/IBM/WebSphere/ND/profiles/AppSvr01
com.ibm.ssl.keyStore=C:/IBM/WebSphere/ND/profiles/AppSvr01/etc/key.p12
com.ibm.ssl.trustStore=C:/IBM/WebSphere/ND/profiles/AppSvr01/etc/trust.p12


Note: Here I just used the same keystore from the server. It might not be the case for production environment.

You will also need to modify the source code to include the following line:


System.setSecurityManager(new RMISecurityManager());


Create one new file named "security.policy" and specify the following in it.


grant {
permission java.security.AllPermission;
};


Note: This is just for demostration purposes. You should tune the security policy instead.

Lastly you must add few JVM arguments for execution.


-Djava.security.auth.login.config=${YOUR_PATH}\wsjaas.conf
-Dcom.ibm.CORBA.ConfigURL=${YOUR_PATH}\sas.client.props
-Djava.security.policy=${YOUR_PATH}\security.policy
-Dcom.ibm.SSL.ConfigURL=file:${YOUR_PATH}\ssl.client.props



Potential Mistake #2: JVM argument "com.ibm.SSL.ConfigURL"

The value specified for this argument must start with "file:" for file URL. Fail to do this will make your head spin.


Finally, the program will be successfully executed and the new String object is bound to the name space. You can use dumpNameSpace utility to verify this.


Good luck.





Top Blogs

No comments: