9 Replies Latest reply on Mar 25, 2004 8:39 AM by philc

    EJB security propagation issue when moving from JBoss 3.0.4

    mescalito

      Hi,

      At the moment I'm moving all our team's applications from JBoss 3.0.4 to JBoss 3.2.3. Everything is done, apart from the following problem, which is really killing me.

      We've got an application with 'standard' (I mean, nothing special or unusual) Web and EJB tiers, which both run under JAAS, under the same security domain (form-based authentication, custom LoginModule derived from DatabaseServerLoginModule with minimal amendments). It perfectly works on JBoss 3.0.4 (for at least a year and 3 production releases), but when deployed on JBoss 3.2.3, security context (propagated from Web actions) dissappears within EJB layer. I've put some logging into the LoginModule and the code where I invoke EJB from Web tier (below in bold).

      18:14:40,137 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking login()
      18:14:40,137 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking getUsernameAndPassword()= [my_login, <<my_password>>]
      18:14:40,153 INFO [com.smth.portal.security.auth.PortalLoginModule] getUsernameAndPassword()= [my_login, <<my_password>>]
      18:14:40,168 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking validatePassword("<<my_password>>, <<my_password>>")
      18:14:40,184 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking commit(): true
      18:14:40,231 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking login()
      18:14:40,231 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking getUsernameAndPassword()= [my_login, <<my_password>>]
      18:14:40,247 INFO [com.smth.portal.security.auth.PortalLoginModule] getUsernameAndPassword()= [my_login, <<my_password>>]
      18:14:40,262 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking validatePassword("<<my_password>>, <<my_password>>")
      18:14:40,278 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking commit(): true
      18:14:40,293 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking logout()
      18:14:40,387 INFO [STDOUT] IN WEB ACTION: SecurityAssociation.getPrincipal() = my_login, getCredential() = [C@e628d8
      18:14:40,403 INFO [STDOUT] IN EJB LAYER: context.context.getCallerPrincipal().getName()=my_login
      18:14:40,403 INFO [STDOUT] IN EJB LAYER: SecurityAssociation.getPrincipal() = null, getCredential() = null
      18:14:40,434 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking login()
      18:14:40,434 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking getUsernameAndPassword()= [null, null]
      18:14:40,450 INFO [com.smth.portal.security.auth.PortalLoginModule] getUsernameAndPassword()= [null, null]
      18:14:40,450 INFO [com.smth.portal.security.auth.PortalLoginModule] Invoking abort()
      18:14:40,466 ERROR [org.jboss.ejb.plugins.SecurityInterceptor] Authentication exception, principal=null
      18:14:40,466 ERROR [org.jboss.ejb.plugins.LogInterceptor] EJBException, causedBy:
      java.lang.SecurityException: Authentication exception, principal=null
       at org.jboss.ejb.plugins.SecurityInterceptor.checkSecurityAssociation(SecurityInterceptor.java:164)
       at org.jboss.ejb.plugins.SecurityInterceptor.invokeHome(SecurityInterceptor.java:81)
       at org.jboss.ejb.plugins.LogInterceptor.invokeHome(LogInterceptor.java:120)
       at org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor.invokeHome(ProxyFactoryFinderInterceptor.java:93)
       at org.jboss.ejb.EntityContainer.internalInvokeHome(EntityContainer.java:483)
       at org.jboss.ejb.Container.invoke(Container.java:720)
       at org.jboss.ejb.plugins.local.BaseLocalProxyFactory.invokeHome(BaseLocalProxyFactory.java:293)
       at org.jboss.ejb.plugins.local.LocalHomeProxy.invoke(LocalHomeProxy.java:110)
       at $Proxy246.findByUserId(Unknown Source)
       at com.smth.app.alm.object.task.ejb.TaskFacadeSL.getTaskList(TaskFacadeSL.java:247)
       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
       at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:324)
       at org.jboss.ejb.StatelessSessionContainer$ContainerInterceptor.invoke(StatelessSessionContainer.java:683)
       at org.jboss.resource.connectionmanager.CachedConnectionInterceptor.invoke(CachedConnectionInterceptor.java:185)
       at org.jboss.ejb.plugins.StatelessSessionInstanceInterceptor.invoke(StatelessSessionInstanceInterceptor.java:72)
       at org.jboss.ejb.plugins.AbstractTxInterceptor.invokeNext(AbstractTxInterceptor.java:84)
       at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:267)
       at org.jboss.ejb.plugins.TxInterceptorCMT.invoke(TxInterceptorCMT.java:128)
       at org.jboss.ejb.plugins.SecurityInterceptor.invoke(SecurityInterceptor.java:118)
       at org.jboss.ejb.plugins.LogInterceptor.invoke(LogInterceptor.java:191)
       at org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor.invoke(ProxyFactoryFinderInterceptor.java:122)
       at org.jboss.ejb.StatelessSessionContainer.internalInvoke(StatelessSessionContainer.java:331)
       at org.jboss.ejb.Container.invoke(Container.java:700)
       at org.jboss.ejb.plugins.local.BaseLocalProxyFactory.invoke(BaseLocalProxyFactory.java:375)
       at org.jboss.ejb.plugins.local.StatelessSessionProxy.invoke(StatelessSessionProxy.java:83)
       at $Proxy387.getTaskList(Unknown Source)
       at com.smth.app.alm.web.actions.TaskAction.performListAction(TaskAction.java:118)
       ...
      

      And the source code:
      In Web action:
      TaskFacade facade = BeanHelper.getTaskFacade();
      String msg;
      try {
       msg = "SecurityAssociation.getPrincipal() = "
       +SecurityAssociation.getPrincipal() + ", "
       + "getCredential() = "
       + SecurityAssociation.getCredential();
      } catch(IllegalStateException ie) {
       msg = null;
      }
      System.out.println("IN WEB ACTION: "+msg);
      request.setAttribute("task.list", facade.getTaskList());
      

      and in EJB tier:
      public Collection getTaskList() throws ApplicationException {
       String username;
       try {
       username = "context.context.getCallerPrincipal().getName()="+context.getCallerPrincipal().getName();
       } catch(IllegalStateException ie) {
       username = null;
       }
       System.out.println("IN EJB LAYER: " + username);
       String msg = "SecurityAssociation.getPrincipal() = "
       + SecurityAssociation.getPrincipal() + ", "
       + "getCredential() = "
       + SecurityAssociation.getCredential();
       System.out.println("IN EJB LAYER: " + msg);
       ...
      }
      

      Method permissions for EJBs are declared in the same way for the same role:
      <security-role>
       <role-name>alm_user</role-name>
      </security-role>
      <method-permission>
       <role-name>alm_user</role-name>
       <method>
       <ejb-name>TaskFacade</ejb-name>
       <method-name>*</method-name>
       </method>
       ...
       <method>
       <ejb-name>Task</ejb-name>
       <method-name>*</method-name>
       </method>
      </method-permission>
      


      So if you take a look at the bold text in log message, you may notice, that the first call to EJB layer goes fine, and the EJB gets a context with Principal set up correctly, but with nullfied SecurityAssociation attributes. The same .EAR-file, deployed on JBoss 3.0.4, has non-null SecurityAssociation attributes in the same place.
      Obviously, when the first EJB in the invocation chain tries to make a call to the second one ($Proxy246.findByUserId(Unknown Source)) with nullified SecurityAssociation attributes, it gets stuck in SecurityInterceptor.

      Could please somebody explain to me, at which stage and why SecurityAssociation.getPrincipal() and SecurityAssociation.getCredential() are being nullified in JBoss 3.2.3 (and prevent application to work correctly), but remain the same in JBoss 3.0.4?
      I've checked through virtually all the possible explanations (I've also got the same problem with other apps, which survived through JBoss 2.4.x, Jboss 3.0.x), so I desperately need some JBoss guru to find out an answer.

      Thanks in advance!

      Stan

        • 1. Re: EJB security propagation issue when moving from JBoss 3.
          starksm64

          What is establishing the thread security context above this line in the
          given statcktrace?

          com.smth.app.alm.web.actions.TaskAction.performListAction

          This is the context that needs to be under a secured servlet or doing a JAAS login to set the thread association for the call into the ejb tier. jboss-3.2.3 is stricter about the scope of the web tier call security association with the request thread than 3.0.4. The SecurityAssociation is cleared as soon as the requesting thread returns from the servlet invocaton from within the SecurityAssociationValve which you are not showing in the stack trace.

          • 2. Re: EJB security propagation issue when moving from JBoss 3.
            mescalito

            Hi Scott,

            Thank you for the hint!

            Yes, this action is a part of standard MVC framework and is invoked by a secured servlet (FORM-based authentication).

            I've posted my EJB and Web deployment descriptors in the Internet: http://www.chat.ru/~kazarena/dd.zip.

            Could you please take a quick look to find out if there's anything in there, which is fine for Jboss 3.0.4, but doesn't work in Jboss 3.2.3?

            Your answer explains why I'm getting a proper Principal in the first EJB, but tt's still a mistery to me, how I can get to EJB layer (which means: to pass the EJB security restrictions, defined in my ejb-jar.xml, check the roles), but get stuck inside EJB layer (when invoking the second bean) with security violation.

            I would really appreciate it, if you could take a look at the descriptors.
            Thanks!

            Stan

            • 3. Re: EJB security propagation issue when moving from JBoss 3.
              starksm64

              The descriptors won't be of any help. You have to post the full stack trace of the security exception to see what invocation layers are involved. If you have an example ear that demonstrates the problem create bug report on sourceforge and attach the ear to the report.

              • 4. Re: EJB security propagation issue when moving from JBoss 3.
                mescalito

                Hi Scott,
                This is the full stack trace:

                2004-03-19 09:15:38,590 ERROR [org.jboss.ejb.plugins.LogInterceptor] EJBException, causedBy:
                java.lang.SecurityException: Authentication exception, principal=null
                 at org.jboss.ejb.plugins.SecurityInterceptor.checkSecurityAssociation(SecurityInterceptor.java:164)
                 at org.jboss.ejb.plugins.SecurityInterceptor.invokeHome(SecurityInterceptor.java:81)
                 at org.jboss.ejb.plugins.LogInterceptor.invokeHome(LogInterceptor.java:120)
                 at org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor.invokeHome(ProxyFactoryFinderInterceptor.java:93)
                 at org.jboss.ejb.EntityContainer.internalInvokeHome(EntityContainer.java:483)
                 at org.jboss.ejb.Container.invoke(Container.java:720)
                 at org.jboss.ejb.plugins.local.BaseLocalProxyFactory.invokeHome(BaseLocalProxyFactory.java:293)
                 at org.jboss.ejb.plugins.local.LocalHomeProxy.invoke(LocalHomeProxy.java:110)
                 at $Proxy728.findByUserId(Unknown Source)
                 at com.smth.wipe.alm.object.task.ejb.TaskFacadeSL.getTaskList(TaskFacadeSL.java:204)
                 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                 at java.lang.reflect.Method.invoke(Method.java:324)
                 at org.jboss.ejb.StatelessSessionContainer$ContainerInterceptor.invoke(StatelessSessionContainer.java:683)
                 at org.jboss.resource.connectionmanager.CachedConnectionInterceptor.invoke(CachedConnectionInterceptor.java:185)
                 at org.jboss.ejb.plugins.StatelessSessionInstanceInterceptor.invoke(StatelessSessionInstanceInterceptor.java:72)
                 at org.jboss.ejb.plugins.AbstractTxInterceptor.invokeNext(AbstractTxInterceptor.java:84)
                 at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:267)
                 at org.jboss.ejb.plugins.TxInterceptorCMT.invoke(TxInterceptorCMT.java:128)
                 at org.jboss.ejb.plugins.SecurityInterceptor.invoke(SecurityInterceptor.java:118)
                 at org.jboss.ejb.plugins.LogInterceptor.invoke(LogInterceptor.java:191)
                 at org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor.invoke(ProxyFactoryFinderInterceptor.java:122)
                 at org.jboss.ejb.StatelessSessionContainer.internalInvoke(StatelessSessionContainer.java:331)
                 at org.jboss.ejb.Container.invoke(Container.java:700)
                 at org.jboss.ejb.plugins.local.BaseLocalProxyFactory.invoke(BaseLocalProxyFactory.java:375)
                 at org.jboss.ejb.plugins.local.StatelessSessionProxy.invoke(StatelessSessionProxy.java:83)
                 at $Proxy881.getTaskList(Unknown Source)
                 at com.smth.wipe.alm.web.actions.TaskAction.performListAction(TaskAction.java:115)
                 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                 at java.lang.reflect.Method.invoke(Method.java:324)
                 at com.smth.webframework.controller.Action.execute(Action.java:95)
                 at com.smth.webframework.controller.ActionCache.executeAction(ActionCache.java:66)
                 at com.smth.webframework.controller.RequestProcessor.processAction(RequestProcessor.java:371)
                 at com.smth.webframework.controller.RequestProcessor.process(RequestProcessor.java:105)
                 at com.smth.webframework.controller.FrontController.process(FrontController.java:159)
                 at com.smth.webframework.controller.FrontController.doGet(FrontController.java:129)
                 at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
                 at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
                 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
                 at com.smth.portal.security.web.impl.PortalSessionFilterImpl.doFilter(PortalSessionFilterImpl.java:99)
                 at com.smth.portal.security.web.PortalSessionFilter.doFilter(PortalSessionFilter.java:29)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:213)
                 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
                 at com.smth.wipe.alm.web.FileUploadFilter.doFilter(FileUploadFilter.java:86)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:213)
                 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
                 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
                 at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
                 at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
                 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
                 at org.jboss.web.tomcat.security.JBossSecurityMgrRealm.invoke(JBossSecurityMgrRealm.java:220)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
                 at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:553)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
                 at org.apache.catalina.valves.CertificatesValve.invoke(CertificatesValve.java:246)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
                 at org.jboss.web.tomcat.tc4.statistics.ContainerStatsValve.invoke(ContainerStatsValve.java:76)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
                 at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
                 at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
                 at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2417)
                 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
                 at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
                 at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:172)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
                 at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:65)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
                 at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:577)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
                 at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
                 at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
                 at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
                 at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
                 at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
                 at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
                 at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:197)
                 at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:781)
                 at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:549)
                 at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:605)
                 at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:677)
                 at java.lang.Thread.run(Thread.java:534)
                


                I'll try to create a sample EAR now, which has the same issue, but it won't be easy as the original one has heavy dependencies on a lot of stuff (e.g. DBs, chain of previously deployed EARs, etc.)

                Regards,
                Stan

                • 5. Re: EJB security propagation issue when moving from JBoss 3.
                  starksm64

                  Something is clearing the security association between the two ejbs then. Hack the org.jboss.security.SecurityAssociation to print a stack trace of who is calling clear or setPrincipal/setCredential with null values, or use a debugger to find this out.

                  • 6. Re: EJB security propagation issue when moving from JBoss 3.
                    mescalito

                    Hi Scott,

                    Looking at one of the recent questions about clearing SecurityAssociation when invoking JMX, I've finally identified the problem. I'm using JMX all over the place in our custom logging framework (using an RMIAdaptor call), but I couldn't even think about it as a cause of the problem, as in the case shown before it's been used in ejbCreate() method, very indirectly. When I commented out all Logging Service calls, it eliminated the problem.

                    So the solution for me will be to change the way of JMX invocation.

                    By the way, what is the preferred way for JBoss to retrieve MBeanServer reference? Is it safe (in terms of compatibility with future versions of JBoss) to use a call like: org.jboss.mx.util.MBeanServerLocator.locateJBoss() ?

                    Thank you very much for your help.

                    Stan.

                    • 7. Re: EJB security propagation issue when moving from JBoss 3.
                      philc

                      I have exactly the same problem. I started upgrading to 3.2.3 this week. The application works well except for Session Beans making JMX calls with RMIAdaptor. The security principal gets cleared.

                      Context ic = new InitialContext();
                      org.jboss.jmx.adaptor.rmi.RMIAdaptor server = (org.jboss.jmx.adaptor.rmi.RMIAdaptor) ic.lookup("jmx/rmi/RMIAdaptor");
                      ObjectName name = new ObjectName(myMBeanName);
                      myAnswer = server.invoke(name, "askQuestion", new Object[] {}, new String[] {});
                      


                      Is there a better way to make JMX calls?

                      Philippe


                      • 8. Re: EJB security propagation issue when moving from JBoss 3.
                        starksm64

                         

                         // Standard
                         MBeanServer server = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
                         // Or jboss specific
                         MBeanServer server = MBeanServerLocator.locateJBoss();
                        
                         // Standard MBeanServer usage
                         ObjectName name = ...;
                         String opName = "...";
                         Object[] args = {...};
                         String[] sig = {...};
                         server.invoke(name, opName, args, sig)
                        


                        • 9. Re: EJB security propagation issue when moving from JBoss 3.
                          philc

                          Great! This fixes the problem for me. Thank you.