CSIv2 on JBoss AS

In this document we describe how to use CSIv2 on JBoss AS to enable security context propagation via IIOP. The CSIv2 (Common Secure Interoperability Protocol Version 2) specification defines a protocol for establishing secure inter-ORB communication.

 

Software used: JBoss Application Server 6.0.0.M3, JDK 1.6.0_18.

 

Sample EJB2 Application

In order to demonstrate how to enable CSIv2 on JBoss, we are going to use a simple EJB2 bean that will be accessible via IIOP. So let's take a look at the bean classes and deployment descriptors.

Home Interface

We start by showing the bean home interface. This is just a standard EJB2 home declaration:

 

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors. 
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.test.security.ejb2;

import java.rmi.RemoteException;

import javax.ejb.CreateException;
import javax.ejb.EJBHome;

/**
 * <p>
 * This is the home interface of the session bean used in CSIv2 security example.
 * </p>
 * 
 * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
 */
public interface SimpleEJB2SessionHome extends EJBHome
{
   /**
    * <p>
    * Creates and returns a reference to the {@code SimpleEJB2Session} interface.
    * </p>
    *
    * @return a reference to the {@code SimpleEJB2Session} remote interface.
    */
   public SimpleEJB2Session create() throws CreateException, RemoteException;

}

Remote Interface

The remote interface for this bean is also pretty standard. It declares the bean business methods. In our case, we have four methods, each one with specific security requirements:

 

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors. 
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.test.security.ejb2;

import java.rmi.RemoteException;
import java.security.Principal;

import javax.ejb.EJBObject;

/**
 * <p>
 * This is the remote interface of the session bean used in the CSIv2 security example.
 * </p>
 * 
 * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
 */
public interface SimpleEJB2Session extends EJBObject
{
   /**
    * <p>
    * This is a method available for regular users and administrators. The deployment descriptor must enforce that
    * only users in RegularUser or Administrator roles are granted access to this method.
    * </p>
    * 
    * @return the caller's {@code Principal}.
    */
   public Principal invokeRegularMethod() throws RemoteException;

   /**
    * <p>
    * This is a method available for administrators only. The deployment descriptor must enforce that only users in the
    * Administrator role are granted access to this method.
    * </p>
    * 
    * @return the caller's {@code Principal}.
    */
   public Principal invokeAdministrativeMethod() throws RemoteException;

   /**
    * <p>
    * This is a method available for all authenticated users, regardless of role. The deployment descriptor must
    * contain an {@code unchecked} element for this method.
    * </p>
    * 
    * @return the caller's {@code Principal}.
    */
   public Principal invokeUnprotectedMethod() throws RemoteException;

   /**
    * <p>
    * This is a method that is unavailable for all roles. The deployment descriptor must add this method to the
    * {@code exclude-list} element.
    * </p>
    * 
    * @return the caller's {@code Principal}.
    */
   public Principal invokeUnavailableMethod() throws RemoteException;

}

Bean Implementation

Our simple bean wouldn't be complete without a bean implementation class. As expected, this class has  implementations for the methods required by the EJB2 specification and for the methods declared in the remote interface:

 

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors. 
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.test.security.ejb2;

import java.rmi.RemoteException;
import java.security.Principal;

import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;

/**
 * <p>
 * This is the bean class implementation of the session bean used in the CSIv2 security example.
 * </p>
 * 
 * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
 */
public class SimpleEJB2SessionBean implements SessionBean
{
    private SessionContext context;

    /**
     * <p>
     * {@code ejbCreate} method required by the EJB2 specification. 
     * </p>
     *
     * @throws CreateException if an error occurs while creating the session bean.
     */
    public void ejbCreate() throws CreateException
    {
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.ejb.SessionBean#ejbActivate()
     */
    public void ejbActivate()
    {
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.ejb.SessionBean#ejbPassivate()
     */
    public void ejbPassivate()
    {
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.ejb.SessionBean#ejbRemove()
     */
    public void ejbRemove()
    {
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext context)
     */
    public void setSessionContext(SessionContext context)
    {
        this.context = context;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.jboss.test.security.ejb2.SimpleEJB2Session#invokeRegularMethod()
     */
    public Principal invokeRegularMethod()
    {  
       // this method can be invoked by RegularUser and Administrator roles.
       return this.context.getCallerPrincipal();
    }

   /*
    * (non-Javadoc)
    * 
    * @see org.jboss.test.security.ejb2.SimpleEJB2Session#invokerAdministrativeMethod()
    */
   public Principal invokeAdministrativeMethod()
   {
      // this method can be invoked by the Administrator role only.
      return this.context.getCallerPrincipal();
   }

   /*
    * (non-Javadoc)
    * 
    * @see org.jboss.test.security.ejb2.SimpleEJB2Session#invokeUnprotectedMethod()
    */
   public Principal invokeUnprotectedMethod()
   {
      // this method can be invoked by any role.
      return this.context.getCallerPrincipal();
   }

   /*
    * (non-Javadoc)
    * 
    * @see org.jboss.test.security.ejb2.SimpleEJB2Session#invokeUnavailableMethod()
    */
   public Principal invokeUnavailableMethod()
   {
      // this method cannot be invoked by any role.
      throw new EJBException("Excluded method - no access should be allowed");
   }
}

Deployment Descriptors

Our simple application has two deployment descriptors: the standard ejb-jar.xml, that is used to specify the bean and its security requirements, and the container-specific jboss.xml, that is used to specify the JNDI mapping for our bean. Let's start by taking a look at the ejb-jar.xml:

 

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE ejb-jar PUBLIC
      "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
      "http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar>
   <display-name>EBJ2 SAML Tests</display-name>
   <enterprise-beans>
      <session>
         <description>A secured stateless session bean</description>
         <ejb-name>SimpleEJB2Session</ejb-name>
         <home>org.jboss.test.security.ejb2.SimpleEJB2SessionHome</home>
         <remote>org.jboss.test.security.ejb2.SimpleEJB2Session</remote>
         <ejb-class>org.jboss.test.security.ejb2.SimpleEJB2SessionBean</ejb-class>
         <session-type>Stateless</session-type>
         <transaction-type>Container</transaction-type>
      </session>
   </enterprise-beans>

   <assembly-descriptor>
      <security-role>
         <description>The role required to invoke administrative methods</description>
         <role-name>Administrator</role-name>
      </security-role>
      <security-role>
         <description>The role required to invoke regular methods</description>
         <role-name>RegularUser</role-name>
      </security-role>

      <!-- specify unchecked methods (methods available to all roles) -->
      <method-permission>
         <unchecked/>
         <method>
            <ejb-name>SimpleEJB2Session</ejb-name>
            <method-name>invokeUnprotectedMethod</method-name>
         </method>
         <method>
            <ejb-name>SimpleEJB2Session</ejb-name>
            <method-intf>Home</method-intf>
            <method-name>create</method-name>
         </method>
      </method-permission>

      <!-- specify methods the Administrator role can access -->
      <method-permission>
         <role-name>Administrator</role-name>
         <method>
            <ejb-name>SimpleEJB2Session</ejb-name>
            <method-intf>Remote</method-intf>
            <method-name>*</method-name>
         </method>
      </method-permission>

      <!-- specify methods the RegularUser role can access -->
      <method-permission>
         <role-name>RegularUser</role-name>
         <method>
            <ejb-name>SimpleEJB2Session</ejb-name>
            <method-intf>Remote</method-intf>
            <method-name>invokeRegularMethod</method-name>
         </method>
      </method-permission>

      <!-- specify excluded methods (those not available to any role) -->
      <exclude-list>
         <description>A method that no one can access in this deployment</description>
         <method>
            <ejb-name>SimpleEJB2Session</ejb-name>
            <method-name>invokeUnavailableMethod</method-name>
         </method>
      </exclude-list>
   </assembly-descriptor>

</ejb-jar>

 

As we can see, this descriptor is also very simple. All we are doing is declaring our sample bean and its security requirements. Now let's take a look at the contents of jboss.xml:

 

<?xml version="1.0"?>
<!DOCTYPE jboss PUBLIC
      "-//JBoss//DTD JBOSS 5.0//EN"
      "http://www.jboss.org/j2ee/dtd/jboss_5_0.dtd">
<jboss>
   <security-domain>java:/jaas/csiv2-sample</security-domain>
   <enterprise-beans>
      <session>
         <ejb-name>SimpleEJB2Session</ejb-name>
         <jndi-name>sample/SimpleEJB2Session</jndi-name>
      </session>
   </enterprise-beans>
</jboss>

 

This descriptor is mapping the SimpleEJB2SessionHome interface to sample/SimpleEJB2Session in JNDI. It also says that the csiv2-sample security domain must be used to authenticate and authorize callers.

Security Domain Configuration

The security domain that has been specified in jboss.xml must be configured in the application server. We can do that by editing JBOSS_HOME/server/partition/conf/login-config.xml or by including the security domain configuration directly in the deployment jar file. We will show how to do the latter.

 

The first thing to do is to create a *-jboss-beans.xml file in the root of our deployment and specify the application policy and all login modules that will be used. The -jboss-beans.xml extension is automatically parsed by JBoss AS and the application policy is registered at runtime by the security integration layer. So let's create a csiv2-sample-jboss-beans.xml:

 

<?xml version="1.0" encoding="UTF-8"?>

<deployment xmlns="urn:jboss:bean-deployer:2.0">

   <!-- csiv2 test application-policy definition -->
   <application-policy xmlns="urn:jboss:security-beans:1.0" name="csiv2-sample">
      <authentication>
         <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required">
            <module-option name="password-stacking">useFirstPass</module-option>
            <module-option name="usersProperties">csiv2-sample-users.properties</module-option>
            <module-option name="rolesProperties">csiv2-sample-roles.properties</module-option>
         </login-module>
      </authentication>
   </application-policy>

</deployment>

 

In this example, we decided to use the UsersRolesLoginModule, so we also have to provide the users and roles properties files. Let's start with the csiv2-sample-users.properties, which maps users to passwords:

 

UserA=PassA
UserB=PassB
UserC=PassC

 

And now the csiv2-sample-roles.properties, which maps users to roles:

 

UserA=RegularUser,Administrator
UserB=RegularUser
UserC=Guest

 

Both properties files must also be placed in the root of the deployment (simple-session.jar).

Exposing the Bean via IIOP

The sample application we've shown so far can be deployed on JBoss and we could easily write a client application to invoke this bean. But if we are to use CSIv2 to carry the security context, we must first configure JBoss to bind the IIOP stubs of our bean instead of the standard dynamic proxies that use JRMP.

 

This is accomplished by specifying the IIOP invoker-proxy-binding-name in jboss.xml. We also need to specify the security requirements of our bean in the ior-security-config section, as shown bellow:

 

<?xml version="1.0"?>
<!DOCTYPE jboss PUBLIC
      "-//JBoss//DTD JBOSS 5.0//EN"
      "http://www.jboss.org/j2ee/dtd/jboss_5_0.dtd">
<jboss>
   <security-domain>java:/jaas/csiv2-sample</security-domain>
   <enterprise-beans>
      <session>
         <ejb-name>SimpleEJB2Session</ejb-name>
         <jndi-name>sample/SimpleEJB2Session</jndi-name>
         <configuration-name>Standard Stateless SessionBean</configuration-name>
         <invoker-bindings>
            <invoker>
               <invoker-proxy-binding-name>iiop</invoker-proxy-binding-name>
            </invoker>
         </invoker-bindings>
         <ior-security-config>
            <transport-config>
               <integrity>none</integrity>
               <confidentiality>none</confidentiality>
               <establish-trust-in-target>none</establish-trust-in-target>
               <establish-trust-in-client>none</establish-trust-in-client>
            </transport-config>
            <as-context>
               <auth-method>username_password</auth-method>
               <realm>default</realm>
               <required>true</required>
            </as-context>
            <sas-context>
               <caller-propagation>none</caller-propagation>
            </sas-context>
         </ior-security-config>
      </session>
   </enterprise-beans>
</jboss>

 

The IIOP invoker proxy binding is what is going to expose our bean via IIOP so it can be invoked by CORBA clients. In other words, the sample/SimpleEJB2Session context in JNDI will now expose the IOR of the bean interfaces.

 

The ior-security-config section specifies the security mechanisms that need to be included in the IOR. Let's take a brief look at the contents of this element:

  • the transport subsection is used to specify the transport-level requirements. So, for example, setting confidentiality or integrity to required would cause the client ORB to use the SSL port when invoking the server.
  • the as-context subsection is used to specify the authentication mechanism to be applied. In the EJB case, only username_password is supported.
  • the sas-context subsection is used to specify which attributes are to be included in the security context. For EJBs, this is used to propagate the caller identity in communications between containers in which a trust relationship exists. So if container C1 calls a bean on container C2 and C2 trusts C1, there is no need for C2 to authenticate the initiating client again, so it can trust C1 to have done that and use the specified identity as the caller principal.

 

If we package and deploy our bean now, we will be able to see its IOR on JBoss console (make sure to deploy the bean in the all partition as we will need the iiop jars that are not available in the default partition):

 

11:34:36,187 INFO  [EjbDeployer] installing bean: ejb/#SimpleEJB2Session,uid12738493
11:34:36,187 INFO  [EjbDeployer]   with dependencies:
11:34:36,187 INFO  [EjbDeployer]   and supplies:
11:34:36,187 INFO  [EjbDeployer]     jndi:SimpleEJB2Session/org.jboss.test.security.ejb2.SimpleEJB2Session
11:34:36,187 INFO  [EjbDeployer]     jndi:sample/SimpleEJB2Session
11:34:36,248 INFO  [EjbModule] Deploying SimpleEJB2Session
11:34:36,730 INFO  [giop] ClientConnectionManager: created new ClientGIOPConnection to 127.0.0.1:3528 (1b5f576)
11:34:36,782 INFO  [sample/SimpleEJB2Session] EJBHome reference for sample/SimpleEJB2Session:
IOR:0000000000000048524D493A6F72672E6A626F73732E746573742E73656375726974792E656A62322E53696D706C65454A423253657373696F6E486F6D653A30303030
3030303030303030303030300000000002000000000000013C000102000000000A3132372E302E302E31000DC80000002E4A426F73732F454A42486F6D65262573616D706C
65262553696D706C65454A423253657373696F6E2FACED0005700000000000060000000000000008000000004A414300000000010000002000000000050100010000000100
01000100010109000000020501000100010100000000190000003F0000000000000037687474703A2F2F3132372E302E302E313A383038332F576562434C5B73616D706C65
2F53696D706C65454A423253657373696F6E5D2F00000000002100000050000000000000000100400000000000220000000000400040000000080606678102010101000000
170401000806066781020101010000000764656661756C7400000000000000000000000000000000000000002000000004000000000000001F000000040000000300000001
000000680000000000000003000000190000003F0000000000000037687474703A2F2F3132372E302E302E313A383038332F576562434C5B73616D706C652F53696D706C65
454A423253657373696F6E5D2F00000000002000000004000000000000001F0000000400000003
11:34:36,785 INFO  [sample/SimpleEJB2Session] Home IOR for SimpleEJB2Session bound to iiop/sample/SimpleEJB2Session in JNP naming service
11:34:36,786 INFO  [giop] ClientConnectionManager: created new ClientGIOPConnection to 127.0.0.1:3528 (1513c2b)
11:34:36,866 INFO  [poa] oid: 
72 6F 6F 74 2F 63 74 78 31                                  root/ctx1
object is activated
11:34:36,868 INFO  [naming] Bound context: sample
11:34:36,869 INFO  [giop] ClientConnectionManager: found ClientGIOPConnection to 127.0.0.1:3528 (1513c2b)
11:34:36,872 INFO  [naming] re-Bound name: SimpleEJB2Session
11:34:36,872 INFO  [sample/SimpleEJB2Session] Home IOR for SimpleEJB2Session bound to sample/SimpleEJB2Session in CORBA naming service

 

We can now use a tool, like JacORB's dior, to inspect the IOR:

 

[sneusatz@BLACKBIRD bin]$ ./dior -i IOR:0000000000000048524D493A6F72672E6A626F73732E746573742E73656375726974792E656A62322E53696D706C65454A42
3253657373696F6E486F6D653A303030303030303030303030303030300000000002000000000000013C000102000000000A3132372E302E302E31000DC80000002E4A426F73
732F454A42486F6D65262573616D706C65262553696D706C65454A423253657373696F6E2FACED0005700000000000060000000000000008000000004A414300000000010000
00200000000005010001000000010001000100010109000000020501000100010100000000190000003F0000000000000037687474703A2F2F3132372E302E302E313A383038
332F576562434C5B73616D706C652F53696D706C65454A423253657373696F6E5D2F000000000021000000500000000000000001004000000000002200000000004000400000
00080606678102010101000000170401000806066781020101010000000764656661756C7400000000000000000000000000000000000000002000000004000000000000001F
000000040000000300000001000000680000000000000003000000190000003F0000000000000037687474703A2F2F3132372E302E302E313A383038332F576562434C5B7361
6D706C652F53696D706C65454A423253657373696F6E5D2F00000000002000000004000000000000001F0000000400000003
May 11, 2010 11:37:34 AM org.jacorb.config.JacORBConfiguration init
INFO: configuration jacorb loaded from file /home/sneusatz/Download/jacorb-2.3.1-patched/etc/jacorb.properties
2010-05-11 11:37:34.811 FINE jacorb.interop.strict_check_on_tc_creation set to true
2010-05-11 11:37:34.812 INFO 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    JacORB V 2.3.1, www.jacorb.org
    (C) The JacORB project 27-May-2009
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2010-05-11 11:37:34.815 FINE RequestProcessorPoolFactory settings: thread_pool_min=5 thread_pool_max=20 thread_pool_shared=false
2010-05-11 11:37:34.817 FINE added ORBInitializer: org.jacorb.orb.standardInterceptors.IORInterceptorInitializer
2010-05-11 11:37:34.824 FINE defaulting to DefaultSocketFactory
2010-05-11 11:37:34.828 FINE Maximum connection threads: 1000
2010-05-11 11:37:34.828 FINE Maximum idle threads: 5
2010-05-11 11:37:34.830 INFO Property "jacorb.hashtable_class" is set to: java.util.Hashtable
2010-05-11 11:37:34.841 FINE TCS set to UTF8
2010-05-11 11:37:34.851 INFO InterceptorManager started with 0 Server Interceptors, 0 Client Interceptors and 1 IOR Interceptors
2010-05-11 11:37:34.853 FINE Under Linux the encoding name is UTF-8 and the canonical encoding name is UTF8
2010-05-11 11:37:34.875 FINE created org.omg.ETF.Factories: org.jacorb.orb.iiop.IIOPFactories
2010-05-11 11:37:34.879 INFO configuration jacorb loaded from file /home/sneusatz/Download/jacorb-2.3.1-patched/etc/jacorb.properties
2010-05-11 11:37:34.879 FINE jacorb.interop.strict_check_on_tc_creation set to true
2010-05-11 11:37:34.880 INFO created ORBSingleton
------IOR components-----
TypeId    :    RMI:org.jboss.test.security.ejb2.SimpleEJB2SessionHome:0000000000000000
TAG_INTERNET_IOP Profiles:
    Profile Id:        0
    IIOP Version:        1.2
    Host:            127.0.0.1
    Port:            3528
    Object key (URL):    JBoss/EJBHome&%25sample&%25SimpleEJB2Session/%AC%ED%00%05p
    Object key (hex):    0x4A 42 6F 73 73 2F 45 4A 42 48 6F 6D 65 26 25 73 61 6D 70 6C 65 26 25 53 69 6D 70 6C 65 45 4A 42 32 53 65 73 73 69 6F 6E 2F AC ED 00 05 70 
    -- Found 6 Tagged Components--
    #0: TAG_ORB_TYPE
        Type: 1245790976 (JacORB)
    #1: TAG_CODE_SETS
        ForChar native code set Id: UTF8
        Char Conversion Code Sets: ISO8859_1
        ForWChar native code set Id: UTF16
        WChar Conversion Code Sets: UTF8
, UCS2
    #2: TAG_JAVA_CODEBASE
        Codebase: http://127.0.0.1:8083/WebCL[sample/SimpleEJB2Session]/
    #3: TAG_CSI_SEC_MECH_LIST
        is stateful: false
        CompoundSecMech #0
            target_requires: 64
            transport mechanism tag: TAG_NULL_TAG
            AS_ContextSec target_supports: 64
            AS_ContextSec target_requires: 64
            AS_ContextSec mech: 06 06 67 81 02 01 01 01 
            AS_ContextSec target_name: default
            SAS_ContextSec target_supports: 0
            SAS_ContextSec target_requires: 0
            SAS_ContextSec Naming types: 0

    Unknown tag : 32
    Unknown tag : 31

Components in MULTIPLE_COMPONENTS profile: 3
    #0: TAG_JAVA_CODEBASE
        Codebase: http://127.0.0.1:8083/WebCL[sample/SimpleEJB2Session]/
    Unknown tag : 32
    Unknown tag : 31

 

Of all the displayed information, the most interesting parts are the CSI_SEC_MECH_LIST tag, which specifies the CSIv2 security requirements, and the JAVA_CODEBASE tag, which specifies the URL from where the IIOP stubs can be retrieved in runtime.

Sample Client Application

In this section we demonstrate how to invoke the sample EJB via IIOP using JacORB as the client ORB.

Client Code

Let's start by taking a look at the client class:

 

/*
 * JBoss, Home of Professional Open Source Copyright 2009, Red Hat Middleware
 * LLC, and individual contributors by the @authors tag. See the copyright.txt
 * in the distribution for a full listing of individual contributors.
 * 
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This software is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package test;

import java.io.IOException;
import java.rmi.AccessException;
import java.security.Principal;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;

import org.jboss.test.security.ejb2.SimpleEJB2Session;
import org.jboss.test.security.ejb2.SimpleEJB2SessionHome;

/**
 * <p>
 * This class tests the usage of CSIv2 to propagate security information via IIOP.
 * </p>
 * 
 * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
 */
public class CSIv2Test
{

   private Hashtable<String, Object> env;
   
   public static void main(String[] args) throws Exception
   {
      CSIv2Test test = new CSIv2Test();
      test.testCSIv2("UserA", "PassA");
      test.testCSIv2("UserB", "PassB");
      test.testCSIv2("UserC", "PassC");
   }
   
   public CSIv2Test()
   {
      // initialize the JNDI env that will be used to lookup the test EJB.
      this.env = new Hashtable<String, Object>();
      this.env.put("java.naming.factory.initial", "com.sun.jndi.cosnaming.CNCtxFactory");
      this.env.put("java.naming.provider.url", "corbaloc::localhost:3528/JBoss/Naming/root");
   }

   public void testCSIv2(String username, String password) throws Exception
   {
      // set the client security context.
      LoginContext loginContext = new LoginContext("csi", new SimpleCallbackHandler(username, password));
      loginContext.login();
      
      System.out.println("\nInvoking secure EJB2 session bean via IIOP with user " + username);
      Context context = new InitialContext(env);
      Object object = context.lookup("sample/SimpleEJB2Session");
      SimpleEJB2SessionHome home = (SimpleEJB2SessionHome) PortableRemoteObject.
         narrow(object, SimpleEJB2SessionHome.class);
      SimpleEJB2Session session = home.create();
      
      // invoke method that requires the Administrator role.
      try
      {
         Principal principal = session.invokeAdministrativeMethod();
         System.out.println("User " + principal.getName() + " successfully called administrative method!");
      }
      catch (AccessException ae)
      {
         System.out.println("User " + username + " is not authorized to call administrative method!");
      }
      
      // invoke method that requires the RegularUser role.
      try
      {
         Principal principal = session.invokeRegularMethod();
         System.out.println("User " + principal.getName() + " successfully called regular method!");
      }
      catch (AccessException ae)
      {
         System.out.println("User " + username + " is not authorized to call regular method!");
      }

      // invoke method that allows all roles.
      try
      {
         Principal principal = session.invokeUnprotectedMethod();
         System.out.println("User " + principal.getName() + " successfully called unprotected method!");
      }
      catch (AccessException ae)
      {
         // this should never happen as long as the user has successfully authenticated.
         System.out.println("User " + username + " is not authorized to call unprotected method!");
      }

      // invoke method that denies access to all roles.
      try
      {
         Principal principal = session.invokeUnavailableMethod();
         // this should never happen because the method should deny access to all roles.
         System.out.println("User " + principal.getName() + " successfully called unavailable method!");
      }
      catch (AccessException ae)
      {
         System.out.println("User " + username + " is not authorized to call unavailable method!");
      }
   }
   
   class SimpleCallbackHandler implements CallbackHandler
   {
      String username;
      char[] password;
      
      public SimpleCallbackHandler(String username, String password)
      {
         this.username = username;
         this.password = password.toCharArray();
      }
      
      @Override
      public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
      {
         for (Callback callback : callbacks)
         {
            if (callback instanceof NameCallback)
            {
               NameCallback nameCallback = (NameCallback) callback;
               nameCallback.setName(this.username);
            }
            else if (callback instanceof PasswordCallback)
            {
               PasswordCallback passCallback = (PasswordCallback) callback;
               passCallback.setPassword(this.password);
            }
            else
               throw new UnsupportedCallbackException(callback);
         }
      }
   }
}

 

The code itself is quite simple: first, a standard client-side JAAS login is performed to establish the client-side security context. Then, a JNDI lookup is performed to retrieve the sample EJB IOR from the CORBA Naming Server. This IOR is converted to a CORBA stub by the narrow() method and then this stub is used to invoke all methods on the remote bean.

 

When executed, however, the code above needs a couple of configuration files to work. First, we need to specify the login module that will perform the client-side login. Then we need to grant our code security permissions to download and run the remote IIOP stubs. Finally, we must specify the client-side CORBA interceptor that is going to build the CORBA security context that will be sent to the server.

Client-side Configuration Files

The first configuration file we are going to show is the client-side application policy that describes the login module that will perform the client-side login. We called this file appclientlogin.conf:

 

csi {
    org.jboss.security.ClientLoginModule required debug=false;
};

 

Nothing fancy here: just using the standard org.jboss.security.ClientLoginModule to create the client-side security context with the username and password supplied by the CallbackHandler.

 

The next configuration file just grants all permissions to our client code so it can download and run the IIOP stubs from the remote codebase specified in the IOR. We called this file client.policy:

 

grant {
        // Allow everything for now
        permission java.security.AllPermission;
};

 

Finally, we are going to need a jacorb.properties file to specify the SASClientInitializer. This ORB initializer installs the client-side interceptors that creates the CORBA security context. The CORBA security context contains the username and password that have been set by the ClientLoginModule and is propagated to the server-side ORB when a call is made on the CORBA stub.

 

###############################################################################
##                                                                           ##
##               JacORB configuration for the JBoss server                   ##
##                                                                           ##
###############################################################################

##################################
#                                #
#   ORB version number output    #
#                                #
##################################

# if on, the ORB's version number is printed whenever the ORB is initialized
jacorb.orb.print_version=off

##################################
#                                #
#   Debug output configuration   #
#                                #
##################################

# verbosity level
# 0 = fatal errors only = "almost off" (FATAL ERRORS)
# 1 = non-fatal errors and exceptions (ERROR)
# 2 = important messages (WARN)
# 3 = informational messages and exceptions (INFO)
# 4 = debug-level output (DEBUG) (may confuse the unaware user :-)
jacorb.config.log.verbosity=3
jacorb.log.default.verbosity=2
jacorb.orb.log.verbosity=3
jacorb.orb.factory.log.verbosity=3
jacorb.orb.interceptors.log.verbosity=3
jacorb.orb.basic.log.verbosity=3
jacorb.orb.giop.log.verbosity=2
jacorb.orb.iiop.log.verbosity=3
# POA and naming sevice verbosity settings appear later on

# where does output go? Terminal is default
#jacorb.logfile=LOGFILEPATH

# Whether to timestamp log entries
jacorb.log.timestamp=off

# If logging to file whether to append to existing file or overwrite
jacorb.logfile.append=off

# If jacorb.logfile.append is on, set rolling log size in kilobytes.
# A value of 0 implies no rolling log
jacorb.logfile.maxLogSize=0

# hexdump outgoing messages
jacorb.debug.dump_outgoing_messages=off

# hexdump incoming messages
jacorb.debug.dump_incoming_messages=off

##################################################
#                                                #
#    WARNING: The following properties should    #
#    only be edited by the expert user. They     #
#    can be left untouched in most cases!        #
#                                                #
##################################################

################################
#                              #
#   Basic ORB Configuration    #
#                              #
################################

# the GIOP minor version number to use for newly created IORs
jacorb.giop_minor_version=2

# number of retries if connection cannot directly be established
jacorb.retries=5

# how many msecs. do we wait between retries
jacorb.retry_interval=500

# size of network buffers for outgoing messages
jacorb.outbuf_size=2048

# log2 of maximum buffer size managed by the internal
# buffer manager.
#
# This is NOT the maximum buffer size that
# can be used, but just the largest size of buffers that
# will be kept and managed. This value will be added to
# an internal constant of 5, so the real value in bytes
# is 2**(5+maxManagedBufSize-1). You only need to increase this
# value if you are dealing with LOTS of LARGE data structures.
# You may decrease it to make the buffer manager release large
# buffers immediately rather than keeping them for later
# reuse.
jacorb.maxManagedBufSize=18
# If this value is 0 an extra unlimited size buffer cache is created
# for the CDROutputStreams. If this value is > 0 then the cache will
# be purged every x msecs.
jacorb.bufferManagerMaxFlush=-1

# Normally, a jacorb server will close the TCP/IP connection right
# after sending a CloseConnection message. However, it may
# occasionally happen that the client sends a message into the closed
# connection because it hasn't handled the CloseConnection yet. To
# avoid this situation, closing of the TCP/IP connection can be delayed.
#jacorb.connection.delay_close=on
#jacorb.connection.timeout_after_closeconnection=20000

# Wait the specified number of msecs for a reply to a request. If
# exceeded, a org.omg.CORBA.TIMEOUT exception will be thrown
#jacorb.client.pending_reply_timeout=0

# client-side connection idle timeout, set no non-zero to stop
# close the connection after so many msecs.
#jacorb.connection.client_idle_timeout=0

# max time (msecs) a server keeps a connection open if nothing happens
#jacorb.connection.server_timeout=10000

# Max no of accepted connections on the server.
#jacorb.connection.max_server_connections=

# The number of msecs that are waited until the next attempt to find
# an idle connection is made (i.e. do not continuously spin)
#jacorb.connection.wait_for_idle_interval=500

# The class name of the SelectionStrategy class
#jacorb.connection.selection_strategy_class=

# The class name of the StatisticsProvider class
#jacorb.connection.statistics_provider_class=

#jacorb.reference_caching=off

#
# The following property specifies the class which is used for
# reference caching. WeakHashtable uses WeakReferences, so entries
# get gc'ed if only the Hashtable has a reference to them. This
# is useful if you have many references to short-living non-persistent
# CORBA objects. It is only available for java 1.2 and above.
#
# On the other hand the standard Hashtable keeps the references until
# they are explicitely deleted by calling _release(). This is useful
# for persistent and long-living CORBA objects.
#
#jacorb.hashtable_class=org.jacorb.util.WeakHashtable
#
jacorb.hashtable_class=java.util.Hashtable

# use GIOP 1.2 byte order markers (since CORBA 2.4-5)
jacorb.use_bom=off

# add additional IIOP 1.0 profiles even if we are using IIOP 1.2
jacorb.giop.add_1_0_profiles=off

# Use DNS names in IORs
jacorb.dns.enable=off

# Compact Typecodes (0 - off, 1 - partial (not member_names), 2 - all)
jacorb.compactTypecodes=0

# Cache typecode on read
jacorb.cacheTypecodes=off

# Cache poa names
jacorb.cachePoaNames=off

###########################################
#                                         #
#         Interoperability                #
#                                         #
###########################################

# Turn off indirection encoding for repeated typecodes. This fixes
# interoperability with certain broken ORB's eg. Orbix2000
jacorb.interop.indirection_encoding_disable=off

# Iona Comet CORBA/COM bridge can incorrectly encode buffer lengths.
# Enabling this property adds additional length checking and adjustment
# for interoperability with Comet.
jacorb.interop.comet=off

# Some ORBs do not set a byte value of 1 as a CDR encoded boolean true
# value. Enabling this property interprets any non zero CDR encoded
# boolean value as true.
jacorb.interop.lax_boolean_encoding=off

# Control whether the method create_abstract_interface_tc performs
# a validity check on the name parameter or not. Turning this check
# off circumvents a bug in Sun's implementation of javax.rmi.CORBA.ValueHander,
# which occasionally passes an invalid name (an empty string) to
# ORBSingleton.create_abstract_interface_tc. If you are using RMI valuetypes,
# you should turn this property off. JBoss uses RMI (and RMI valuetypes), so
# you should NOT turned this property on!
jacorb.interop.strict_check_on_tc_creation=off

# Custom-marshalled RMI valuetypes should be encoded as chunks, but some
# ORBs are not able to decode chunked values. Disable this property for
# interoperability with the ORB in Sun's JDK 1.4.2.
jacorb.interop.chunk_custom_rmi_valuetypes=on

###########################################
#                                         #
#         Socket Factories                #
#                                         #
###########################################

# A factory design pattern is used for the creation of sockets and server
# sockets.
# The jacorb.net.socket_factory property can be used to configure
# a socket factory that must implement the operations defined in the
# interface org.jacorb.orb.factory.SocketFactory.
# The jacorb.net.server_socket_factory property can be used to configure a
# server socket factory that must implement the operations defined in the
# interface org.jacorb.orb.factory.ServerSocketFactory.
#
#jacorb.net.socket_factory=org.jacorb.orb.factory.DefaultSocketFactory
#jacorb.net.server_socket_factory=org.jacorb.orb.factory.DefaultServerSocketFactory
#jacorb.net.socket_factory=org.jacorb.orb.factory.PortRangeSocketFactory
#jacorb.net.server_socket_factory=org.jacorb.orb.factory.PortRangeServerSocketFactory
#
# Additional socket factores are supported that allow for the configuration
# of maximum and minimum port numbers that can be used. This can be used to
# enable firewall traversal via a fixed port range. To use these socket factories
# configure one or both of the following property pairs. The first property pair
# configures the client socket factory and the second pair the server socket
# factory.
#
#jacorb.net.socket_factory.port.min
#jacorb.net.socket_factory.port.max
#jacorb.net.server_socket_factory.port.min
#jacorb.net.server_socket_factory.port.max

###########################################
#                                         #
#         BiDirectional GIOP              #
#                                         #
###########################################

# uncomment this initializer if you want to use BiDirectional GIOP

#org.omg.PortableInterceptor.ORBInitializerClass.bidir_init=org.jacorb.orb.connection.BiDirConnectionInitializer


###########################################
#                                         #
#       Proxy address in IOR              #
#                                         #
###########################################

#
# with these two properties it is possible to
# tell the ORB what IP/port IORs should contain,
# if the ServerSockets IP/port can't be used
# (e.g. for traffic through a firewall).
#
# WARNING: this is just "dumb" replacing, so you
# have to take care of your configuration!
#

#jacorb.ior_proxy_host=1.2.3.4
#jacorb.ior_proxy_port=4711


###########################################
#                                         #
#   The Object Adapter Internet Address   #
#                                         #
###########################################

# IP address on multi-homed host (this gets encoded in
# object references). NOTE: Adresses like 127.0.0.X
# will only be accessible from the same machine!
#OAIAddr=1.2.3.4
#OAPort=4711

# This are the IIOP ports officially assigned to JBoss by IANA:
#OAPort=3528
#OASSLPort=3529

############################
#                          #
#   Default Interceptors   #
#   Please leave them in!  #
#                          #
############################
org.omg.PortableInterceptor.ORBInitializerClass.standard_init=org.jacorb.orb.standardInterceptors.IORInterceptorInitializer

###############################################
#                                             #
#   Implementation Repository Configuration   #
#                                             #
###############################################
# Switch off to avoid contacting the ImR on every server start-up
jacorb.use_imr=off

# the implementation name, should be set to a different 
# name in the code of persistent servers
jacorb.implname=JBoss

#########################
#                       #
#   SSL Configuration   #
#                       #
#########################

# qualified classname of access decision object
#jacorb.security.access_decision=org.jacorb.security.level2.AccessDecisionImpl

# list of qualified classnames of principal authenticator objects,
# separated by commas (no whitespaces!). The first entry (that can
# be successfully created) will be available through the
# principal_authenticator property.
#jacorb.security.principal_authenticator=org.jacorb.security.level2.SunJssePrincipalAuthenticatorImpl

# the qualified classname of the ssl socket factory class
#jacorb.ssl.socket_factory=org.jboss.iiop.jacorb.SSLSocketFactory

# the qualified classname of the ssl server socket factory class
#jacorb.ssl.server_socket_factory=org.jboss.iiop.jacorb.SSLServerSocketFactory

jacorb.ssl.socket_factory=org.jacorb.security.ssl.sun_jsse.SSLSocketFactory
jacorb.ssl.server_socket_factory=org.jacorb.security.ssl.sun_jsse.SSLServerSocketFactory
# change to "on" to enable SSL
jacorb.security.support_ssl=on

# Point to the jboss cts/conf/keystores/tomcat.keystore
jacorb.security.keystore=@CTS_CONF@/keystores/tomcat.keystore
jacorb.security.keystore_password=rmi+ssl
jacorb.security.jsse.trustees_from_ks=true
#jacorb.security.default_user=
#jacorb.security.default_password=

# IIOP/SSL parameters (numbers are hex values, without the leading "0x"):
# EstablishTrustInTarget = 20
# EstablishTrustInClient = 40
# mutual authentication = 60

jacorb.security.ssl.client.supported_options=60
jacorb.security.ssl.client.required_options=0

jacorb.security.ssl.server.supported_options=60
jacorb.security.ssl.server.required_options=0

org.omg.PortableInterceptor.ORBInitializerClass.org.jboss.iiop.csiv2.SASClientInitializer

#########################
#                       #
#   POA Configuration   #
#                       #
#########################

# displays a GUI monitoring tool for servers
jacorb.poa.monitoring=off

# POA log levels:
# 0 = fatal errors only = "almost off" (FATAL ERRORS)
# 1 = non-fatal errors and exceptions (ERROR)
# 2 = important messages (WARN)
# 3 = informational messages and exceptions (INFO)
# 4 = debug-level output (DEBUG) (may confuse the unaware user :-)
jacorb.poa.log.verbosity=2

# thread pool configuration for request processing
jacorb.poa.thread_pool_max=8
jacorb.poa.thread_pool_min=2

# if set, request processing threads in thePOA
# will run at this priority. If not set or invalid,
# MAX_PRIORITY will be used.
#jacorb.poa.thread_priority=

# Properties controlling the POA's request queue.  If queue_wait is off,
# then if there are more than queue_max requests in the queue, the
# client gets TRANSIENT exceptions for any requests.  If queue_wait is on,
# then the call blocks at the server side until no more than queue_min
# requests are left in the queue.  The new request is then delivered as usual.
jacorb.poa.queue_wait=off
jacorb.poa.queue_max=100
jacorb.poa.queue_min=10

##################################
#                                #
#   Name Service Configuration   #
#                                #
##################################

# log levels:
# 0 = fatal errors only = "almost off" (FATAL ERRORS)
# 1 = non-fatal errors and exceptions (ERROR)
# 2 = important messages (WARN)
# 3 = informational messages and exceptions (INFO)
# 4 = debug-level output (DEBUG) (may confuse the unaware user :-)

jacorb.naming.log.verbosity=3

# map objectKey "NameService" to "JBoss/Naming/root" to allow
# corbaname URLs that don't specify an objectKey,
# (and default to "NameService" for jacorb)
# to resolve correctly
jacorb.orb.objectKeyMap.NameService=JBoss/Naming/root

##################################
#                                #
#   Logger Factory Configuration #
#                                #
##################################

jacorb.log.loggerFactory=org.jboss.util.Log4jLoggerFactory

Running the Client Application

In order to run the client application we must set a few system properties. Those properties tell the JVM to use JacORB as the client-side ORB and also specify the policy and login-config files:

 

java -Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB -Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton
 -Djava.security.manager -Djava.security.policy=client.policy -Djava.security.auth.login.config=appclientlogin.conf
 -cp $JBOSS_HOME/client/jbossall-client.jar:$JBOSS_HOME/common/lib/jboss-iiop.jar:lib/simple-session.jar test.CSIv2Test

 

The classpath contains three jars: jbossall-client.jar (located in $JBOSS_HOME/client), jboss-iiop.jar (located in $JBOSS_HOME/common/lib), and the simple-session.jar file that contains our sample EJB classes.

 

When we run the client application we should see an output like this:

 

Invoking secure EJB2 session bean via IIOP with user UserA
14:35:51,941 WARN  [config] jacorb.home unset! Will use '.'
14:35:52,138 WARN  [config] jacorb.home unset! Will use '.'
User UserA successfully called administrative method!
User UserA successfully called regular method!
User UserA successfully called unprotected method!
User UserA is not authorized to call unavailable method!

Invoking secure EJB2 session bean via IIOP with user UserB
User UserB is not authorized to call administrative method!
User UserB successfully called regular method!
User UserB successfully called unprotected method!
User UserB is not authorized to call unavailable method!

Invoking secure EJB2 session bean via IIOP with user UserC
User UserC is not authorized to call administrative method!
User UserC is not authorized to call regular method!
User UserC successfully called unprotected method!
User UserC is not authorized to call unavailable method! 

 

As we can see from the output, the EJB security constraints have been enforced successfully for each user.

General Notes

 

NOTE 1: not all ORBs support dynamic retrieval of stubs. If this is the case of the ORB you are using, you must use a stub compilation tool to create the stubs from the EJB interfaces and include the generated stubs in the client application classpath.

 

NOTE 2: the simple-session.jar containing our sample EJB source code and configuration files can be found attached in this document. Just drop it in $JBOSS_HOME/server/all/deploy to have it deployed and exposed via IIOP.

 

NOTE 3: the csiv2-test project containing the client code and configuration files can also be found attached in this document. It is an Eclipse project, so all one has to do to run it is to open Eclipse, import the project, and change the JBOSS_HOME classpath variable to your JBoss AS instalation location. When running the client code remember to open the "Run Configurations" and add the following VM arguments:

-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB -Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton
-Djava.security.manager -Djava.security.policy=client.policy -Djava.security.auth.login.config=appclientlogin.conf

 

NOTE 4: the sample application has been tested on JBoss AS 6.0.0.M3, but it should work just fine on JBoss AS 5.1.0.GA. If using an earlier JBoss AS version you will have to specify the security domain in the $JBOSS_HOME/server/all/conf/login-config.xml file as the csiv2-sample-jboss-beans.xml won't be parsed by the old JMX microkernel.