3 Replies Latest reply on Nov 20, 2005 12:51 PM by starksm64

    JAAS Caller identity null on second EJB call in a transactio

    mclark00

      Hi,

      First off, I have tried my best to look through the sticky topics and many other posts on the net, and cannot figure out how to make this work. Sorry if this is a basic question.

      I've set up a Struts application using JBoss 4.0.3SP1, and I've set up form-based authentication posting to j_security_check. I'm seeing the following sequence:

      1) Attempt to access a secured resource
      2) Receive the login page
      3) Submit the login page
      4) User is authenticated successfully using the DatabaseLoginModule
      5) SessionBean1 is successfully looked up and a method is executed
      6) SessionBean1 attempts to look up SessionBean2
      7) Receive exception: 'java.lang.IllegalStateExeception: No valid security context for the caller identity'

      My understanding was that the default behavior was that subsequent EJB calls would run under the calling user's identity, but that doesn't appear to be happening. Am I doing soemthing wrong?

      Thank you very much for your help, and let me know if I can provide any more information.

      Thanks,
      Matt

      Section of login-config.xml

      <application-policy name="mwo">
       <authentication>
       <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
       flag="required">
       <module-option name="managedConnectionFactoryName">
       jboss.jca:service=LocalTxCM,name=MySQLDS
       </module-option>
       <module-option name="dsJndiName">
       java:/MySQLDS
       </module-option>
       <module-option name="principalsQuery">
       Select Password from Principals where ID =?
       </module-option>
       <module-option name="rolesQuery">
       Select R.Role 'Roles', R.RoleGroup 'RoleGroups'
       from Roles R, LINK_PRINCIPAL_ROLE L
       where L.PRINCIPAL_ID =?
       </module-option>
       </login-module>
       <login-module code="org.jboss.security.ClientLoginModule" flag="required" restore-login-identity="true"/>
       </authentication>
       </application-policy>


      Snippet from ejb-jar.xml
      <method-permission>
       <role-name>Administrator</role-name>
       <method>
       <ejb-name>SessionBean1</ejb-name>
       <method-name>*</method-name>
       </method>
       <method>
       <ejb-name>SessionBean2</ejb-name>
       <method-name>*</method-name>
       </method>
       </method-permission>


      My EJB-JBoss configuration and my Web.xml are both using the same <security-domain>, and I have no unauthenticated-principal set.


      Here is the trace:

      2005-11-20 00:46:17,140 TRACE [org.jboss.web.tomcat.security.SecurityAssociationValve] Begin invoke, callerGenericPrincipal[admin(Administrator,Guest,)]
      2005-11-20 00:46:17,140 TRACE [org.jboss.security.SecurityAssociation] pushRunAsIdentity, runAs=null
      2005-11-20 00:46:17,140 TRACE [org.jboss.web.tomcat.security.SecurityAssociationValve] Restoring principal info from cache
      2005-11-20 00:46:17,140 TRACE [org.jboss.security.SecurityAssociation] pushSubjectContext, subject=Subject:
      Principal: admin
      Principal: Roles(members:Guest,Administrator)
      , sc=org.jboss.security.SecurityAssociation$SubjectContext@13f25e3{principal=admin,subject=18489944}
      2005-11-20 00:46:17,140 TRACE [org.jboss.web.tomcat.security.RunAsListener] jsp, runAs: null
      2005-11-20 00:46:17,140 TRACE [org.jboss.web.tomcat.security.RunAsListener] jsp, runAs: null
      2005-11-20 00:46:17,156 TRACE [org.jboss.web.tomcat.security.RunAsListener] jsp, runAs: null
      2005-11-20 00:46:17,156 TRACE [org.jboss.web.tomcat.security.RunAsListener] jsp, runAs: null
      2005-11-20 00:46:17,156 TRACE [org.jboss.security.SecurityAssociation] popRunAsIdentity, runAs=null
      2005-11-20 00:46:17,156 TRACE [org.jboss.web.tomcat.security.SecurityAssociationValve] End invoke, callerGenericPrincipal[admin(Administrator,Guest,)]
      2005-11-20 00:46:17,156 TRACE [org.jboss.security.SecurityAssociation] clear, server=true
      2005-11-20 00:46:18,375 DEBUG [org.apache.catalina.connector.CoyoteAdapter] Requested cookie session id is 2E6F48CFB8D7C4D4A6B829B9E87D4256
      2005-11-20 00:46:18,375 DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Security checking request GET /mwo/actions/secure/ListAccounts.do
      2005-11-20 00:46:18,375 DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] We have cached auth type FORM for principal GenericPrincipal[admin(Administrator,Guest,)]
      2005-11-20 00:46:18,375 DEBUG [org.apache.catalina.realm.RealmBase] Checking constraint 'SecurityConstraint[action]' against GET /actions/secure/ListAccounts.do --> true
      2005-11-20 00:46:18,390 DEBUG [org.apache.catalina.realm.RealmBase] Checking constraint 'SecurityConstraint[action]' against GET /actions/secure/ListAccounts.do --> true
      2005-11-20 00:46:18,390 DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Calling hasUserDataPermission()
      2005-11-20 00:46:18,390 DEBUG [org.apache.catalina.realm.RealmBase] User data constraint has no restrictions
      2005-11-20 00:46:18,390 DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Calling authenticate()
      2005-11-20 00:46:18,390 DEBUG [org.apache.catalina.authenticator.FormAuthenticator] Already authenticated 'admin'
      2005-11-20 00:46:18,390 DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Calling accessControl()
      2005-11-20 00:46:18,390 DEBUG [org.apache.catalina.authenticator.AuthenticatorBase] Successfully passed all security constraints
      2005-11-20 00:46:18,390 TRACE [org.jboss.web.tomcat.security.SecurityAssociationValve] Begin invoke, callerGenericPrincipal[admin(Administrator,Guest,)]
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.SecurityAssociation] pushRunAsIdentity, runAs=null
      2005-11-20 00:46:18,390 TRACE [org.jboss.web.tomcat.security.SecurityAssociationValve] Restoring principal info from cache
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.SecurityAssociation] pushSubjectContext, subject=Subject:
      Principal: admin
      Principal: Roles(members:Guest,Administrator)
      , sc=org.jboss.security.SecurityAssociation$SubjectContext@d062ed{principal=admin,subject=18489944}
      2005-11-20 00:46:18,390 DEBUG [org.apache.catalina.core.StandardWrapper] Returning non-STM instance
      2005-11-20 00:46:18,390 TRACE [org.jboss.web.tomcat.security.RunAsListener] action, runAs: null
      2005-11-20 00:46:18,390 TRACE [org.jboss.web.tomcat.security.RunAsListener] action, runAs: null
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.SecurityAssociation] getPrincipal, principal=admin
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] Begin isValid, principal:admin, cache info: org.jboss.security.plugins.JaasSecurityManager$DomainInfo@17fdc2d[Subject(20435221).principals=org.jboss.security.SimplePrincipal@17286677(admin)org.jboss.security.SimpleGroup@17346346(Roles(members:Guest,Administrator)),credential.class=java.lang.String@13577344,expirationTime=1132470898390]
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] Begin validateCache, info=org.jboss.security.plugins.JaasSecurityManager$DomainInfo@17fdc2d[Subject(20435221).principals=org.jboss.security.SimplePrincipal@17286677(admin)org.jboss.security.SimpleGroup@17346346(Roles(members:Guest,Administrator)),credential.class=java.lang.String@13577344,expirationTime=1132470898390];credential.class=java.lang.String@13577344
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] End validateCache, isValid=true
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] End isValid, true
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.SecurityAssociation] pushSubjectContext, subject=Subject:
      Principal: admin
      Principal: Roles(members:Guest,Administrator)
      , sc=org.jboss.security.SecurityAssociation$SubjectContext@10edaf3{principal=admin,subject=4767079}
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.SecurityAssociation] getSubject, sc=org.jboss.security.SecurityAssociation$SubjectContext@10edaf3{principal=admin,subject=4767079}
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] doesUserHaveRole(Set), subject: Subject:
      Principal: admin
      Principal: Roles(members:Guest,Administrator)

      2005-11-20 00:46:18,390 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] roles=Roles(members:Guest,Administrator)
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] hasRole(Administrator)=true
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] hasRole=true
      2005-11-20 00:46:18,390 TRACE [org.jboss.security.SecurityAssociation] pushRunAsIdentity, runAs=null
      2005-11-20 00:46:18,390 DEBUG [com.myejb.Session1Bean] getSession2
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.SecurityAssociation] getPrincipal, principal=admin
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] Begin isValid, principal:admin, cache info: org.jboss.security.plugins.JaasSecurityManager$DomainInfo@17fdc2d[Subject(20435221).principals=org.jboss.security.SimplePrincipal@17286677(admin)org.jboss.security.SimpleGroup@17346346(Roles(members:Guest,Administrator)),credential.class=java.lang.String@13577344,expirationTime=1132470898390]
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] Begin validateCache, info=org.jboss.security.plugins.JaasSecurityManager$DomainInfo@17fdc2d[Subject(20435221).principals=org.jboss.security.SimplePrincipal@17286677(admin)org.jboss.security.SimpleGroup@17346346(Roles(members:Guest,Administrator)),credential.class=java.lang.String@13577344,expirationTime=1132470898390];credential.class=java.lang.String@13577344
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] End validateCache, isValid=true
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] End isValid, true
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.SecurityAssociation] pushSubjectContext, subject=Subject:
      Principal: admin
      Principal: Roles(members:Guest,Administrator)
      , sc=org.jboss.security.SecurityAssociation$SubjectContext@15ab821{principal=admin,subject=15632500}
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.SecurityAssociation] getSubject, sc=org.jboss.security.SecurityAssociation$SubjectContext@15ab821{principal=admin,subject=15632500}
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] doesUserHaveRole(Set), subject: Subject:
      Principal: admin
      Principal: Roles(members:Guest,Administrator)

      2005-11-20 00:46:18,484 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] roles=Roles(members:Guest,Administrator)
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] hasRole(Administrator)=true
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] hasRole=true
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.SecurityAssociation] pushRunAsIdentity, runAs=null
      2005-11-20 00:46:18,484 DEBUG [com.myejb.jboss.Session2SecurityProxy] Entered setEJBContext(EJBContext)
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] getPrincipal, cache info: null
      2005-11-20 00:46:18,484 TRACE [org.jboss.security.SecurityAssociation] popRunAsIdentity, runAs=null
      2005-11-20 00:46:18,500 TRACE [org.jboss.security.SecurityAssociation] popSubjectContext, sc=org.jboss.security.SecurityAssociation$SubjectContext@15ab821{principal=admin,subject=15632500}
      2005-11-20 00:46:18,500 ERROR [org.jboss.ejb.plugins.LogInterceptor] TransactionRolledbackException in method: public abstract com.myejb.Session2Remote com.myejb.Session2Home.create() throws javax.ejb.CreateException,java.rmi.RemoteException, causedBy:
      java.lang.IllegalStateException: No valid security context for the caller identity
      at org.jboss.ejb.EnterpriseContext$EJBContextImpl.getCallerPrincipalInternal(EnterpriseContext.java:370)

        • 1. Re: JAAS Caller identity null on second EJB call in a transa
          mclark00

          I've been trying to dig through the JBoss source to understand how the caller principal is set for the home.create() call, and I think it's possible that there is a bug in the JBoss code. It's definitely possiblet hat Im wrong and I am just not understanding something correctly, but let me show you what I looked at.

          In org.jboss.ejb.plugins.SecurityInterceptor#setContainer, if the runAsCallerIdentity() == true, which is default, the runAsIdentity is never initialized and remains null.

           public void setContainer(Container container)
           {
           super.setContainer(container);
           if (container != null)
           {
           BeanMetaData beanMetaData = container.getBeanMetaData();
           ApplicationMetaData applicationMetaData = beanMetaData.getApplicationMetaData();
           AssemblyDescriptorMetaData assemblyDescriptor = applicationMetaData.getAssemblyDescriptor();
           securityRoles = assemblyDescriptor.getSecurityRoles();
          
           SecurityIdentityMetaData secMetaData = beanMetaData.getSecurityIdentityMetaData();
           if (secMetaData != null && secMetaData.getUseCallerIdentity() == false)
           {
           String roleName = secMetaData.getRunAsRoleName();
           String principalName = secMetaData.getRunAsPrincipalName();
          
           // the run-as principal might have extra roles mapped in the assembly-descriptor
           Set extraRoleNames = assemblyDescriptor.getSecurityRoleNamesByPrincipal(principalName);
           runAsIdentity = new RunAsIdentity(roleName, principalName, extraRoleNames);
           }
          
           securityManager = container.getSecurityManager();
           realmMapping = container.getRealmMapping();
          
           try
           {
           // Get the timeout method
           ejbTimeout = TimedObject.class.getMethod("ejbTimeout", new Class[]{Timer.class});
           }
           catch (NoSuchMethodException ignore)
           {
           }
           }
           }


          Later on, in when an EJB looks up another EJB, the org.jboss.ejb.plugins.SecurityInterceptor#invokeHome is called. There is a call that indiscrimintately pushes the runAsIdentity onto the stack. Since this is null from before, the next call is run with a null caller identity. This is confirmed by the TRACE below.

           public Object invokeHome(Invocation mi) throws Exception
           {
           // Authenticate the subject and apply any declarative security checks
           checkSecurityAssociation(mi);
          
           /* If a run-as role was specified, push it so that any calls made
           by this bean will have the runAsRole available for declarative
           security checks.
           */
           SecurityActions.pushRunAsIdentity(runAsIdentity);
          
           try
           {
           Object returnValue = getNext().invokeHome(mi);
           return returnValue;
           }
           finally
           {
           SecurityActions.popRunAsIdentity();
           SecurityActions.popSubjectContext();
           }
           }
          


          Here is the trace where you can see the caller identity of null being pushed, and the subsequent exception that is caused by it.

          2005-11-20 08:14:53,140 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] doesUserHaveRole(Set), subject: Subject:
           Principal: admin
           Principal: Roles(members:Guest,Administrator)
          
          2005-11-20 08:14:53,140 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] roles=Roles(members:Guest,Administrator)
          2005-11-20 08:14:53,140 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] hasRole(Administrator)=true
          2005-11-20 08:14:53,140 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] hasRole=true
          2005-11-20 08:14:53,140 TRACE [org.jboss.security.SecurityAssociation] pushRunAsIdentity, runAs=null
          2005-11-20 08:14:53,140 DEBUG [com.myejb.jboss.Session2SecurityProxy] Entered setEJBContext(EJBContext)
          2005-11-20 08:14:53,156 TRACE [org.jboss.security.plugins.JaasSecurityManager.mwo] getPrincipal, cache info: null
          2005-11-20 08:14:53,156 TRACE [org.jboss.security.SecurityAssociation] popRunAsIdentity, runAs=null
          2005-11-20 08:14:53,156 TRACE [org.jboss.security.SecurityAssociation] popSubjectContext, sc=org.jboss.security.SecurityAssociation$SubjectContext@652552{principal=admin,subject=7683106}
          2005-11-20 08:14:53,156 ERROR [org.jboss.ejb.plugins.LogInterceptor] TransactionRolledbackException in method: public abstract com.myejb.Session1Remote com.myejb.Session1Home.create() throws javax.ejb.CreateException,java.rmi.RemoteException, causedBy:
          java.lang.IllegalStateException: No valid security context for the caller identity
          


          It seems the StatelessSessionInstanceInterceptor is responsible for pushing the caller identity into the StatelessSessionEnterpriseContext, but it is pulling it from the methodInvocation. I tried to trace this all the way back to where the MethodInvocation was formed by looking at the proxy compiler, but I don't think I understand it well enough. To me it didn't look like the principal is set on the MI by the proxy itself, but I could be wrong.

          When the code finally gets to the point where it calls getCallerPrincipal(), here is the code that gets executed. I've watched this in the debugger. The beanPrincipal is null, principal is null, and rm is not null, so it tries to set the beanPrincipal by calling peekRunAsIdentity. As previously established, this is null so I get the "No valid security context" error.

           Principal getCallerPrincipalInternal()
           {
           if (beanPrincipal == null)
           {
           RealmMapping rm = con.getRealmMapping();
           if (principal != null)
           {
           if (rm != null)
           beanPrincipal = rm.getPrincipal(principal);
           else
           beanPrincipal = principal;
           }
           else if (rm != null)
           {
           // Check for the caller's run-as identity, not this bean's run-as
           beanPrincipal = SecurityActions.peekRunAsIdentity(1);
           if (beanPrincipal == null)
           {
           // Let the RealmMapping map the null principal
           beanPrincipal = rm.getPrincipal(principal);
           }
           }
           else
           { // Check for a unauthenticated principal value
           ApplicationMetaData appMetaData = con.getBeanMetaData().getApplicationMetaData();
           String name = appMetaData.getUnauthenticatedPrincipal();
           if (name != null)
           beanPrincipal = new SimplePrincipal(name);
           }
           }
           if( beanPrincipal == null )
           {
           throw new IllegalStateException("No valid security context for the caller identity");
           }
           return beanPrincipal;
           }
          


          Is it possible that the invokeHome method in SecurityInterceptor should be doing a conditional check on the pushRunAsIdentity? It seems that it should not be pushing it if the intent is to run as the caller identity.

          Thanks,
          Matt

          • 2. Re: JAAS Caller identity null on second EJB call in a transa
            mclark00

            I'm sure you've loved watching me have a conversation with myself, but I found the problem. In the security proxy for Session2, in the setEJBContext method, I was calling getCallerPrincipal(). Per table 3 in the EJB 2 spec, this isn't allowed.

            Thanks Scott, for the solution which I found in http://jira.jboss.com/jira/browse/JBAS-751.

            Matt

            • 3. Re: JAAS Caller identity null on second EJB call in a transa
              starksm64

              Its good your looking at the source. The unconditional push does not matter as the check for the effective run-as simply ignores nulls.