This section will go into the details of the default JBoss security manager implementation to illustrate the interaction between the SecurityInterceptor and the security interfaces as well as usage of the JaasSecurityManager.
The default security implementation that JBoss comes preconfigured with consists of a JMX service MBean and a JAAS based implementation of the EJBSecurityManager and RealmMapping interfaces. The JMX bean is org.jboss.security.plugins.JaasSecurityManagerService and the security interfaces implementation is org.jboss.security.plugins.JaasSecurityManager. The JMX service MBean handles configurable aspects of the security manager and integrates the security manager into the JNDI namespace.
Even though there is a preconfigured security manager with the JBoss distribution, by default none of the EJB container configurations require security. The reason for this is that security may not be required and there is no way to provide a reasonable default setup. To secure your EJBs you must either change the default container configurations or specify the container configuration at deployment time. The standard container configuration is found in the conf/config-name/standardjboss.xml directory of the JBoss distribution tree. The “config-name” value is “default” by default. When specifying container configurations at deployment time you include a jboss.xml descriptor in your ear or ejb-jar META-INF directory. The key DTD elements for security configuration elements is given Figure 9.4
Figure 9.4. standardjboss.xml/jboss.xml Deployment Descriptor Security Configuration Elements
The JaasSecurityManagerService is a JMX MBean that handles the configuration of security. This includes the security manager implementation, the security proxy factory and the authentication cache policy.
org.jboss.security.plugins.JaasSecurityManagerService Attributes
This is the name of the class that implements the EJBSecurityMgr and RealmMapping interfaces that will be created on demand to fulfill the authentication and role mapping roles for SecurityInterceptors in a given security domain. If this attribute is not specified it defaults to the org.jboss.security.plugins.JaasSecurityManager.
This is the name of the class that implements the SecurityProxyFactory interface which is used to obtain the SecurityProxy interface used by the SecurityInterceptor for the delegation of custom security checks. If this attribute is not set it defaults to org.jboss.security.SubjectSecurityProxyFactory.
This is the JNDI name of the org.jboss.util.CachePolicy instance that is to be used for caching of authentication information. The cache is made available to the security manager class if it implements a setCachePolicy(CachePolicy) method.
Example 9.4. Example JaasSecurityManagerService MBean jboss.jcml Entry
<mbean code="org.jboss.security.plugins.JaasSecurityManagerService" name="Security:name=JaasSecurityManager"> <attribute name="SecurityManagerClassName"> org.jboss.security.plugins.JaasSecurityManager </attribute> <attribute name=“SecurityProxyFactoryClassName"> org.jboss.security.SubjectSecurityProxyFactory </attribute> <attribute name=“AuthenticationCacheJndiName"> srp/SRPAuthenticationCache </attribute> </mbean>
The JaasSecurityManagerService manages the association of security manager instances to container SecurityInterceptors by implementing the JNDI ObjectFactory interface and binding itself under java:/jaas in the JNDI namespace. This allows one to use a naming convention of the form java:/jaas/XYZ to access the security manager instance for the XYZ security domain. The security manager instance is created on the first lookup by creating an instance of the SecurityManagerClassName using a constructor that takes the name of the security domain. For example, consider the following container security configuration snippet:
Figure 9.5. Example Container Security Configuration Snippet
<jboss> <!-- Configure all containers to be secured under the "hades" security domain --> <security-domain>java:/jaas/hades</security-domain> ... </jboss>
Any lookup on the JBoss JNDI IntialContext of the name "java:/jaas/hades" will return a security manager instance that has been associated with the security domain named "hades". This security manager will implement the EJBSecurityMgr and RealmMapping security interfaces and will be of type SecurityManagerClassName.
As mentioned in the previous section, the JaasSecurityManager is the default choice for the security manager implementation class. It uses the Java Authentication and Authorization Service(JAAS) packages to implement its behavior. In particular, the behavior derives from the use of the login modules(javax.security.auth.spi.LoginModule) whose configuration entry name matches that of the security domain to which the JaasSecurityManager has been assigned. The login modules implement the principal authentication and role mapping behavior for the security domain. Hence, the JaasSecurityManager can be used across very different security domains simply by plugging in different login module configurations for the domains.
Consider a client that is invoking a method on an EJB that has been configured to use a JaasSecurityManager instance for security. Figure 9.6 gives a diagram of some the components involved in the access security check process.
The client first has to perform a login to establish the principal and credentials that will be used for authentication purposes. This entails creating a LoginContext and passing the name of the configuration to use. In the above figure the configuration name is "beanDomain". This is a one time process that associates the login principal and credentials with all subsequent EJB method invocations. Note that the login process may not actually authenticate the user. It depends on the login module configuration. In Figure 9.6 the beanDomain client side configuration is using the ClientLoginModule(org.jboss.security.ClientLoginModule) and this module does not perform client side authentication. It simply binds the username and password to the JBoss EJB invocation layer for later authentication on the server.
The client at some later point obtains the home interface for an EJB and attempts to create a bean. This results in a home interface method invocation that is sent to the JBoss server. The invocation includes the method arguments passed by the client along with the user identity and credentials.
The first step of the security check is to authenticate the user invoking the call. As on the client side, this involves invoking the login modules that are configured for the security domain the EJB is secured under. In the figure the EJB security domain is also named "beanDomain". If the user is authenticated, a JAAS Subject is created that contains the following in its PrincipalsSet:
A Group named "Roles" that contains the role names from the application domain that the user has been assigned. The role names are represented by org.jboss.security.SimplePrincipal objects. This is a simple String based implementation of Principal. These roles are used to validate the roles assigned to methods in the ejb-jar.xml descriptor as well as the EJBContext isCallerInRole(String) method.
An optional Group named "CallerPrincipal" that contains a single Principal that corresponds to the identity of the caller in the application domain. This is the value returned by the EJBContext getCallerPrincipal() method.
This usage pattern of the Subject Principals set is the standard usage that JBossSX expects of server side login modules. To ensure proper conformance to this pattern any custom login module you write should subclass the JBossSX AbstractServerLoginModule class. See the section called “Custom LoginModules” for details.
Although the login configuration names used by the client and server in this example happen to be the same, there really is no requirement for this as the client and server login module configurations are two indepdendent configurations. They are associated with each other only by the values for the principal and credentials that the are exchanged. The server side login modules have to be able to understand and validate the principal and credentials that the client side login modules set.
The final step in the security check is the authorization phase which consists of the following steps.
Obtain the names of the roles that are allowed to access the EJB method. This is obtained from the EJB container and consists of the ejb-jar.xml descriptor role-name elements of all method-permission elements containing the invoked method.
If no roles have been assigned then no access to the method is allowed. Otherwise, the JaasSecurityManager doesUserHaveRole(Princpal, Set) method is invoked to see if the caller principal has one of the role names assigned. This check is performed by iterating through the role names and checking to see if the "Roles" group contains a SimplePrincpal with the role name. Access is allowed if any role name is a member of the "Roles" group. Access is denied if the none of the role names are members of the "Roles" group.
If the EJB was configured with a custom security proxy, the method invocation is delegated to it. If the security proxy wants to deny access to the caller it will throw a SecurityException. If no SecurityException is thrown access to the EJB method is allowed and the SecurityInterceptor passes the method invocation to the next container interceptor.
There are two ways to write a security proxy for handling custom security that is outside the scope of the EJB declarative security model. The first is to write an implementation of the org.jboss.security.SecurityProxy interface allong with an implementation of the org.jboss.security.SecurityProxyFactory interface and setup your factory class as the SecurityProxyFactoryClassName value for the JaasSecurityManagerService. The other approach is to simply write an object that implements one or more of the EJB home and/or remote interface methods and code your security checks in each method. This section goes over each approach and highlights the advanatges and disadvantages to each approach.