How to propagate JAAS Subject when calling a remote EJB (RMI over IIOP) from a pure client
Asked Answered
E

2

9

I am testing the propagation of JAAS Subject with a custom Principal from a standalone EJB client running on a raw Java runtime to a JavaEE server. I am targeting both JBoss and WebSphere implementations.

According to this forum thread I have expected it would work with JBoss easily.

Here is my EJB client code code snippet:

Subject subject = new Subject();
Principal myPrincipal = new MyPrincipal("me I myself");
subject.getPrincipals().add(myPrincipal);

PrivilegedExceptionAction<String> action = new PrivilegedExceptionAction<String>() {
    public String run() throws Exception {
            String result;
            System.out.println("Current Subject: " + Subject.getSubject(AccessController.getContext()));
            InitialContext ic = new InitialContext();
            Business1 b = (Business1) ic.lookup("StatelessBusiness1");
            result = b.getNewMessage("Hello World");
            return result;
        }
    };

result = subject.doAs(subject, action);
System.out.println("result "+result);

Server-side code is:

public String getNewMessage(String msg) {
    System.out.println("getNewMessage principal: " + sessionContext.getCallerPrincipal());
    System.out.println("Current Subject: " + Subject.getSubject(AccessController.getContext()));
    return "getNewMessage: " + msg;
}

To be sure, even if it is the default behaviour, I have added this section to my ejb-jar.xml session bean:

<security-identity>
   <use-caller-identity/>
</security-identity>

My session bean is not protected by any role.

According to this IBM WebSphere infocenter section, I have also enabled the system property com.ibm.CSI.rmiOutboundPropagationEnabled=true.

Technically speaking the service call works properly either on JBoss or WebSphere. But the JAAS Subject including my custom principal created on the client is not propagated to the server. Or course, the Subject dumped just before JNDI context creation and EJB call is OK.

I run the same Java runtime version for server and client (IBM Java6 SR9 FP2...), MyPrincipal serializable class is available in server ClassPath (AppServer/lib/ext for WebSphere, server/default/lib for JBoss)

WebSphere dumps:

[8/31/12 11:56:26:514 CEST] 00000024 SystemOut     O getNewMessage principal: UNAUTHENTICATED
[8/31/12 11:56:26:515 CEST] 00000024 SystemOut     O Current Subject: null

JBoss dumps:

 12:30:20,540 INFO  [STDOUT] getNewMessage principal: anonymous
 12:30:20,540 INFO  [STDOUT] Current Subject: null

For sure, I have missed some kind of magic spell. Do you know which one ?

Eisenstark answered 31/8, 2012 at 10:42 Comment(3)
So ? I have already successfully written a custom LoginModule for WebSphere and Tivoli PDPrincipal support, I have configured LoginModule in JBoss to enable Kerberos SPNEGO over HTTP... Definitely I am comfortable with JAAS and other stuff around. I just ask if I am wrong thinking it should work... before opening JBoss source code to verifyEisenstark
Then you are more experienced than me ;) (I played with LoginModules, custom authentication, principal forwarding over RMI-IIOP / CORBA in Glassfish 2.x, and that's been always very complicated. Ultimately, we just handled that at the application level ourselves) I would discourage anyobody form using these standards for username/password authentication. If the point is to integrate wiht Kerberos and provide single sign-on, that might still be the best way to go! Sorry, I can't help more.Panoply
If fact, the aim is to work-around failure to use Kerberos with WebSphere in our specific context: #10518557Eisenstark
I
3

I suspect you don't have security enabled on the WAS server. Because security is not enabled and you didn't authenticate to WAS, there is no credential. Thus your call to getCallerPrincipal is returning UNAUTHENTICATED.

If you turn on application security in WAS, you'll have to authenticate via the CSIv2 protocol. Creating your own JAAS subject in a standalone client will not do it. If it could, then anyone could create a "hey, it's me" credential and login to any remote EJB they wanted.

Your code will work on the server by attaching your subject to the running thread of execution. Flowing subjects/credentials across the wire requires a protocol to effect the serialization of the subject info and ensure trust of the party asserting the identity in the credential. From a standalone client, WAS accepts user info in the form of basic authorization, LTPA, and kerberos. This can be configured on an inbound CSIv2 configuration within the admin console. It's documented in the Info Center link I referenced earlier.

It's fun stuff. Good luck.

Insecurity answered 12/9, 2012 at 4:15 Comment(1)
Thanks and you're right, a specific LoginModule is required for JBoss. For WebSphere propagation is not the easiest part, so I decide to customize auth chain with a custom LoginModule and then client JVM must be configured with sas.client.properties in com.ibm.CORBA.ConfigURL. In any case, EJB methods must be secured.Eisenstark
R
0

probably this will help you with the price to use proprietary websphere-classes. as I remember , websphere does NOT propagate the jaas caller-subject, this is typical to ibm

    package foo.bar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.login.CredentialExpiredException;
import org.apache.log4j.Logger;
import com.ibm.websphere.security.WSSecurityException;
import com.ibm.websphere.security.auth.CredentialDestroyedException;
import com.ibm.websphere.security.auth.WSSubject;
import com.ibm.websphere.security.cred.WSCredential;

public class IdentityHelper
{
    private static final Logger log = Logger.getLogger(IdentityHelper.class);
    private static final String CLASS_OBJECT = "java.util.HashMap";
    private static final String KEY_OBJECT = "java.lang.String";
    private static final String VALUE_OBJECT = "java.util.HashSet"; 

    private Subject subject=null;
    private WSCredential creds;
    private Set publicCredentials=null;
    public IdentityHelper(Subject _subject) throws WSSecurityException
    {
        if(_subject==null)
        {
            IdentityHelper.log.warn("given subject was null, using Caller-Subject or the RunAs-Subject!");
            this.subject = WSSubject.getCallerSubject();
            if(this.subject==null)this.subject=WSSubject.getRunAsSubject();
        }
        else
        {           
            this.subject=_subject;
        }
        init();
    }
    public IdentityHelper() throws WSSecurityException
    {
        this.subject=WSSubject.getRunAsSubject();
        if(this.subject==null)
        {
            IdentityHelper.log.warn("using Caller-Subject NOT the RunAs-Subject!");
            this.subject = WSSubject.getCallerSubject();
        }
        init();
    }

    private void init() throws WSSecurityException
    {
        Set<WSCredential> credSet= this.subject.getPublicCredentials(WSCredential.class);
        //set should contain exactly one WSCredential
        if(credSet.size() > 1) throw new WSSecurityException("Expected one WSCredential, found " + credSet.size());
        if(credSet.isEmpty())
        {
            throw new WSSecurityException("Found no credentials");
        }
        Iterator<WSCredential> iter= credSet.iterator();
        this.creds=(WSCredential) iter.next();
        this.publicCredentials=this.subject.getPublicCredentials();
    }
    public WSCredential getWSCredential() throws WSSecurityException
    {
        return this.creds;
    }
    public List<String> getGroups() throws WSSecurityException,CredentialDestroyedException,CredentialExpiredException
    {
        WSCredential c = this.getWSCredential();
        return c.getGroupIds();
    }
    /**
     * helper method for obtaining user attributes from Subject objects.
     * @param subject
     * @return
     */
    @SuppressWarnings("unchecked")
    public Map<String, Set<String>> getAttributes()
    {
        Map<String, Set<String>> attributes = null;
        Iterator<?> i = this.subject.getPublicCredentials().iterator();
        while (attributes == null && i.hasNext())
        {
            Map<String, Set<String>> tmp = null;
            Object o = i.next();
            if(IdentityHelper.log.isDebugEnabled())
            {
                IdentityHelper.log.debug("checking for attributes (class name): " + o.getClass().getName());
            }
            if(!o.getClass().getName().equals(CLASS_OBJECT))
                continue;//loop through
            tmp = (Map) o;
            Object tObject = null;
            Iterator<?> t = null;
            t = tmp.keySet().iterator();
            tObject = t.next();
            if(IdentityHelper.log.isDebugEnabled())
            {
                IdentityHelper.log.debug("checking for attributes (key object name): " + tObject.getClass().getName()); 
            }
            if(!tObject.getClass().getName().equals(KEY_OBJECT))
                continue;//loop through
            t = tmp.values().iterator();
            tObject = t.next();
            if(IdentityHelper.log.isDebugEnabled())
            {
                IdentityHelper.log.debug("checking for attributes (value object name): " + tObject.getClass().getName()); 
            }
            if(!tObject.getClass().getName().equals(VALUE_OBJECT))
                continue;//loop through
            attributes = (Map) o;
        }
        if (attributes == null)
        {
            attributes = new HashMap<String, Set<String>>(); 
        }
        return attributes;
    }
    public Subject getSubject()
    {
        return this.subject;
    }
    protected Set getPublicCredentials() {
        return publicCredentials;
    }


}

see also: Getting the caller subject from the thread for JAAS and Getting the RunAs subject from the thread

Rumney answered 26/4, 2013 at 14:27 Comment(1)
I gave up as far as security should be enabled "globally" in Websphere to work, and Kerberos authentication may be required to get SSO without asking users to authenticate when security is enabled. As a work-around, I have designed a ORB PortableInterceptor implementation to append IIOP messages with my own context where I stored user's login name.Eisenstark

© 2022 - 2024 — McMap. All rights reserved.