CHAPTER 8 Security on JBoss - J2EE Security Configuration and Architecture

Security is a fundamental part of any enterprise application. You need to be able to restrict who is allowed to access your applications and control what operations application users may perform. The J2EE specifications define a simple role-based security model for EJBs and Web components. The JBoss component framework that handles security is the JBossSX extension framework. The JBossSX security extension provides support for both the role-based declarative J2EE security model as well as integration of custom security via a security proxy layer. The default implementation of the declarative security model is based on Java Authentication and Authorization Service (JAAS) login modules and subjects. The security proxy layer allows custom security that cannot be described using the declarative model to be added to an EJB in a way that is independent of the EJB business object. Before getting into the JBoss security implementation details, we will review the EJB 2.0, the Servlet 2.2 specification security model, and JAAS to establish the foundation for these details.

J2EE Declarative Security Overview

The security model advocated by the J2EE specification is a declarative model. It is declarative in that you describe the security roles and permissions using a standard XML descriptor rather than embedding security into your business component. This isolates security from business-level code because security tends to be a more a function of where the component is deployed, rather than an inherent aspect of the component's business logic. For example, consider an ATM component that is to be used to access a bank account. The security requirements, roles and permissions will vary independent of how one accesses the bank account based on what bank is managing the account, where the ATM machine is deployed, and so on.

Securing a J2EE application is based on the specification of the application security requirements via the standard J2EE deployment descriptors. You secure access to EJBs and Web components in an enterprise application by using the ejb-jar.xml and web.xml deployment descriptors. See A subset of the EJB 2.0 deployment descriptor content model that shows the security related elements.. and See A subset of the Servlet 2.2 deployment descriptor content model that shows the security related elements.. illustrate the security-related elements in the EJB 2.0 and Servlet 2.2 deployment descriptors, respectively.

 

FIGURE 8-1. A subset of the EJB 2.0 deployment descriptor content model that shows the security related elements.
FIGURE 8-2. A subset of the Servlet 2.2 deployment descriptor content model that shows the security related elements.

The purpose and usage of the various security elements given in See A subset of the EJB 2.0 deployment descriptor content model that shows the security related elements.. and See A subset of the Servlet 2.2 deployment descriptor content model that shows the security related elements.. is discussed in the following subsections.

Security References

Both EJBs and servlets may declare one or more security-role-ref elements. This element is used to declare that a component is using the role-name value as an argument to the isCallerInRole(String) method. Using the isCallerInRole method, a component can verify if the caller is in a role that has been declared with a security-role-ref/role-name element. The role-name element value must link to a security-role element through the role-link element. The typical use of isCallerInRole is to perform a security check that cannot be defined using the role based method-permissions elements. However, use of isCallerInRole is discouraged because this results in security logic embedded inside of the component code. Example descriptor fragments that illustrate the security-role-ref element usage are presented in Listing presented in See An example ejb-jar.xml and web.xml descriptor fragments which illustrate the security-role-ref element usage...

An example ejb-jar.xml and web.xml descriptor fragments which illustrate the security-role-ref element usage.

<!-- A sample ejb-jar.xml fragment -->

<ejb-jar>

<enterprise-beans>

<session>

<ejb-name>ASessionBean</ejb-name>

...

<security-role-ref>

<role-name>TheRoleICheck</role-name>

<role-link>TheApplicationRole</role-link>

</security-role-ref>

</session>

</enterprise-beans>

...

</ejb-jar>

<!-- A sample web.xml fragment -->

<web-app>

<servlet>

<servlet-name>AServlet</servlet-name>

...

<security-role-ref>

<role-name>TheServletRole</role-name>

<role-link>TheApplicationRole</role-link>

</security-role-ref>

</servlet>

...

</web-app>

Security Identity

EJBs can optionally declare a security-identity element. New to EJB 2.0 is the capability to specify what identity an EJB should use when it invokes methods on other components. The invocation identity can be that of the current caller, or a specific role. The application assembler uses the security-identity element with a use-caller-identity child element to indicate the current caller's identity should be propagated as the security identity for method invocations made by the EJB. Propagation of the caller's identity is the default used in the absence of an explicit security-identity element declaration.

Alternatively, the application assembler can use the run-as/role-name child element to specify that a specific security role given by the role-name value should be used as the security identity for method invocations made by the EJB. Note that this does not change the caller's identity as seen by EJBContext.getCallerPrincipal() . Rather, the caller's security roles are set to the single role specified by the run-as/role-name element value. One use case for the run-as element is to prevent external clients from accessing internal EJBs. This is accomplished by assigning the internal EJB method-permission elements that restrict access to a role never assigned to an external client. EJBs that need to use internal EJB are then configured with a run-as/role-name equal to the restricted role. An example descriptor fragment that illustrates security-identity element usage is presented in See An example ejb-jar.xml descriptor fragment which illustrates the security-identity element usage...

An example ejb-jar.xml descriptor fragment which illustrates the security-identity element usage.

<!-- A sample ejb-jar.xml fragment -->

<ejb-jar>

<enterprise-beans>

<session>

<ejb-name>ASessionBean</ejb-name>

...

<security-identity>

<use-caller-identity/>

</security-identity>

</session>

<session>

<ejb-name>RunAsBean</ejb-name>

...

<security-identity>

<run-as>

<description>A private internal role</description>

<role-name>InternalRole</role-name>

</run-as>

</security-identity>

</session>

</enterprise-beans>

...

</ejb-jar>

The same security identity capability has been introduced for servlets as of the J2EE 2.3 servlet specification but this capability is currently unsupported in JBoss 3.0.

Security roles

The security role name referenced by either the security-role-ref or security-identity element needs to map to one of the application's declared roles. An application assembler defines logical security roles by declaring security-role elements. The role-name value is a logical application role name like Administrator, Architect, SalesManager, etc.

What is a role? The J2EE specifications note that it is important to keep in mind that the security roles in the deployment descriptor are used to define the logical security view of an application. Roles defined in the J2EE deployment descriptors should not be confused with the user groups, users, principals, and other concepts that exist in the target enterprise's operational environment. The deployment descriptor roles are application constructs with application domain specific names. For example, a banking application might use role names like BankManager, Teller, and Customer.

In JBoss, a security-role is only used to map security-role-ref/role-name values to the logical role that the component role referenced. The user's assigned roles are a dynamic function of the application's security manager, as you will see when we discuss the JBossSX implementation details. JBoss does not require the definition of security-roles in order to declare method permissions. Therefore, the specification of security-role elements is simply a good practice to ensure portability across application servers and for deployment descriptor maintenance. Example descriptor fragments that illustrate security-role usage are presented in See An example ejb-jar.xml and web.xml descriptor fragments which illustrate the security-role element usage...

An example ejb-jar.xml and web.xml descriptor fragments which illustrate the security-role element usage.

<!-- A sample ejb-jar.xml fragment -->

<ejb-jar>

...

<assembly-descriptor>

<security-role>

<description>The single application role</description>

<role-name>TheApplicationRole</role-name>

</security-role>

</assembly-descriptor>

</ejb-jar>

<!-- A sample web.xml fragment -->

<web-app>

...

<security-role>

<description>The single application role</description>

<role-name>TheApplicationRole</role-name>

</security-role>

</web-app>

EJB method permissions

An application assembler can set the roles that are allowed to invoke an EJB's home and remote interface methods through method-permission element declarations. Each method-permission element contains one or more role-name child elements that define the logical roles allowed access the EJB methods as identified by method child elements. As of EJB 2.0, you can now specify an unchecked element instead of the role-name element to declare that any authenticated user can access the methods identified by method child elements. In addition, you can declare that no one should have access to a method with the exclude-list element. If an EJB has methods that have not been declared as accessible by a role using a method-permission element, the EJB methods default to being excluded from use. This is equivalent to defaulting the methods into the exclude-list.

There are three supported styles of method element declarations.

<method>

<ejb-name>EJBNAME</ejb-name>

<method-name>*</method-name>

</method>

<method>

<ejb-name>EJBNAME</ejb-name>

<method-name>METHOD</method-name>

</method>

<method>

<ejb-name>EJBNAME</ejb-name>

<method-name>METHOD</method-name>

<method-params>

<method-param>PARAMETER_1</method-param>

...

<method-param>PARAMETER_N</method-param>

</method-params>

</method>

The optional method-intf element can be used to differentiate methods with the same name and signature that are defined in both the home and remote interfaces of an enterprise bean.See An example ejb-jar.xml descriptor fragment which illustrates the method-permission element usage.. provides examples of the method-permission element usage.

An example ejb-jar.xml descriptor fragment which illustrates the method-permission element usage.

<ejb-jar>

<assembly-descriptor>

<method-permission>

<description>The employee and temp-employee roles may

access any method of the EmployeeService bean

</description>

<role-name>employee</role-name>

<role-name>temp-employee</role-name>

<method>

<ejb-name>EmployeeService</ejb-name>

<method-name>*</method-name>

</method>

</method-permission>

<method-permission>

<description>The employee role may access the

findByPrimaryKey, getEmployeeInfo, and the

updateEmployeeInfo(String) method of the AardvarkPayroll

bean

</description>

<role-name>employee</role-name>

<method>

<ejb-name>AardvarkPayroll</ejb-name>

<method-name>findByPrimaryKey</method-name>

</method>

<method>

<ejb-name>AardvarkPayroll</ejb-name>

<method-name>getEmployeeInfo</method-name>

</method>

<method>

<ejb-name>AardvarkPayroll</ejb-name>

<method-name>updateEmployeeInfo</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

</method>

</method-permission>

<method-permission>

<description>The admin role may access any method

of the EmployeeServiceAdmin bean

</description>

<role-name>admin</role-name>

<method>

<ejb-name>EmployeeServiceAdmin</ejb-name>

<method-name>*</method-name>

</method>

</method-permission>

<method-permission>

<description>Any authenticated user may access any method

of the EmployeeServiceHelp bean

</description>

<unchecked/>

<method>

<ejb-name>EmployeeServiceHelp</ejb-name>

<method-name>*</method-name>

</method>

</method-permission>

<exclude-list>

<description>No fireTheCTO methods of the EmployeeFiring

bean may be used in this deployment

</description>

<method>

<ejb-name>EmployeeFiring</ejb-name>

<method-name>fireTheCTO</method-name>

</method>

</exclude-list>

</assembly-descriptor>

</ejb-jar>

Web Content Security Constraints

In a Web application, security is defined by the roles allowed access to content by a URL pattern that identifies the protected content. This set of information is declared using the web.xml security-constraint element. The content to be secured is declared using one or more web-resource-collection elements. Each web-resource-collection element contains an optional series of url-pattern elements followed by an optional series of http-method elements. The url-pattern element value specifies a URL pattern against which a request URL must match for the request to correspond to an attempt to access secured content. The http-method element value specifies a type of HTTP request to allow.

The optional user-data-constraint element specifies the requirements for the transport layer of the client to server connection. The requirement may be for content integrity (preventing data tampering in the communication process) or for confidentiality (preventing reading while in transit). The transport-guarantee element value specifies the degree to which communication between client and server should be protected. Its values are NONE, INTEGRAL, or CONFIDENTIAL. A value of NONE means that the application does not require any transport guarantees. A value of INTEGRAL means that the application requires the data sent between the client and server be sent in such a way that it can't be changed in transit. A value of CONFIDENTIAL means that the application requires the data be transmitted in a fashion that prevents other entities from observing the contents of the transmission. In most cases, the presence of the INTEGRAL or CONFIDENTIAL flag indicates that the use of SSL is required.

The optional login-config is used to configure the authentication method that should be used, the realm name that should be used for this application, and the attributes that are needed by the form login mechanism. The auth-method child element specifies the authentication mechanism for the Web application. As a prerequisite to gaining access to any Web resources that are protected by an authorization constraint, a user must have authenticated using the configured mechanism. Legal values for auth-method are BASIC, DIGEST, FORM, or CLIENT-CERT. The realm-name child element specifies the realm name to use in HTTP BASIC and DIGEST authorization. The form-login-config child element specifies the log in as well as error pages that should be used in form-based login. If the auth-method value is not FORM, form-login-config and its child elements are ignored.

As an example, the web.xml descriptor fragment given in See A web.xml descriptor fragment which illustrates the use of the security-constraint and related elements.. indicates that any URL lying under the web application "/restricted" path requires an AuthorizedUser role. There is no required transport guarantee and the authentication method used for obtaining the user identity is BASIC HTTP authentication.

A web.xml descriptor fragment which illustrates the use of the security-constraint and related elements.

<web-app>

...

<security-constraint>

<web-resource-collection>

<web-resource-name>Secure Content</web-resource-name>

<url-pattern>/restricted/*</ url-pattern></

<web-resource-collection>

<auth-constraint>

<role-name>AuthorizedUser</role-name>

</auth-constraint>

<user-data-constraint>

<transport-guarantee>NONE</transport-guarantee>

</user-data-constraint>

</security-constraint>

...

<login-config>

<auth-method>BASIC</auth-method>

<realm-name>The Restricted Zone</realm-name>

</login-config>

...

<security-role>

<description>The role required to access restricted content

</description>

<role-name>AuthorizedUser</role-name>

</security-role>

</web-app>

Enabling Declarative Security in JBoss

The J2EE security elements that have been covered describe only the security requirements from the application's perspective. Since J2EE security elements declare logical roles, the application deployer maps the roles from the application domain onto the deployment environment. The J2EE specifications omit these application-server-specific details. In JBoss, mapping the application roles onto the deployment environment entails specifying a security manager that implements the J2EE security model using JBoss server specific deployment descriptors. We will avoid discussion the details of this step for now. The details behind the security configuration will be discussed when we describe the generic JBoss server security interfaces in the section The JBoss Security Model .

An Introduction to JAAS

The default implementation of the JBossSX framework is based on the JAAS API. Because this is a relatively new API, one which has not seen wide spread use, its important that you understand the basic elements of the JAAS API to understand the implementation details of JBossSX. This section provides an introduction to JAAS to prepare you for the JBossSX architecture discussion.

Additional details on the JAAS package can be found at the JAAS home page at: http://java.sun.com/products/jaas/ .

What is JAAS?

The JAAS 1.0 API consists of a set of Java packages designed for user authentication and authorization. It implements a Java version of the standard Pluggable Authentication Module (PAM) framework and compatibly extends the Java 2 Platform's access control architecture to support user-based authorization. JAAS was first released as an extension package for JDK 1.3 and is bundled with JDK 1.4+. Because the JBossSX framework uses only the authentication capabilities of JAAS to implement the declarative role-based J2EE security model, this introduction focuses on only that topic.

Much of this section's material is derived from the JAAS 1.0 Developers Guide, so if you are familiar with its content you can skip ahead to the JBossSX architecture discussion section The JBoss Security Extension Architecture

JAAS authentication is performed in a pluggable fashion. This permits Java applications to remain independent from underlying authentication technologies and allows the JBossSX security manager to work in different security infrastructures. Integration with a security infrastructure can be achieved without changing the JBossSX security manager implementation. All that needs to change is the configuration of the authentication stack that JAAS uses.

The JAAS Core Classes

The JAAS core classes can be broken down into three categories: common, authentication, and authorization. The following list presents only the common and authentication classes because these are the specific classes used to implement the functionality of JBossSX covered in this chapter.

Common classes:

  • Subject ( javax.security.auth.Subject )
  • Principal ( java.security.Principal )

Authentication classes:

  • Callback ( javax.security.auth.callback.Callback )
  • CallbackHandler ( javax.security.auth.callback.CallbackHandler )
  • Configuration ( javax.security.auth.login.Configuration )
  • LoginContext ( javax.security.auth.login.LoginContext )
  • LoginModule ( javax.security.auth.spi.LoginModule )
Subject and Principal

To authorize access to resources, applications first need to authenticate the request's source. The JAAS framework defines the term subject to represent a request's source. The Subject class is the central class in JAAS. A Subject represents information for a single entity, such as a person or service. It encompasses the entity's principals, public credentials, and private credentials. The JAAS APIs use the existing Java 2 java.security.Principal interface to represent a principal, which is essentially just a typed name.

During the authentication process, a Subject is populated with associated identities, or Principal s. A Subject may have many Principal s. For example, a person may have a name Principal (John Doe) and a social security number Principal (123-45-6789), and a username Principal (johnd), all of which help distinguish the Subject from other Subject s. To retrieve the Principal s associated with a Subject , two methods are available:

{

...

public Set getPrincipals() {...}

public Set getPrincipals(Class c) {...}

}

The first method returns all Principal s contained in the Subject . The second method only returns those Principal s that are instances of Class c or one of its subclasses. An empty set will be returned if the Subject has no matching Principal s. Note that the java.security.acl.Group interface is a subinterface of java.security.Principal , and so an instance in the principals set may represent a logical grouping of other principals or groups of principals.

Authentication of a Subject

Authentication of a Subject requires a JAAS login. The login procedure consists of the following steps:

  1. An application instantiates a LoginContext passing in the name of the login configuration and a CallbackHandler to populate the Callback objects as required by the configuration LoginModule s.
  2. The LoginContext consults a Configuration to load all of the LoginModules included in the named login configuration. If no such named configuration exists the "other" configuration is used as a default.
  3. The application invokes the LoginContext.login method.
  4. The login method invokes all the loaded LoginModule s. As each LoginModule attempts to authenticate the Subject , it invokes the handle method on the associated CallbackHandler to obtain the information required for the authentication process. The required information is passed to the handle method in the form of an array of Callback objects. Upon success, the LoginModule s associate relevant Principal s and credentials with the Subject .
  5. The LoginContext returns the authentication status to the application. Success is represented by a return from the login method. Failure is represented through a LoginException being thrown by the login method.
  6. If authentication succeeds, the application retrieves the authenticated Subject using the LoginContext.getSubject method.
  7. After the scope of the Subject authentication is complete, all Principal s and related information associated with the Subject by the login method may be removed by invoking the LoginContext.logout method.

The LoginContext class provides the basic methods for authenticating Subject s and offers a way to develop an application independent of the underlying authentication technology. The LoginContext consults a Configuration to determine the authentication services configured for a particular application. LoginModule s classes represent the authentication services. Therefore, you can plug in different LoginModule s into an application without changing the application itself. See An illustration of the steps of the authentication process from the application perspective.. provides code fragments that illustrate the steps required by an application to authenticate a Subject.

An illustration of the steps of the authentication process from the application perspective.

CallbackHandler handler = new MyHandler();

LoginContext lc = new LoginContext("some-config", handler);

try

{

lc.login();

Subject subject = lc.getSubject();

}

catch(LoginException e)

{

System.out.println("authentication failed");

e.printStackTrace();

}

// Perform work as authenticated Subject

...

// Scope of work complete, logout to remove authentication info

try

{

lc.logout();

}

catch(LoginException e)

{

System.out.println("logout failed");

e.printStackTrace();

}

// A sample MyHandler class

class MyHandler implements CallbackHandler

{

public void handle(Callback[] callbacks) throws

IOException, UnsupportedCallbackException

{

for (int i = 0; i < callbacks.length; i++)

{

if (callbacks[i] instanceof NameCallback)

{

NameCallback nc = (NameCallback)callbacks[i];

nc.setName(username);

}

else if (callbacks[i] instanceof PasswordCallback)

{

PasswordCallback pc = (PasswordCallback)callbacks[i];

pc.setPassword(password);

}

else

{

throw new UnsupportedCallbackException(callbacks[i],

"Unrecognized Callback");

}

}

}

}

Developers integrate with an authentication technology by creating an implementation of the LoginModule interface. This allows different authentication technologies to be plugged into an application by administrator. Multiple LoginModule s can be chained together to allow for more than one authentication technology as part of the authentication process. For example, one LoginModule may perform username/password-based authentication, while another may interface to hardware devices such as smart card readers or biometric authenticators. The life cycle of a LoginModule is driven by the LoginContext object against which the client creates and issues the login method. The process consists of a two phases. The steps of the process are as follows:

  1. The LoginContext creates each configured LoginModule using its public no-arg constructor.
  2. Each LoginModule is initialized with a call to its initialize method. The Subject argument is guaranteed to be non-null. The signature of the initialize method is:
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options);
  3. The login method is then called to start the authentication process. An example method implementation might prompt the user for a username and password, and then verify the information against data stored in a naming service such as NIS or LDAP. Alternative implementations might interface to smart cards and biometric devices, or simply extract user information from the underlying operating system. The validation of user identity by each LoginModule is considered phase 1 of JAAS authentication. The signature of the login method is:
    boolean login() throws LoginException;
    Failure is indicated by throwing a LoginException . A return of true indicates that the method succeeded, while a return of false indicates that the login module should be ignored.
  4. If the LoginContext 's overall authentication succeeds, commit is invoked on each LoginModule . If phase 1 succeeded for a LoginModule , then the commit method continues with phase 2: associating relevant Principal s, public credentials, and/or private credentials with the Subject . If phase 1 fails for a LoginModule , then commit removes any previously stored authentication state, such as usernames or passwords. The signature of the commit method is:
    boolean commit() throws LoginException;
    Failure to complete the commit phase is indicated by throwing a LoginException. A return of true indicates that the method succeeded, while a return of false indicates that the login module should be ignored.
  5. If the LoginContext 's overall authentication failed, then the abort method is invoked on each LoginModule . The abort method removes/destroys any authentication state created by the login or initialize methods. The signature of the abort method is:
    boolean abort() throws LoginException;
    Failure to complete the abort phase is indicated by throwing a LoginException. A return of true indicates that the method succeeded, while a return of false indicates that the login module should be ignored.
  6. Removal of the authentication state after a successful login is accomplished when the application invokes logout on the LoginContext . This in turn results in a logout method invocation on each LoginModule . The logout method removes the Principal s and credentials originally associated with the Subject during the commit operation. Credentials should be destroyed upon removal. The signature of the logout method is:
    boolean logout() throws LoginException;
    Failure to complete the logout process is indicated by throwing a LoginException. A return of true indicates that the method succeeded, while a return of false indicates that the login module should be ignored.

When a LoginModule must communicate with the user to obtain authentication information, it uses a CallbackHandler object. Applications implement the CallbackHandler interface and pass it to the LoginContext , which forwards it directly to the underlying LoginModule s. LoginModules use the CallbackHandler both to gather input from users, such as a password or smart-card PIN number, and to supply information to users, such as status information. By allowing the application to specify the CallbackHandler , underlying LoginModule s remain independent from the different ways applications interact with users. For example, a CallbackHandler 's implementation for a GUI application might display a window to solicit user input. On the other hand, a CallbackHandler 's implementation for a non-GUI environment, such as an application server, might simply obtain credential information using an application server API. The CallbackHandler interface has one method to implement:

void handle(Callback[] callbacks)

throws java.io.IOException, UnsupportedCallbackException;

The last authentication class to cover is the Callback interface. This is a tagging interface for which several default implementations are provided, including NameCallback and PasswordCallback that were used in See An illustration of the steps of the authentication process from the application perspective... LoginModule s use a Callback to request information required by the authentication mechanism the LoginModule encapsulates. LoginModule s pass an array of Callback s directly to the CallbackHandler.handle method during the authentication's login phase. If a CallbackHandler does not understand how to use a Callback object passed into the handle method, it throws an UnsupportedCallbackException to abort the login call.

The JBoss Security Model

Similar to the rest of the JBoss architecture, security at the lowest level is defined as a set of interfaces for which alternate implementations may be provided. There are three basic interfaces that define the JBoss server security layer: org.jboss.security.AuthenticationManager , org.jboss.security.RealmMapping , and org.jboss.security.SecurityProxy . See The key security model interfaces and their relationship to the JBoss server EJB container elements.. shows a class diagram of the security interfaces and their relationship to the EJB container architecture.

 
FIGURE 8-3. The key security model interfaces and their relationship to the JBoss server EJB container elements.

The light blue classes represent the security interfaces while the yellow classes represent the EJB container layer. The two interfaces required for the implementation of the J2EE security model are the org.jboss.security.AuthenticationManager and org.jboss.security.RealmMapping . The roles of the security interfaces presented in See The key security model interfaces and their relationship to the JBoss server EJB container elements.. are summarized in the following list.

Note that the AuthenticationManager , RealmMapping and SecurityProxy interfaces have no association to JAAS related classes. Although the JBossSX framework is heavily dependent on JAAS, the basic security interfaces required for implementation of the J2EE security model are not. The JBossSX framework is simply an implementation of the basic security plug-in interfaces that are based on JAAS. The component diagram presented in Figure 8.4 illustrates this fact. The implication of this plug-in architecture is that you are free to replace the JAAS-based JBossSX implementation classes with your own custom security manager implementation that does not make use of JAAS, if you so desire. You'll see how to do this when you look at the JBossSX MBeans available for the configuration of JBossSX in See The relationship between the JBossSX framework implementation classes and the JBoss server EJB container layer...

 

FIGURE 8-4. The relationship between the JBossSX framework implementation classes and the JBoss server EJB container layer.

Enabling Declarative Security in JBoss Revisited

Recall that our discussion of the J2EE standard security model ended with a requirement for the use of JBoss server specific deployment descriptor to enable security. The details of this configuration is presented here, as this is part of the generic JBoss security model. See The security element subsets of the JBoss server jboss.xml and jboss-web.xml deployment descriptors.. shows the JBoss-specific EJB and Web application deployment descriptor's security-related elements.

FIGURE 8-5. The security element subsets of the JBoss server jboss.xml and jboss-web.xml deployment descriptors.

The value of a security-domain element specifies the JNDI name of the security manager interface implementation that JBoss uses for the EJB and Web containers. This is an object that implements both of the AuthenticationManager and RealmMapping interfaces. When specified as a top-level element it defines what security domain in effect for all EJBs in the deployment unit. This is the typical usage because mixing security managers within a deployment unit complicates inter-component operation and administration.

To specify the security-domain for an individual EJB, you specify the security-domain at the container configuration level. This will override any top-level security-domain element..

The unauthenticated-principal element specifies the name to use for the Principal object returned by the EJBContext.getUserPrincpal method when an unauthenticated user invokes an EJB. Note that this conveys no special permissions to an unauthenticated caller. Its primary purpose is to allow unsecured servlets and JSP pages to invoke unsecured EJBs and allow the target EJB to obtain a non-null Principal for the caller using the getUserPrincipal method. This is a J2EE specification requirement.

The security-proxy element identifies a custom security proxy implementation that allows per-request security checks outside the scope of the EJB declarative security model without embedding security logic into the EJB implementation. This may be an implementation of the org.jboss.security.SecurityProxy interface, or just an object that implements methods in the home, remote, local home or local interfaces of the EJB to secure without implementing any common interface. If the given class does not implement the SecurityProxy interface, the instance must be wrapped in a SecurityProxy implementation that delegates the method invocations to the object. The org.jboss.security.SubjectSecurityProxy is an example SecurityProxy implementation used by the default JBossSX installation.

Take a look at a simple example of a custom SecurityProxy in the context of a trivial stateless session bean. The custom SecurityProxy validates that no one invokes the bean's echo method with a four-letter word as its argument. This is a check that is not possible with role-based security; you cannot define a FourLetterEchoInvoker role because the security context is the method argument, not a property of the caller. The code for the custom SecurityProxy is given in See The example 1 custom EchoSecurityProxy implementation that enforces the echo argument-based security constraint.., and the full source code is available in the src/main/org/jboss/chap8/ex1 directory of the book examples. The associated jboss.xml descriptor that installs the EchoSecurityProxy as the custom proxy for the EchoBean is given in See The jboss.xml descriptor which configures the EchoSecurityProxy as the custom security proxy for the EchoBean...

The example 1 custom EchoSecurityProxy implementation that enforces the echo argument-based security constraint.

package org.jboss.chap8.ex1;

import java.lang.reflect.Method;

import javax.ejb.EJBContext;

import org.apache.log4j.Category;

import org.jboss.security.SecurityProxy;

/** A simple example of a custom SecurityProxy implementation

that demonstrates method argument based security checks.

* @author Scott.Stark@jboss.org

* @version $Revision:$

*/

public class EchoSecurityProxy implements SecurityProxy

{

Category log = Category.getInstance(EchoSecurityProxy.class);

Method echo;

public void init(Class beanHome, Class beanRemote,

Object securityMgr)

throws InstantiationException

{

log.debug("init, beanHome="+beanHome

+ ", beanRemote="+beanRemote

+ ", securityMgr="+securityMgr);

// Get the echo method for equality testing in invoke

try

{

Class[] params = {String.class};

echo = beanRemote.getDeclaredMethod("echo", params);

}

catch(Exception e)

{

String msg = "Failed to finde an echo(String) method";

log.error(msg, e);

throw new InstantiationException(msg);

}

}

public void setEJBContext(EJBContext ctx)

{

log.debug("setEJBContext, ctx="+ctx);

}

public void invokeHome(Method m, Object[] args)

throws SecurityException

{

// We don't validate access to home methods

}

public void invoke(Method m, Object[] args, Object bean)

throws SecurityException

{

log.debug("invoke, m="+m);

// Check for the echo method

if( m.equals(echo) )

{

// Validate that the msg arg is not 4 letter word

String arg = (String) args[0];

if( arg == null || arg.length() == 4 )

throw new SecurityException("No 4 letter words");

}

// We are not responsible for doing the invoke

}

}

The jboss.xml descriptor which configures the EchoSecurityProxy as the custom security proxy for the EchoBean.

<jboss>

<security-domain>java:/jaas/other</security-domain>

<enterprise-beans>

<session>

<ejb-name>EchoBean</ejb-name>

<security-proxy>org.jboss.chap8.ex1.EchoSecurityProxy

</security-proxy>

</session>

</enterprise-beans>

</jboss>

The EchoSecurityProxy checks that the method to be invoked on the bean instance corresponds to the echo(String) method loaded the init method. If there is a match, the method argument is obtained and its length compared against 4 or null. Either case results in a SecurityException being thrown. Certainly this is a contrived example, but only in its application. It is a common requirement that applications must perform security checks based on the value of method arguments. The point of the example is to demonstrate how custom security beyond the scope of the standard declarative security model can be introduced independent of the bean implementation. This allows the specification and coding of the security requirements to be delegated to security experts. Since the security proxy layer can be done independent of the bean implementation, security can be changed to match the deployment environment requirements.

Now test the custom proxy by running a client that attempts to invoke the EchoBean.echo method with the arguments "Hello" and "Four" as illustrated in this fragment:

public class ExClient

{

public static void main(String args[]) throws Exception

{

Logger log = Logger.getLogger("ExClient");

log.info("Looking up EchoBean");

InitialContext iniCtx = new InitialContext();

Object ref = iniCtx.lookup("EchoBean");

EchoHome home = (EchoHome) ref;

Echo echo = home.create();

log.info("Created Echo");

log.info("Echo.echo('Hello') = "+echo.echo("Hello"));

log.info("Echo.echo('Four') = "+echo.echo("Four"));

}

}

The first call should succeed, while the second should fail due to the fact that "Four" is a four-letter word. Run the client as follows using Ant from the examples directory:

[nr@toki examples]$ ant -Dchap=chap8 -Dex=1 run-example

run-example1:

[copy] Copying 1 file to /tmp/jboss-3.2.3/server/default/deploy

[echo] Waiting for 5 seconds for deploy...

[java] [INFO,ExClient] Looking up EchoBean

[java] [INFO,ExClient] Created Echo

[java] [INFO,ExClient] Echo.echo('Hello') = Hello

[java] Exception in thread "main" java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:

[java] java.rmi.ServerException: RuntimeException; nested exception is:

[java] java.lang.SecurityException: No 4 letter words

...

[java] at org.jboss.chap8.ex1.ExClient.main(ExClient.java:23)

[java] Caused by: java.rmi.ServerException: RuntimeException; nested exception is:

[java] java.lang.SecurityException: No 4 letter words

...

[java] Exception in thread "main"

[java] Java Result: 1

The result is that the echo('Hello') method call succeeds as expected and the echo('Four') method call results in a rather messy looking exception, which is also expected. The above output has been truncated to fit in the book. The key part to the exception is that the SecurityException("No 4 letter words") generated by the EchoSecurityProxy was thrown to abort the attempted method invocation as desired.

The JBoss Security Extension Architecture

The preceding discussion of the general JBoss security layer has stated that the JBossSX security extension framework is an implementation of the security layer interfaces. This is the primary purpose of the JBossSX framework. The details of the implementation are interesting in that it offers a great deal of customization for integration into existing security infrastructures. A security infrastructure can be anything from a database or LDAP server to a sophisticated security software suite. The integration flexibility is achieved using the pluggable authentication model available in the JAAS framework.

The heart of the JBossSX framework is org.jboss.security.plugins.JaasSecurityManager . This is the default implementation of the AuthenticationManager and RealmMapping interfaces. See The relationship between the security-domain component deployment descriptor value, the component container and the JaasSecurityManager.. shows how the JaasSecurityManager integrates into the EJB and Web container layers based on the security-domain element of the corresponding component deployment descriptor.

FIGURE 8-6. The relationship between the security-domain component deployment descriptor value, the component container and the JaasSecurityManager.

See The relationship between the security-domain component deployment descriptor value, the component container and the JaasSecurityManager.. depicts an enterprise application that contains both EJBs and Web content secured under the security domain jwdomain. The EJB and Web containers have a request interceptor architecture that includes a security interceptor, which enforces the container security model. At deployment time, the security-domain element value in the jboss.xml and jboss-web.xml descriptors is used to obtain the security manager instance associated with the container. The security interceptor then uses the security manager to perform its role. When a secured component is requested, the security interceptor delegates security checks to the security manager instance associated with the container.

The JBossSX JaasSecurityManager implementation, shown in See The relationship between the security-domain component deployment descriptor value, the component container and the JaasSecurityManager.. as the JaasSecurityMgr component, performs security checks based on the information associated with the Subject instance that results from executing the JAAS login modules configured under the name matching the security-domain element value. We will drill into the JaasSecurityManager implementation and its use of JAAS in the following section.

How the JaasSecurityManager Uses JAAS

The JaasSecurityManager uses the JAAS packages to implement the AuthenticationManager and RealmMapping interface behavior. In particular, its behavior derives from the execution of the login module instances that are configured under the name that matches the security domain to which the JaasSecurityManager has been assigned. The login modules implement the security domain's principal authentication and role-mapping behavior. Thus, you can use the JaasSecurityManager across different security domains simply by plugging in different login module configurations for the domains.

To illustrate the details of the JaasSecurityManager 's usage of the JAAS authentication process, you will walk through a client invocation of an EJB home method invocation. The prerequisite setting is that the EJB has been deployed in the JBoss server and its home interface methods have been secured using method-permission elements in the ejb-jar.xml descriptor, and it has been assigned a security domain named "jwdomain" using the jboss.xml descriptor security-domain element.

FIGURE 8-7. An illustration of the steps involved in the authentication and authorization of a secured EJB home method invocation.

See An illustration of the steps involved in the authentication and authorization of a secured EJB home method invocation.. provides a view of the client to server communication we will discuss. The numbered steps shown in See An illustration of the steps involved in the authentication and authorization of a secured EJB home method invocation.. are:

  1. The client first has to perform a JAAS login to establish the principal and credentials for authentication, and this is labeled Client Side Login in See An illustration of the steps involved in the authentication and authorization of a secured EJB home method invocation... This is how clients establish their login identities in JBoss. Support for presenting the login information via JNDI InitialContext properties is provided via an alternate configuration as described in The Login InitialContext Factory Implementation. A JAAS login entails creating a LoginContext instance and passing the name of the configuration to use. In Figure 8.7, the configuration name is "other". This one-time login associates the login principal and credentials with all subsequent EJB method invocations. Note that the process might not authenticate the user. The nature of the client-side login depends on the login module configuration that the client uses. In Figure 8.7, the "other" client-side login configuration entry is set up to use the ClientLoginModule module (an org.jboss.security.ClientLoginModule ). This is the default client side module that simply binds the username and password to the JBoss EJB invocation layer for later authentication on the server. The identity of the client is not authenticated on the client.
  2. Later, the client obtains the EJB home interface and attempts to create a bean. This event is labeled as Home Method Invocation in See An illustration of the steps involved in the authentication and authorization of a secured EJB home method invocation.. . This results in a home interface method invocation being sent to the JBoss server. The invocation includes the method arguments passed by the client along with the user identity and credentials from the client-side JAAS login performed in step 1.
  3. On the server side, the security interceptor first requires authentication of the user invoking the call, which, as on the client side, involves a JAAS login.
  4. The security domain under which the EJB is secured determines the choice of login modules. The security domain name is used as the login configuration entry name passed to the LoginContext constructor. In See An illustration of the steps involved in the authentication and authorization of a secured EJB home method invocation.., the EJB security domain is "jwdomain". If the JAAS login authenticates the user, a JAAS Subject is created that contains the following in its PrincipalsSet:

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 or one of its subclasses, or at least follow the pattern as documented in the custom login module section Writing Custom Login Modules

  • A java.security.Principal that corresponds to the client identity as known in the deployment security environment.
  • A java.security.acl.Group named "Roles" that contains the role names from the application domain to which the user has been assigned. org.jboss.security.SimplePrincipal objects are used to represent the role names; SimplePrincipal is a simple string-based implementation of Principal . These roles are used to validate the roles assigned to methods in ejb-jar.xml and the EJBContext.isCallerInRole(String) method implementation.
  • An optional java.security.acl.Group named "CallerPrincipal", which contains a single org.jboss.security.SimplePrincipal that corresponds to the identity of the application domain's caller. The "CallerPrincipal" sole group member will be the value returned by the EJBContext.getCallerPrincipal() method. The purpose of this mapping is to allow a Principal as known in the operational security environment to map to a Principal with a name known to the application. In the absence of a "CallerPrincipal" mapping the deployment security environment principal is used as the getCallerPrincipal method value. That is, the operational principal is the same as the application domain principal.
  • The final step of the security interceptor check is to verify that the authenticated user has permission to invoke the requested method This is labeled as Server Side Authorization in 8-7. Performing the authorization this entails the following steps:
  • Obtain the names of the roles allowed to access the EJB method from the EJB container. The role names are determined by ejb-jar.xml descriptor role-name elements of all method-permission elements containing the invoked method.
  • If no roles have been assigned, or the method is specified in an exclude-list element, then access to the method is denied. Otherwise, the JaasSecurityManager.doesUserHaveRole(Principal, Set) method is invoked by the security interceptor to see if the caller has one of the assigned role names. The doesUserHaveRole method implementation iterates through the role names and checks if the authenticated user's Subject "Roles" group contains a SimplePrincipal with the assigned role name. Access is allowed if any role name is a member of the "Roles" group. Access is denied if none of the role names are members.
  • 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 java.lang.SecurityException . If no SecurityException is thrown, access to the EJB method is allowed and the method invocation passes to the next container interceptor. Note that the SecurityProxyInterceptor handles this check and this interceptor is not shown in See An illustration of the steps involved in the authentication and authorization of a secured EJB home method invocation...

Every secured EJB method invocation, or secured Web content access, requires the authentication and authorization of the caller because security information is handled as a stateless attribute of the request that must be presented and validated on each request. This can be an expensive operation if the JAAS login involves client-to-server communication. Because of this, the JaasSecurityManager supports the notion of an authentication cache that is used to store principal and credential information from previous successful logins. You can specify the authentication cache instance to use as part of the JaasSecurityManager configuration as you will see when the associated MBean service is discussed in following section. In the absence of any user-defined cache, a default cache that maintains credential information for a configurable period of time is used.

The JaasSecurityManagerService MBean

The JaasSecurityManagerService MBean service manages security managers. Although its name begins with Jaas, the security managers it handles need not use JAAS in their implementation. The name arose from the fact that the default security manager implementation is the JaasSecurityManager . The primary role of the JaasSecurityManagerService is to externalize the security manager implementation. You can change the security manager implementation by providing an alternate implementation of the AuthenticationManager and RealmMapping interfaces. Of course this is optional because, by default, the JaasSecurityManager implementation is used.

The second fundamental role of the JaasSecurityManagerService is to provide a JNDI javax.naming.spi.ObjectFactory implementation to allow for simple code-free management of the JNDI name to security manager implementation mapping. It has been mentioned that security is enabled by specifying the JNDI name of the security manager implementation via the security-domain deployment descriptor element. When you specify a JNDI name, there has to be an object-binding there to use. To simplify the setup of the JNDI name to security manager bindings, the JaasSecurityManagerService manages the association of security manager instances to names by binding a next naming system reference with itself as the JNDI ObjectFactory under the name "java:/jaas". This allows one to use a naming convention of the form "java:/jaas/XYZ" as the value for the security-domain element, and the security manager instance for the "XYZ" security domain will be created as needed for you. The security manager for the domain "XYZ" is created on the first lookup against the "java:/jaas/XYZ" binding by creating an instance of the class specified by the SecurityManagerClassName attribute using a constructor that takes the name of the security domain. For example, consider the following 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 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 AuthenticationManager and RealmMapping security interfaces and will be of the type specified by the JaasSecurityManagerService SecurityManagerClassName attribute.

The JaasSecurityManagerService MBean is configured by default for use in the standard JBoss distribution, and you can often use the default configuration as is. The configurable attributes of the JaasSecurityManagerService include:

The JaasSecurityManagerService also supports a number of useful operations. These include flushing any security domain authentication cache at runtime, getting the list of active users in a security domain authentication cache, and any of the security manager interface methods.

Flushing a security domain authentication cache can be used to drop all cached credentials when the underlying store has been updated and you want the store state to be used immediately. The MBean operation signature is as follows:

public void flushAuthenticationCache(String securityDomain);

This can be invoked programmatically using the following code snippet:

MBeanServer server = ...;

String jaasMgrName = "jboss.security:service=JaasSecurityManager";

ObjectName jaasMgr = new ObjectName(jaasMgrName);

Object[] params = {domainName};

String[] signature = {"java.lang.String"};

server.invoke(jaasMgr, "flushAuthenticationCache", params, signature);

Getting the list of active users provides a snapshot of the Principals keys in a security domain authentication cache that are not expired. The MBean operation signature is:

public List getAuthenticationCachePrincipals(String securityDomain);

This can be invoked programmatically using the following code snippet:

MBeanServer server = ...;

String jaasMgrName = "jboss.security:service=JaasSecurityManager";

ObjectName jaasMgr = new ObjectName(jaasMgrName);

Object[] params = {domainName};

String[] signature = {"java.lang.String"};

List users = (List) server.invoke(jaasMgr, "getAuthenticationCachePrincipals", params, signature);

The security manager access method added in 3.0.5 have the following operation signatures:

public boolean isValid(String securityDomain, Principal principal,
Object credential);

public Principal getPrincipal(String securityDomain, Principal principal);

public boolean doesUserHaveRole(String securityDomain, Principal principal,
Set roles);

public Set getUserRoles(String securityDomain, Principal principal);

They provide access to the corresponding AuthenticationManager and RealmMapping interface method of the associated security domain named by the securityDomain argument.

An Extension to JaasSecurityManager, the JaasSecurityDomain MBean

The org.jboss.security.plugins.JaasSecurityDomain is an extension of JaasSecurityManager that adds the notion of a KeyStore, and JSSE KeyManagerFactory and TrustManagerFactory for supporting SSL and other cryptographic use cases. The additional configurable attributes of the JaasSecurityDomain include:

An XML JAAS Login Configuration MBean

JBoss 3.x uses a custom implementation of the javax.security.auth.login.Configuration class that is provided by the org.jboss.security.auth.login.XMLLoginConfig MBean. This configuration implementation uses an XML format that conforms to the DTD given by See The XMLLoginConfig DTD..

FIGURE 8-8. The XMLLoginConfig DTD

The name attribute of the application-policy is the login configuration name. This corresponds to the portion of the jboss.xml and jboss-web.xml security-domain element value after the "java:/jaas/" prefix. The code attribute of the login-module element specifies the class name of the login module implementation. The flag attribute controls the overall behavior of the authentication stack. The allowed values and measnings are:

  • required: the LoginModule is required to succeed. If it succeeds or fails, authentication still continues to proceed down the LoginModule list.
  • requisite: the LoginModule is required to succeed. If it succeeds, authentication continues down the LoginModule list. If it fails, control immediately returns to the application (authentication does not proceed down the LoginModule list).
  • sufficient: the LoginModule is not required to succeed. If it does succeed, control immediately returns to the application (authentication does not proceed down the LoginModule list). If it fails, authentication continues down the LoginModule list.
  • optional: the LoginModule is not required to succeed. If it succeeds or fails, authentication still continues to proceed down the LoginModule list.

Zero or more module-option elements may be specified as child elements of a login-module. These define name/value string pairs that are made available to the login module during initialization. The name attribute specifies the option name while the module-option body provides the value. An example login configuration is given in See A sample login module configuration suitable for use with XMLLoginConfig..

A sample login module configuration suitable for use with XMLLoginConfig

<policy>

<application-policy name="srp-test">

<authentication>

<login-module code="org.jboss.security.srp.jaas.SRPCacheLoginModule"

flag="required">

<module-option name="cacheJndiName">srp-test/AuthenticationCache

</module-option>

</login-module>

<login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"

flag="required">

<module-option name="password-stacking">useFirstPass</module-option>

</login-module>

</authentication>

</application-policy>

</policy>

The XMLLoginConfig MBean supports the following attributes:

  • ConfigURL: specifies the URL of the XML login configuration file that should be loaded by this mbean on startup. This must be a valid URL string representation.
  • ConfigResource: specifies the resource name of the XML login configuration file that should be loaded by this mbean on startup. The name is treated as a classpath resource for which a URL is located using the thread context class loader.
  • ValidateDTD: a flag indicating if the XML configuration should be validated against its DTD. This defaults to true.

The MBean also supports the following operations that allow one to dynamically extend the login configurations at runtime. Note that any operation that attempts to alter login configuration requires a javax.security.auth.AuthPermission("refreshLoginConfiguration") when running with a security manager. The org.jboss.chap8.service.SecurityConfig service demonstrates how this can be used to add/remove a deployment specific scurity configuration dynamically.

  • void addAppConfig(String appName, AppConfigurationEntry[] entries); this adds the given login module configuration stack to the current configuration under the given appName. This replaces any existing entry under that name.
  • void removeAppConfig(String appName); this removes the login module configuration registered under the given appName.
  • String[] loadConfig(URL configURL) throws Exception; this loads one or more login configurations from a URL representing either an XML or legacy Sun login configuration file. Note that all login configurations must be added or none will be added. It returns the names of the login configurations that were added.
  • void removeConfigs(String[] appNames); this removes the login configurations specified appNames array.
  • String displayAppConfig(String appName); this operation displays a simple string format of the named configuration if it exists.

The JAAS Login Configuration Management MBean

The installation of the custom javax.security.auth.login.Configuration is managed by the org.jboss.security.plugins.SecurityConfig MBean. The configurable attribute of the

In addition to allowing for a custom JAAS login configuration implementation, this service allows configurations to be chained together in a stack at runtime. This allows one to push a login configuration onto the stack and latter pop it. This is a feature used by the security unit tests to install custom login configurations into a default JBoss installation. Pusing a new configuration is done using:

public void pushLoginConfig(String objectName) throws JMException, MalformedObjectNameException;

The objectName parameters specifies an MBean similar to the LoginConfig attribute. The current login configuration may be removed using:

public void popLoginConfig() throws JMException;

Using and Writing JBossSX Login Modules

The JaasSecurityManager implementation allows complete customization of the authentication mechanism using JAAS login module configurations. By defining the login module configuration entry that corresponds to the security domain name you have used to secure access to your J2EE components, you define the authentication mechanism and integration implementation.

The JBossSX framework includes a number of bundled login modules suitable for integration with standard security infrastructure store protocols such as LDAP and JDBC. It also includes standard base class implementations that help enforce the expected LoginModule to Subject usage pattern that was described in the Writing Custom Login Modules These implementations allow for easy integration of your own authentication protocol, if none of the bundled login modules prove suitable. In this section we will first describe the useful bundled login modules and their configuration, and then end with a discussion of how to create your own custom LoginModule implementations for use with JBoss.

org.jboss.security.auth.spi.IdentityLoginModule

The IdentityLoginModule is a simple login module that associates the principal specified in the module options with any subject authenticated against the module. It creates a SimplePrincipal instance using the name specified by the "principal" option. Although this is certainly not an appropriate login module for production strength authentication, it can be of use in development environments when you want to test the security associated with a given principal and associated roles.

The supported login module configuration options include:

  • principal=string, The name to use for the SimplePrincipal all users are authenticated as. The principal name defaults to "guest" if no principal option is specified.
  • roles=string-list, The names of the roles that will be assigned to the user principal. The value is a comma-delimited list of role names.
  • password-stacking=useFirstPass, When password-stacking option is set, this module first looks for a shared username under the property name "javax.security.auth.login.name" in the login module shared state Map. If found this is used as the principal name. If not found the principal name set by this login module is stored under the property name "javax.security.auth.login.name".

A sample legacy Sun format login configuration entry that would authenticate all users as the principal named "jduke" and assign role names of "TheDuke", and "AnimatedCharacter" is:

testIdentity {

org.jboss.security.auth.spi.IdentityLoginModule required

principal=jduke

roles=TheDuke,AnimatedCharater;

};

The corresponding XMLLoginConfig format is:

<policy>

<application-policy name="testIdentity">

<authentication>

<login-module code="org.jboss.security.auth.spi.IdentityLoginModule"

flag="required">

<module-option name="principal">jduke</module-option>

<module-option name="roles">TheDuke,AnimatedCharater</module-option>

</login-module>

</application-policy>

</policy>

To add this entry to a JBoss server login cofiguration found in the default configuration file set you would modify the conf/default/auth.conf file of the JBoss distribution.

org.jboss.security.auth.spi.UsersRolesLoginModule

The UsersRolesLoginModule is another simple login module that supports multiple users and user roles, and is based on two Java Properties formatted text files. The username-to-password mapping file is called "users.properties" and the username-to-roles mapping file is called "roles.properties". The properties files are loaded during initialization using the initialize method thread context class loader. This means that these files can be placed into the J2EE deployment jar, the JBoss configuration directory, or any directory on the JBoss server or system classpath. The primary purpose of this login module is to easily test the security settings of multiple users and roles using properties files deployed with the application.

The users.properties file uses a "username=password" format with each user entry on a separate line as show here:

username1=password1

username2=password2

...

The roles.properties file uses as "username=role1,role2,..." format with an optional group name value. For example:

username1=role1,role2,...

username1.RoleGroup1=role3,role4,...

username2=role1,role3,...

The "username.XXX" form of property name is used to assign the username roles to a particular named group of roles where the XXX portion of the property name is the group name. The "username=..." form is an abbreviation for "username.Roles=...", where the "Roles" group name is the standard name the JaasSecurityManager expects to contain the roles which define the users permissions.

The following would be equivalent definitions for the jduke username:

jduke=TheDuke,AnimatedCharacter

jduke.Roles=TheDuke,AnimatedCharacter

The supported login module configuration options include the following:

  • unauthenticatedIdentity=name, Defines the principal name that should be assigned to requests that contain no authentication information. This can be used to allow unprotected servlets to invoke methods on EJBs that do not require a specific role. Such a principal has no associated roles and so can only access either unsecured EJBs or EJB methods that are associated with the unchecked permission constraint.
  • password-stacking=useFirstPass, When password-stacking option is set, this module first looks for a shared username and password under the property names "javax.security.auth.login.name" and "javax.security.auth.login.password" respectively in the login module shared state Map. If found these are used as the principal name and password. If not found the principal name and password are set by this login module and stored under the property names "javax.security.auth.login.name" and "javax.security.auth.login.password" respectively.
  • hashAlgorithm=string: The name of the java.security.MessageDigest algorithm to use to hash the password. There is no default so this option must be specified to enable hashing. When hashAlgorithm is specified, the clear text password obtained from the CallbackHandler is hashed before it is passed to UsernamePasswordLoginModule.validatePassword as the inputPassword argument. The expectedPassword as stored in the users.properties file must be comparably hashed.
  • hashEncoding=base64|hex: The string format for the hashed pass and must be either "base64" or "hex". Base64 is the default.
  • hashCharset=string: The encoding used to convert the clear text password to a byte array. The platform default encoding is the default.
  • usersProperties=string: (2.4.5+) The name of the properties resource containing the username to password mappings. This defaults to users.properties.
  • rolesProperties=string: (2.4.5+) The name of the properties resource containing the username to roles mappings. This defaults to roles.properties.

A sample legacy Sun format login configuration entry that assigned unauthenticated users the principal name "nobody" and contains based64 encoded, MD5 hashes of the passwords in a "usersb64.properties" file is:

testUsersRoles {

org.jboss.security.auth.spi.UsersRolesLoginModule required

usersProperties=usersb64.properties

hashAlgorithm=MD5

hashEncoding=base64

unauthenticatedIdentity=nobody

;

};

The corresponding XMLLoginConfig format is:

<policy>

<application-policy name="testUsersRoles">

<authentication>

<login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"

flag="required">

<module-option name="usersProperties">usersb64.properties

</module-option>

<module-option name="hashAlgorithm">MD5</module-option>

<module-option name="hashEncoding">base64</module-option>

<module-option name="unauthenticatedIdentity">nobody</module-option>

</login-module>

</application-policy>

</policy>

org.jboss.security.auth.spi.LdapLoginModule

The LdapLoginModule is a LoginModule implementation that authenticates against an LDAP server using JNDI login using the login module configuration options. You would use the LdapLoginModule if your username and credential information are store in an LDAP server that is accessible using a JNDI LDAP provider.

The LDAP connectivity information is provided as configuration options that are passed through to the environment object used to create JNDI initial context. The standard LDAP JNDI properties used include the following:

  • java.naming.factory.initial, The classname of the InitialContextFactory implementation. This defaults to the Sun LDAP provider implementation com.sun.jndi.ldap.LdapCtxFactory .
  • java.naming.provider.url, The ldap URL for the LDAP server
  • java.naming.security.authentication, The security level to use. This defaults to "simple".
  • java.naming.security.protocol, The transport protocol to use for secure access, such as, ssl
  • java.naming.security.principal, The principal for authenticating the caller to the service. This is built from other properties as described below.
  • java.naming.security.credentials, The value of the property depends on the authentication scheme. For example, it could be a hashed password, clear-text password, key, certificate, and so on.

The supported login module configuration options include the following:

  • principalDNPrefix=string, A prefix to add to the username to form the user distinguished name. See principalDNSuffix for more info.
  • principalDNSuffix=string, A suffix to add to the username when forming the user distiguished name. This is useful if you prompt a user for a username and you don't want the user to have to enter the fully distinguished name. Using this property and principalDNSuffix the userDN will be formed as:

String userDN = principalDNPrefix + username + principalDNSuffix;

  • useObjectCredential=true|false, Indicates that the credential should be obtained as an opaque Object using the org.jboss.security.auth.callback.ObjectCallback type of Callback rather than as a char[] password using a JAAS PasswordCallback . This allows for passing non-char[] credential information to the LDAP server.
  • rolesCtxDN=string, The fixed distinguished name to the context to search for user roles.
  • userRolesCtxDNAttributeName=string, The name of an attribute in the user object that contains the distinguished name to the context to search for user roles. This differs from rolesCtxDN in that the context to search for a user's roles can be unique for each user.
  • roleAttributeID=string, The name of the attribute that contains the user roles. If not specified this defaults to "roles".
  • roleAttributeIsDN=string: A flag indicating whether the roleAttributeID contains the fully distinguished name of a role object, or the role name. If false, the role name is taken from the value of roleAttributeID. If true, the role attribute represents the distinguished name of a role object. The role name is taken from the value of the roleNameAttributeId attribute of the context name by the distinguished name. In certain directory schemas (e.g., MS ActiveDirectory), role attributes in the user object are stored as DNs to role objects instead of as simple names, in which case, this property should be set to true. The default is false.
  • roleNameAttributeID=string: The name of the attribute of the in the context pointed to by the roleCtxDN distiguished name value which contains the role name. If the roleAttributeIsDN property is set to true, this property is used to find the role object's name attribute. The default is "group".
  • uidAttributeID=string, The name of the attribute in the object containing the user roles that corresponds to the userid. This is used to locate the user roles. If not specified this defaults to "uid".
  • matchOnUserDN=true|false, A flag indicating if the search for user roles should match on the user's fully distinguished name. If false, just the username is used as the match value against the uidAttributeName attribute. If true, the full userDN is used as the match value.
  • unauthenticatedIdentity=string, The principal name that should be assigned to requests that contain no authentication information. This behavior is inherited from the UsernamePasswordLoginModule superclass.
  • password-stacking=useFirstPass, When the password-stacking option is set, this module first looks for a shared username and password under the property names "javax.security.auth.login.name" and "javax.security.auth.login.password" respectively in the login module shared state Map. If found these are used as the principal name and password. If not found the principal name and password are set by this login module and stored under the property names "javax.security.auth.login.name" and "javax.security.auth.login.password" respectively.
  • hashAlgorithm=string: The name of the java.security.MessageDigest algorithm to use to hash the password. There is no default so this option must be specified to enable hashing. When hashAlgorithm is specified, the clear text password obtained from the CallbackHandler is hashed before it is passed to UsernamePasswordLoginModule.validatePassword as the inputPassword argument. The expectedPassword as stored in the LDAP server must be comparably hashed.
  • hashEncoding=base64|hex: The string format for the hashed pass and must be either "base64" or "hex". Base64 is the default.
  • hashCharset=string: The encoding used to convert the clear text password to a byte array. The platform default encoding is the default.
  • allowEmptyPasswords: A flag indicating if empty(length==0) passwords should be passed to the ldap server. An empty password is treated as an anonymous login by some ldap servers and this may not be a desirable feature. Set this to false to reject empty passwords, true to have the ldap server validate the empty password. The default is true.

The authentication of a user is performed by connecting to the LDAP server based on the login module configuration options. Connecting to the LDAP server is done by creating an InitialLdapContext with an environment composed of the LDAP JNDI properties described previously in this section. The Context.SECURITY_PRINCIPAL is set to the distinguished name of the user as obtained by the callback handler in combination with the principalDNPrefix and principalDNSuffix option values, and the Context.SECURITY_CREDENTIALS property is either set to the String password or the Object credential depending on the useObjectCredential option.

Once authentication has succeeded by virtue of being able to create an InitialLdapContext instance, the user's roles are queried by performing a search on the rolesCtxDN location with search attributes set to the roleAttributeName and uidAttributeName option values. The roles names are obtaining by invoking the toString method on the role attributes in the search result set.

A sample Sun legacy format login configuration entry is:

testLdap {

org.jboss.security.auth.spi.LdapLoginModule required

java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory

java.naming.provider.url="ldap://ldaphost.jboss.org:1389/"

java.naming.security.authentication=simple

principalDNPrefix=uid=

uidAttributeID=userid

roleAttributeID=roleName

principalDNSuffix=,ou=People,o=jboss.org

rolesCtxDN=cn=JBossSX Tests,ou=Roles,o=jboss.org

};

The corresponding XMLLoginConfig format is:

<policy>

<application-policy name="testLdap">

<authentication>

<login-module code="org.jboss.security.auth.spi.LdapLoginModule"

flag="required">

<module-option name="java.naming.factory.initial">

com.sun.jndi.ldap.LdapCtxFactory

</module-option>

<module-option name="java.naming.provider.url">

ldap://ldaphost.jboss.org:1389/

</module-option>

<module-option name="java.naming.security.authentication">

simple

</module-option>

<module-option name="principalDNPrefix">uid=</module-option>

<module-option name="uidAttributeID">userid</module-option>

<module-option name="roleAttributeID">roleName</module-option>

<module-option name="principalDNSuffix">,ou=People,o=jboss.org

</module-option>

<module-option name="rolesCtxDN">cn=JBossSX Tests,ou=Roles,o=jboss.org

</module-option>

</login-module>

</application-policy>

</policy>

To help you understand all of the options of the LdapLoginModule, consider the sample LDAP server data shown in See An LDAP server configuration compatible with the testLdap sample configuration... This figure corresponds to the testLdap login configuration just shown.

 
FIGURE 8-9. An LDAP server configuration compatible with the testLdap sample configuration.

Take a look at the testLdap login module configuration in comparision to the See An LDAP server configuration compatible with the testLdap sample configuration.. schema. The java.naming.factory.initial , java.naming.factory.url and java.naming.security options indicate the Sun LDAP JNDI provider implementation will be used, the LDAP server is located on host ldaphost.jboss.org on port 1389, and that simple username and password will be used to authenticate clients connecting to the LDAP server.

When the LdapLoginModule performs authentication of a user, it does so by connecting to the LDAP server specified by the java.naming.factory.url . The java.naming.security.principal property is built from the principalDNPrefix, passed in username and principalDNSuffix as described above. For the testLdap configuration example and a username of 'jduke', the java.naming.security.principal string would be 'uid=jduke,ou=People,o=jboss.org'. This corresponds to the LDAP context on the lower right of See An LDAP server configuration compatible with the testLdap sample configuration.. labeled as Principal Context. The java.naming.security.credentials property would be set to the passed in password and it would have to match the userPassword attribute of the Principal Context. How a secured LDAP context stores the authentication credential information depends on the LDAP server, so your LDAP server may handle the validation of the java.naming.security.credentials property differently.

Once authentication succeeds, the roles on which authorization will be based are retrieved by performing a JNDI search of the LDAP context whose distinguished name is given by the rolesCtxDN option value. For the testLdap configuration this is 'cn=JBossSX Tests,ou=Roles,o=jboss.org' and corresponds to the LDAP context on the lower left of See An LDAP server configuration compatible with the testLdap sample configuration.. labeled Roles Context. The search attempts to locate any subcontexts that contain an attribute whose name is given by the uidAttributeID option, and whose value matches the username passed to the login module. For any matching context, all values of the attribute whose name is given by the roleAttributeID option are obtained. For the testLdap configuration the attribute name that contains the roles is called roleName. The resulting roleName values are stored in the JAAS Subject associated with the LdapLoginModule as the Roles group principals that will be used for role-based authorization. For the LDAP schema shown in See An LDAP server configuration compatible with the testLdap sample configuration.., the roles that will be assigned to the user 'jduke' are 'TheDuke' and 'AnimatedCharacter'.

org.jboss.security.auth.spi.DatabaseServerLoginModule

The DatabaseServerLoginModule is a JDBC based login module that supports authentication and role mapping. You would use this login module if you have your username, password and role information in a JDBC accessible database. The DatabaseServerLoginModule is based on two logical tables:

Table Principals(PrincipalID text, Password text)

Table Roles(PrincipalID text, Role text, RoleGroup text)

The Principals table associates the user PrincipalID with the valid password and the Roles table associates the user PrincipalID with its role sets. The roles used for user permissions must be contained in rows with a RoleGroup column value of Roles. The tables are logical in that you can specify the SQL query that the login module uses. All that is required is that the java.sql.ResultSet has the same logical structure as the Principals and Roles tables described previously. The actual names of the tables and columns are not relevant as the results are accessed based on the column index. To clarify this notion, consider a database with two tables, Principals and Roles, as already declared. The following statements build the tables to contain a PrincipalID 'java' with a Password of 'echoman' in the Principals table, a PrincipalID 'java' with a role named 'Echo' in the 'Roles' RoleGroup in the Roles table, and a PrincipalID 'java' with a role named 'caller_java' in the 'CallerPrincipal' RoleGroup in the Roles table:

INSERT INTO Principals VALUES('java', 'echoman')

INSERT INTO Roles VALUES('java', 'Echo', 'Roles')

INSERT INTO Roles VALUES('java', 'caller_java', 'CallerPrincipal')

The supported login module configuration options include the following:

  • dsJndiName: The JNDI name for the DataSource of the database containing the logical "Principals" and "Roles" tables. If not specified this defaults to "java:/DefaultDS".
  • principalsQuery: The prepared statement query equivalent to: "select Password from Principals where PrincipalID=?". If not specified this is the exact prepared statement that will be used.
  • rolesQuery: The prepared statement query equivalent to: "select Role, RoleGroup from Roles where PrincipalID=?". If not specified this is the exact prepared statement that will be used.
  • unauthenticatedIdentity=string, The principal name that should be assigned to requests that contain no authentication information.
  • password-stacking=useFirstPass, When password-stacking option is set, this module first looks for a shared username and password under the property names "javax.security.auth.login.name" and "javax.security.auth.login.password" respectively in the login module shared state Map. If found these are used as the principal name and password. If not found the principal name and password are set by this login module and stored under the property names "javax.security.auth.login.name" and "javax.security.auth.login.password" respectively.
  • hashAlgorithm=string: The name of the java.security.MessageDigest algorithm to use to hash the password. There is no default so this option must be specified to enable hashing. When hashAlgorithm is specified, the clear text password obtained from the CallbackHandler is hashed before it is passed to UsernamePasswordLoginModule.validatePassword as the inputPassword argument. The expectedPassword as obtained from the database must be comparably hashed.
  • hashEncoding=base64|hex: The string format for the hashed pass and must be either "base64" or "hex". Base64 is the default.
  • hashCharset=string: The encoding used to convert the clear text password to a byte array. The platform default encoding is the default

As an example DatabaseServerLoginModule configuration, consider a custom table schema like the following:

CREATE TABLE Users(username VARCHAR(64) PRIMARY KEY, passwd VARCHAR(64))

CREATE TABLE UserRoles(username VARCHAR(64), userRoles VARCHAR(32))

A sample Sun legacy format corresponding DatabaseServerLoginModule configuration would be:

testDB {

org.jboss.security.auth.spi.DatabaseServerLoginModule required

dsJndiName="java:/MyDatabaseDS"

principalsQuery="select passwd from Users username where username=?"

rolesQuery="select userRoles, 'Roles' from UserRoles where username=?"

;

};

The corresponding XMLLoginConfig format is:

<policy>

<application-policy name="testDB">

<authentication>

<login-module

code="org.jboss.security.auth.spi.DatabaseServerLoginModule"

flag="required">

<module-option name="dsJndiName">java:/MyDatabaseDS

</module-option>

<module-option name="principalsQuery">select passwd from Users username where username=?</module-option>

<module-option name="rolesQuery">select userRoles, 'Roles' from UserRoles where username=?</module-option>

</login-module>

</application-policy>

</policy>

org.jboss.security.auth.spi.ProxyLoginModule

The ProxyLoginModule is a login module that loads a delegate LoginModule using the current thread context class loader. The purpose of this module is to work around the current JAAS 1.0 class loader limitation that requires LoginModule s to be on the system classpath1. Some custom LoginModule s use classes that are loaded from the JBoss server lib/ext directory and these are not available if the LoginModule is placed on the system classpath. To work around this limitation you use the ProxyLoginModule to bootstrap the custom LoginModule. The ProxyLoginModule has one required configuration option called moduleName. It specifies the fully qualified class name of the LoginModule implementation that is to be bootstrapped. Any number of additional configuration options may be specified, and they will be passed to the bootstrapped login module.

As an example, consider a custom login module that makes use of some service that is loaded from the JBoss lib/ext directory. The class name of the custom login module is com.biz.CustomServiceLoginModule. A suitable Sun legacy format ProxyLoginModule configuration entry for bootstrapping this custom login module would be:

testProxy {

org.jboss.security.auth.spi.ProxyLoginModule required

moduleName=com.biz.CustomServiceLoginModule

customOption1=value1

customOption2=value2

customOption3=value3;

};

The corresponding XMLLoginConfig format is:

<policy>

<application-policy name="testProxy">

<authentication>

<login-module code="org.jboss.security.auth.spi.ProxyLoginModule"

flag="required">

<module-option name="moduleName">com.biz.CustomServiceLoginModule

</module-option>

<module-option name="customOption1">value1</module-option>

<module-option name="customOption2">value2</module-option>

<module-option name="customOption3">value3</module-option>

</login-module>

</application-policy>

</policy>

org.jboss.security.auth.spi.RunAsLoginModule

New in JBoss-3.0.3 is a helper login module called RunAsLoginModule . It pushes a run as role for the duration of the login phase of authentication, and pops the run as role in either the commit or abort phase. The purpose of this login module is to provide a role for other login modules that need to access secured resources in order to perform their authentication. An example would be a login module that accesses an secured EJB. This login module must be configured ahead of the login module(s) that need a run as role established.

The only login module configuration option is:

  • roleName: the name of the role to use as the run as role during login phase. If not specified a default of "nobody" is used.
org.jboss.security.ClientLoginModule

The ClientLoginModule is an implementation of LoginModule for use by JBoss clients for the establishment of the caller identity and credentials. This simply sets the org.jboss.security.SecurityAssociation.principal to the value of the NameCallback filled in by the CallbackHandler , and the org.jboss.security.SecurityAssociation.credential to the value of the PasswordCallback filled in by the CallbackHandler . This is the only supported mechanism for a client to establish the current thread's caller. Both stand-alone client applications and server environments, acting as JBoss EJB clients where the security environment has not been configured to use JBossSX transparently, need to use the ClientLoginModule . Of course, you could always set the org.jboss.security.SecurityAssociation information directly, but this is considered an internal API that is subject to change without notice.

Note that this login module does not perform any authentication. It merely copies the login information provided to it into the JBoss server EJB invocation layer for subsequent authentication on the server. If you need to perform client-side authentication of users you would need to configure another login module in addition to the ClientLoginModule .

The supported login module configuration options include the following:

  • multi-threaded=true|false, When the multi-threaded option is set to true, each login thread has its own principal and credential storage. This is useful in client environments where multiple user identities are active in separate threads. When true, each separate thread must perform its own login. When set to false the login identity and credentials are global variables that apply to all threads in the VM. The default for this option is false.
  • password-stacking=useFirstPass, When password-stacking option is set, this module first looks for a shared username and password using "javax.security.auth.login.name" and "javax.security.auth.login.password" respectively in the login module shared state Map. This allows a module configured prior to this one to establish a valid username and password that should be passed to JBoss. You would use this option if you want to perform client-side authentication of clients using some other login module such as the LdapLoginModule.

A sample login configuration for ClientLoginModule is the default configuration entry found in the JBoss distribution client/auth.conf file. The configuration is:

other {

// Put your login modules that work without jBoss here

// jBoss LoginModule

org.jboss.security.ClientLoginModule required;

// Put your login modules that need jBoss here

};

Writing Custom Login Modules

If the login modules bundled with the JBossSX framework do not work with your security environment, you can write your own custom login module implementation that does.

Recall from the section on the JaasSecurityManager architecture that the JaasSecurityManager expected a particular usage pattern of the Subject principals set. You need to understand the JAAS Subject class's information storage features and the expected usage of these features to be able to write a login module that works with the JaasSecurityManager . This section examines this requirement and introduces two abstract base LoginModule implementations that can help you implement your own custom login modules.

You can obtain security information associated with a Subject in six ways using the following methods:

java.util.Set getPrincipals()

java.util.Set getPrincipals(java.lang.Class c)

java.util.Set getPrivateCredentials()

java.util.Set getPrivateCredentials(java.lang.Class c)

java.util.Set getPublicCredentials()

java.util.Set getPublicCredentials(java.lang.Class c)

For Subject identities and roles, JBossSX has selected the most natural choice: the principals sets obtained via getPrincipals() and getPrincipals(java.lang.Class) . The usage pattern is as follows:

Support for the Subject Usage Pattern

To simplify correct implementation of the Subject usage patterns described in the preceding section, JBossSX includes two abstract login modules that handle the population of the authenticated Subject with a template pattern that enforces correct Subject usage. The most generic of the two is the org.jboss.security.auth.spi.AbstractServerLoginModule class. It provides a concrete implementation of the javax.security.auth.spi.LoginModule interface and offers abstract methods for the key tasks specific to an operation environment security infrastructure. The key details of the class are highlighted in the following class fragment. The Javadoc comments detail the responsibilities of subclasses.

package org.jboss.security.auth.spi;

/** This class implements the common functionality required for a

JAAS server-side LoginModule and implements the JBossSX standard

Subject usage pattern of storing identities and roles. Subclass

this module to create your own custom LoginModule and override the

login(), getRoleSets(), and getIdentity() methods.

*/

public abstract class AbstractServerLoginModule

implements javax.security.auth.spi.LoginModule

{

protected Subject subject;

protected CallbackHandler callbackHandler;

protected Map sharedState;

protected Map options;

protected Logger log;

/** Flag indicating if the shared credential should be used */

protected boolean useFirstPass;

/** Flag indicating if the login phase succeeded. Subclasses that override

the login method must set this to true on successful completion of login

*/

protected boolean loginOk;

...

/**

* Initialize the login module. This stores the subject, callbackHandler

* and sharedState and options for the login session. Subclasses should override

* if they need to process their own options. A call to super.initialize(...)

* must be made in the case of an override.

* <p>

* The options are checked for the <em>password-stacking</em> parameter.

* If this is set to "useFirstPass", the login identity will be taken from the

* <code>javax.security.auth.login.name</code> value of the sharedState map,

* and the proof of identity from the

* <code>javax.security.auth.login.password</code> value of the sharedState map.

*

* @param subject the Subject to update after a successful login.

* @param callbackHandler the CallbackHandler that will be used to obtain the

* the user identity and credentials.

* @param sharedState a Map shared between all configured login module instances

* @param options the parameters passed to the login module.

*/

public void initialize(Subject subject,

CallbackHandler callbackHandler,

Map sharedState,

Map options)

{

...

}

/** Looks for javax.security.auth.login.name and javax.security.auth.login.password

values in the sharedState map if the useFirstPass option was true and returns

true if they exist. If they do not or are null this method returns false.

 

Note that subclasses that override the login method must set the loginOk

ivar to true if the login succeeds in order for the commit phase to

populate the Subject. This implementation sets loginOk to true if the

login() method returns true, otherwise, it sets loginOk to false.

*/

public boolean login() throws LoginException

{

...

}

/** Overridden by subclasses to return the Principal that

corresponds to the user primary identity.

*/

abstract protected Principal getIdentity();

/** Overridden by subclasses to return the Groups that

correspond to the role sets assigned to the user. Subclasses

should create at least a Group named "Roles" that contains

the roles assigned to the user.

A second common group is "CallerPrincipal," which provides

the application identity of the user rather than the security

domain identity.

@return Group[] containing the sets of roles

*/

abstract protected Group[] getRoleSets() throws LoginException;

}

One key change in JBoss-3.0.3 was the addition of the loginOk instance variable. This must be set to true if the login succeeds, false otherwise by any subclasses that override the login method. Failure to set this variable correctly will result in the commit method either not updating the Subject when it should, or updating the Subject when it should not. Tracking the outcome of the login phase was added to allow login module to be chained together with control flags that do not require that the login module succeed in order for the overall login to succeed.

The second abstract base login module suitable for custom login modules is the org.jboss.security.auth.spi.UsernamePasswordLoginModule . The login module further simplifies custom login module implementation by enforcing a string-based username as the user identity and a char[] password as the authentication credential. It also supports the mapping of anonymous users (indicated by a null username and password) to a Principal with no roles. The key details of the class are highlighted in the following class fragment. The Javadoc comments detail the responsibilities of subclasses.

package org.jboss.security.auth.spi;

/** An abstract subclass of AbstractServerLoginModule that imposes

a an identity == String username, credentials == String password

view on the login process. Subclasses override the

getUsersPassword() and getUsersRoles() methods to return the

expected password and roles for the user.

*/

public abstract class UsernamePasswordLoginModule

extends AbstractServerLoginModule

{

/** The login identity */

private Principal identity;

/** The proof of login identity */

private char[] credential;

/** The principal to use when a null username and password

are seen */

private Principal unauthenticatedIdentity;

/** The message digest algorithm used to hash passwords. If null then

plain passwords will be used. */

private String hashAlgorithm = null;

/** The name of the charset/encoding to use when converting the password

String to a byte array. Default is the platform's default encoding.

*/

private String hashCharset = null;

/** The string encoding format to use. Defaults to base64. */

private String hashEncoding = null;

...

/** Override the superclass method to look for an

unauthenticatedIdentity property. This method first invokes

the super version.

@param options,

@option unauthenticatedIdentity: the name of the principal

to assign and authenticate when a null username and password

are seen.

*/

public void initialize(Subject subject,

CallbackHandler callbackHandler,

Map sharedState,

Map options)

{

super.initialize(subject, callbackHandler, sharedState,

options);

// Check for unauthenticatedIdentity option.

Object option = options.get("unauthenticatedIdentity");

String name = (String) option;

if( name != null )

unauthenticatedIdentity = new SimplePrincipal(name);

}

...

/** A hook that allows subclasses to change the validation of

the input password against the expected password. This version

checks that neither inputPassword or expectedPassword are null

and that inputPassword.equals(expectedPassword) is true;

@return true if the inputPassword is valid, false otherwise.

*/

protected boolean validatePassword(String inputPassword,

String expectedPassword)

{

if( inputPassword == null || expectedPassword == null )

return false;

return inputPassword.equals(expectedPassword);

}

/** Get the expected password for the current username

available via the getUsername() method. This is called from

within the login() method after the CallbackHandler has

returned the username and candidate password.

@return the valid password String

*/

abstract protected String getUsersPassword()

throws LoginException;

}

The choice of subclassing the AbstractServerLoginModule versus UsernamePasswordLoginModule is simply based on whether a String based username and String credential are usable for the authentication technology you are writing the login module for. If the string based semantic is valid, then subclass UsernamePasswordLoginModule , else subclass AbstractServerLoginModule .

The steps you are required to perform when writing a custom login module are summerized in the following depending on which base login module class you choose. When writing a custom login module that integrates with your security infrastructure, you should start by subclassing AbstractServerLoginModule or UsernamePasswordLoginModule to ensure that your login module provides the authenticated Principal information in the form expected by the JBossSX security manager.

When subclassing the AbstractServerLoginModule , you need to override the following:

  • void initialize(Subject, CallbackHandler, Map, Map); if you have custom options to parse.
  • boolean login(); to perform the authentication activity. Be sure to set the loginOk instance variable to true if login succeeds, false if it fails.
  • Principal getIdentity(); to return the Principal object for the user authenticated by the log() step.
  • Group[] getRoleSets() ; to return at least one Group named "Roles" that contains the roles assigned to the Principal authenticated during login(). A second common Group is named "CallerPrincipal" and provides the user's application identity rather than the security domain identity.

When subclassing the UsernamePasswordLoginModule , you need to override the following:

  • void initialize(Subject, CallbackHandler, Map, Map) ; if you have custom options to parse.
  • Group[] getRoleSets() ; to return at least one Group named "Roles" that contains the roles assigned to the Principal authenticated during login() . A second common Group is named "CallerPrincipal" and provides the user's application identity rather than the security domain identity.
  • String getUsersPassword(); to return the expected password for the current username available via the getUsername() method. The getUsersPassword() method is called from within login() after the CallbackHandler returns the username and candidate password.
A Custom LoginModule Example

In this section we will develop a custom login module example. It will extend the UsernamePasswordLoginModule and obtains a user's password and role names from a JNDI lookup. The idea is that there is a JNDI context that will return a user's password if you perform a lookup on the context using a name of the form "password/<username>" where <username> is the current user being authenticated. Similary, a lookup of the form "roles/<username>" returns the requested user's roles.

The source code for the example is located in the src/main/org/jboss/chap8/ex2 directory of the book examples. See A JndiUserAndPass custom login module. shows the source code for the JndiUserAndPass custom login module. Note that because this extends the JBoss UsernamePasswordLoginModule , all the JndiUserAndPass does is obtain the user's password and roles from the JNDI store. The JndiUserAndPass does not concern itself with the JAAS LoginModule operations.

A JndiUserAndPass custom login module

package org.jboss.chap8.ex2;

import java.security.acl.Group;

import java.util.Map;

import javax.naming.InitialContext;

import javax.naming.NamingException;

import javax.security.auth.Subject;

import javax.security.auth.callback.CallbackHandler;

import javax.security.auth.login.LoginException;

import org.jboss.security.SimpleGroup;

import org.jboss.security.SimplePrincipal;

import org.jboss.security.auth.spi.UsernamePasswordLoginModule;

/** An example custom login module that obtains passwords and roles for a user

from a JNDI lookup.

@author Scott.Stark@jboss.org

@version $Revision$

*/

public class JndiUserAndPass extends UsernamePasswordLoginModule

{

/** The JNDI name to the context that handles the password/<username> lookup */

private String userPathPrefix;

/** The JNDI name to the context that handles the roles/<username> lookup */

private String rolesPathPrefix;

/** Override to obtain the userPathPrefix and rolesPathPrefix options.

*/

public void initialize(Subject subject, CallbackHandler callbackHandler,

Map sharedState, Map options)

{

super.initialize(subject, callbackHandler, sharedState, options);

userPathPrefix = (String) options.get("userPathPrefix");

rolesPathPrefix = (String) options.get("rolesPathPrefix");

}

/** Get the roles the current user belongs to by querying the

rolesPathPrefix + '/' + super.getUsername() JNDI location.

*/

protected Group[] getRoleSets() throws LoginException

{

try

{

InitialContext ctx = new InitialContext();

String rolesPath = rolesPathPrefix + '/' + super.getUsername();

String[] roles = (String[]) ctx.lookup(rolesPath);

Group[] groups = {new SimpleGroup("Roles")};

log.info("Getting roles for user="+super.getUsername());

for(int r = 0; r < roles.length; r ++)

{

SimplePrincipal role = new SimplePrincipal(roles[r]);

log.info("Found role="+roles[r]);

groups[0].addMember(role);

}

return groups;

}

catch(NamingException e)

{

log.error("Failed to obtain groups for user="+super.getUsername(), e);

throw new LoginException(e.toString(true));

}

}

/** Get the password of the current user by querying the

userPathPrefix + '/' + super.getUsername() JNDI location.

*/

protected String getUsersPassword() throws LoginException

{

try

{

InitialContext ctx = new InitialContext();

String userPath = userPathPrefix + '/' + super.getUsername();

log.info("Getting password for user="+super.getUsername());

String passwd = (String) ctx.lookup(userPath);

log.info("Found password="+passwd);

return passwd;

}

catch(NamingException e)

{

log.error("Failed to obtain password for user="+super.getUsername(), e);

throw new LoginException(e.toString(true));

}

}

}

The details of the JNDI store are found in the org.jboss.chap8.ex2.service.JndiStore MBean. This service binds an ObjectFactory that returns a javax.naming.Context proxy into JNDI. The proxy handles lookup operations done against it by checking the prefix of the lookup name against "password" and "roles". When the name begins with "password", a user's password is being requested. When the name begins with "roles" the user's roles are being requested. The example implementation always returns a password of "theduke" and an array of roles names equal to {"TheDuke", "Echo"} regardless of what the username is. You can experiment with other implementations as you wish.

The example code includes a simple session bean for testing the custom login module. To build, deploy and run the example, execute the following command from the examples directory. Make sure you have the JBoss server running. The key lines from the client are given in See The chap8-ex2 secured client access output.while the server side operation of the is shown in See The chap8-ex2 server side behavior of the JndiUserAndPass..

The chap8-ex2 secured client access output

[nr@toki examples]$ ant -Dchap=chap8 -Dex=2 run-example

Buildfile: build.xml

...

run-example2:

[copy] Copying 1 file to /tmp/jboss-3.2.3/server/default/deploy

[echo] Waiting for 5 seconds for deploy...

[java] [INFO,ExClient] Login with username=jduke, password=theduke

[java] [INFO,ExClient] Looking up EchoBean2

[java] [INFO,ExClient] Created Echo

[java] [INFO,ExClient] Echo.echo('Hello') = Hello

BUILD SUCCESSFUL

Total time: 12 seconds

The chap8-ex2 server side behavior of the JndiUserAndPass

01:34:11,118 INFO [MainDeployer] Starting deployment of package: file:/private/tmp/jboss-3.2.3/server/default/deploy/chap8-ex2.jar

01:34:11,312 INFO [EJBDeployer] nested deployment: file:/private/tmp/jboss-3.2.3/server/default/tmp/deploy/tmp36177chap8-ex2.jar-contents/chap8-ex2.sar

01:34:12,831 INFO [EjbModule] Deploying EchoBean2

01:34:13,084 INFO [JaasSecurityManagerService] Created securityMgr=org.jboss.security.plugins.JaasSecurityManager@1df832

01:34:13,091 INFO [JaasSecurityManagerService] setCachePolicy, c=org.jboss.util.TimedCachePolicy@91b2c8

01:34:13,094 INFO [JaasSecurityManagerService] Added chap8-ex2, org.jboss.security.plugins.SecurityDomainContext@7095e5 to map

01:34:13,849 INFO [JndiStore] Start, bound security/store

01:34:13,854 INFO [SecurityConfig] Using JAAS AuthConfig: jar:file:/private/tmp/jboss-3.2.3/server/default/tmp/deploy/tmp36177chap8-ex2.jar-contents/chap8-ex2.sar!/META-INF/login-config.xml

01:34:14,174 INFO [SecurityConfig] Started jboss.docs.chap8:service=LoginConfig-EX2

01:34:14,679 INFO [StatelessSessionInstancePool] Started jboss.j2ee:jndiName=EchoBean2,plugin=pool,service=EJB

01:34:14,683 INFO [StatelessSessionContainer] Started jboss.j2ee:jndiName=EchoBean2,service=EJB

01:34:14,687 INFO [EjbModule] Started jboss.j2ee:module=chap8-ex2.jar,service=EjbModule

01:34:14,690 INFO [EJBDeployer] Deployed: file:/private/tmp/jboss-3.2.3/server/default/deploy/chap8-ex2.jar

01:34:15,043 INFO [MainDeployer] Deployed package: file:/private/tmp/jboss-3.2.3/server/default/deploy/chap8-ex2.jar

01:34:19,685 INFO [JndiUserAndPass] Getting password for user=jduke

01:34:19,859 INFO [JndiStore] lookup, name=password/jduke

01:34:19,862 INFO [JndiUserAndPass] Found password=theduke

01:34:19,940 INFO [JndiStore] lookup, name=roles/jduke

01:34:19,946 INFO [JndiUserAndPass] Getting roles for user=jduke

01:34:19,947 INFO [JndiUserAndPass] Found role=TheDuke

01:34:19,947 INFO [JndiUserAndPass] Found role=Echo

The choice of using the JndiUserAndPass custom login module for the server side authentication of the user is determined by the login configuration for the example security domain. The ejb-jar META-INF/jboss.xml descriptor sets the security domain and the sar META-INF/login-config.xml descriptor defines the login module configuration. The contents of these descriptors are shown in See The chap8-ex2 security domain and login module configuration..

The chap8-ex2 security domain and login module configuration

The chap8-ex2 jboss.xml descriptor security domain settings

<?xml version="1.0"?>

<jboss>

<security-domain>java:/jaas/chap8-ex2</security-domain>

</jboss>

The login-config.xml configuration fragment for the chap8-ex2 application

<application-policy name = "chap8-ex2">

<authentication>

<login-module code = "org.jboss.chap8.ex2.JndiUserAndPass"

flag = "required">

<module-option name = "userPathPrefix">/security/store/password

</module-option>

<module-option name = "rolesPathPrefix">/security/store/roles

</module-option>

</login-module>

</authentication>

</application-policy>

The Secure Remote Password (SRP) Protocol

The SRP protocol is an implementation of a public key exchange handshake described in the Internet standards working group request for comments 2945(RFC2945). The RFC2945 abstract states:

This document describes a cryptographically strong network authentication mechanism known as the Secure Remote Password (SRP) protocol. This mechanism is suitable for negotiating secure connections using a user-supplied password, while eliminating the security problems traditionally associated with reusable passwords. This system also performs a secure key exchange in the process of authentication, allowing security layers (privacy and/or integrity protection) to be enabled during the session. Trusted key servers and certificate infrastructures are not required, and clients are not required to store or manage any long-term keys. SRP offers both security and deployment advantages over existing challenge-response techniques, making it an ideal drop-in replacement where secure password authentication is needed.

Note: The complete RFC2945 specification can be obtained from http://www.rfc-editor.org/rfc.html. Additional information on the SRP algorithm and its history can be found here: http://www-cs-students.stanford.edu/~tjw/srp/.

SRP is similar in concept and security to other public key exchange algorithms, such as Diffie-Hellman and RSA. SRP is based on simple string passwords in a way that does not require a clear text password to exist on the server. This is in contrast to other public key-based algorithms that require client certificates and the corresponding certificate management infrastructure.

Algorithms like Diffie-Hellman and RSA are known as public key exchange algorithms. The concept of public key algorithms is that you have two keys, one public that is available to everyone, and one that is private and known only to you. When someone wants to send encrypted information to you, then encrpyt the information using your public key. Only you are able to decrypt the information using your private key. Contrast this with the more traditional shared password based encryption schemes that require the sender and receiver to know the shared password. Public key algorithms eliminate the need to share passwords. For more information on public key algorithms as well as numerous other cryptographic algorithms, see "Applied Cryptography, Second Edition" by Bruce Schneier, ISBN 0-471-11709-9.

The JBossSX framework includes an implementation of SRP that consists of the following elements:

See The JBossSX components of the SRP client-server framework.. gives a diagram of the key components involved in the JBossSX implementation of the SRP client/server framework.

FIGURE 8-10. The JBossSX components of the SRP client-server framework.

On the client side, SRP shows up as a custom JAAS LoginModule implementation that communicates to the authentication server through an org.jboss.security.srp.SRPServerInterface proxy. A client enables authentication using SRP by creating a login configuration entry that includes the org.jboss.security.srp.jaas.SRPLoginModule. This module supports the following configuration options:

Any other options passed in that do not match one of the previous named options is treated as a JNDI property to use for the environment passed to the IntialContext constructor. This is useful if the SRP server interface is not available from the default IntialContext.

The SRPLoginModule needs to be configured along with the standard ClientLoginModule to allow the SRP authentication credentials to be used for validation of access to security J2EE components. An example login configuration entry that demonstrates such a setup is:

srp {

org.jboss.security.srp.jaas.SRPLoginModule required

srpServerJndiName="SRPServerInterface"

;

org.jboss.security.ClientLoginModule required

password-stacking="useFirstPass"

;

};

On the JBoss server side, there are two MBeans that manage the objects that collectively make up the SRP server. The primary service is the org.jboss.security.srp.SRPService MBean, and it is responsible for exposing an RMI accessible version of the SRPServerInterface as well as updating the SRP authentication session cache. The configurable SRPService MBean attributes include the following:

The one input setting is the VerifierSourceJndiName attribute. This is the location of the SRP password information store implementation that must be provided and made available through JNDI. The org.jboss.security.srp SRPVerifierStoreService is an example MBean service that binds an implementation of the SRPVerifierStore interface that uses a file of serialized objects as the persistent store. Although not realistic for a production environment, it does allow for testing of the SRP protocol and provides an example of the requirements for an SRPVerifierStore service. The configurable SRPVerifierStoreService MBean attributes include the following:

The SRPVerifierStoreService MBean also supports addUser and delUser operations for addition and deletion of users. The signatures are:

public void addUser(String username, String password) throws IOException;

public void delUser(String username) throws IOException;

An example configuration of these services is presented in the section The Secure Remote Password (SRP) Protocol.

Providing Password Information for SRP

The default implementation of the SRPVerifierStore interface is not likely to be suitable for you production security environment as it requires all password hash information to be available as a file of serialized objects. You need to provide an MBean service that provides an implementation of the SRPVerifierStore interface that integrates with your existing security information stores. The SRPVerifierStore interface is shown in .

The SRPVerifierStore interface

package org.jboss.security.srp;

 

import java.io.IOException;

import java.io.Serializable;

import java.security.KeyException;

 

public interface SRPVerifierStore

{

public static class VerifierInfo implements Serializable

{

/** The username the information applies to. Perhaps redundant but it

makes the object self contained.

*/

public String username;

/** The SRP password verifier hash */

public byte[] verifier;

/** The random password salt originally used to verify the password */

public byte[] salt;

/** The SRP algorithm primitive generator */

public byte[] g;

/** The algorithm safe-prime modulus */

public byte[] N;

}

 

/** Get the indicated user's password verifier information.

*/

public VerifierInfo getUserVerifier(String username)

throws KeyException, IOException;

/** Set the indicated users' password verifier information. This is equivalent

to changing a user's password and should generally invalidate any existing

SRP sessions and caches.

*/

public void setUserVerifier(String username, VerifierInfo info)

throws IOException;

 

/** Verify an optional auxillary challenge sent from the client to the server.

The auxChallenge object will have been decrypted if it was sent encrypted from

the client. An example of a auxillary challenge would be the validation of a

hardware token (SafeWord, SecureID, iButton) that the server validates to

further strengthen the SRP password exchange.

*/

public void verifyUserChallenge(String username, Object auxChallenge)

throws SecurityException;

}

The primary function of a SRPVerifierStore implementation is to provide access to the SRPVerifierStore.VerifierInfo object for a given username. The getUserVerifier(String) method is called by the SRPService at that start of a user SRP session to obtain the parameters needed by the SRP algorithm. The elements of the VerifierInfo objects are:

  • username: The user's name or id used to login.
  • verifier: This is the one-way hash of the password or PIN the user enters as proof of their identity. The org.jboss.security.Util class has a calculateVerifier method that performs that password hashing algorithm. The output password H(salt | H(username | ':' | password)) as defined by RFC2945. Here H is the SHA secure hash function. The username is converted from a string to a byte[] using the UTF-8 encoding.
  • salt: This is a random number used to increase the difficulty of a brute force dictionary attack on the verifier password database in the event that the database is compromised. It is a value that should be generated from a cyrptographically strong random number algorithm when the user's existing clear-text password is hashed.
  • g:The SRP algorithm primitive generator. In general this can be a well known fixed parameter rather than a per-user setting. The org.jboss.security.srp.SRPConf utility class provides several settings for g including a good default which can obtained via SRPConf.getDefaultParams().g() .
  • N: The SRP algorithm safe-prime modulus . In general this can be a well known fixed parameter rather than a per-user setting. The org.jboss.security.srp.SRPConf utility class provides several settings for N including a good default which can obtained via SRPConf.getDefaultParams().N() .

So, step 1 of integrating your existing password store is the creation of a hashed version of the password information. If your passwords are already store in an irreversible hashed form, then this can only be done on a per-user basis as part of an upgrade proceedure for example. Note that the setUserVerifier(String, VerifierInfo) method is not used by the current SRPSerivce and may be implemented as noop method, or even one that throws an exception stating that the store is read-only.

Step 2 is the creation of the custom SRPVerifierStore interface implementation that knows how to obtain the VerifierInfo from the store you created in step 1. The verifyUserChallenge(String, Object) method of the interface is only called if the client SRPLoginModule configuration specifies the hasAuxChallenge option. This can be used to integrate existing hardware token based schemes like SafeWord or Radius into the SRP algorithm.

Step 3 is the creation of an MBean that makes the step 2 implementation of the SRPVerifierStore interface available via JNDI, and exposes any configurable parameters you need. In addition to the default org.jboss.security.srp.SRPVerifierStoreService example, the SRP example presented later in this chapter provides a Java properties file based SRPVerifierStore implementation. Between the two examples you should have enough to integrate your security store.

Inside of the SRP algorithm

The appeal of the SRP algorithm is that is allows for mutual authentication of client and server using simple text passwords without a secure communication channel. You might be wondering how this is done. See The SRP client-server authentication algorithm sequence diagram.. presents a sequence diagram of the authentication protocol as implemented by JBossSX.

FIGURE 8-11. The SRP client-server authentication algorithm sequence diagram.

The highlights of what is taking place for the key message exchanges presented in See The SRP client-server authentication algorithm sequence diagram.. are as follows. If you want the complete details and theory behind the algorithm, refer to the SRP references mentioned in a note earlier. There are six steps that are performed to complete authentication:

  1. The client side SRPLoginModule retrieves the SRPServerInterface instance for the remote authentication server from the naming service.
  2. The client side SRPLoginModule next requests the SRP parameters associated with the username attempting the login. There are a number of parameters involved in the SRP algorithm that must be chosen when the user password is first transformed into the verifier form used by the SRP algorithm. Rather than hard-coding the parameters (which could be done with minimal security risk), the JBossSX implementation allows a user to retrieve this information as part of the exchange protocol. The getSRPParameters(username) call retrieves the SRP parameters for the given username.
  3. The client side SRPLoginModule begins an SRP session by creating an SRPClientSession object using the login username, clear-text password, and SRP parameters obtained from step 2. The client then creates a random number A that will be used to build the private SRP session key. The client then initializes the server side of the SRP session by invoking the SRPServerInterface.init method and passes in the username and client generated random number A. The server returns its own random number B. This step corresponds to the exchange of public keys.
  4. The client side SRPLoginModule obtains the private SRP session key that has been generated as a result of the previous messages exchanges. This is saved as a private credential in the login Subject. The server challenge response M2 from step 4 is verified by invoking the SRPClientSession.verify method. If this succeeds, mutual authentication of the client to server, and server to client have been completed.The client side SRPLoginModule next creates a challenge M1 to the server by invoking SRPClientSession.response method passing the server random number B as an argument. This challenge is sent to the server via the SRPServerInterface.verify method and server's response is saved as M2. This step corresponds to an exchange of challenges. At this point the server has verified that the user is who they say they are.
  5. The client side SRPLoginModule saves the login username and M1 challenge into the LoginModule sharedState Map . This is used as the Principal name and credentials by the standard JBoss ClientLoginModule . The M1 challenge is used in place of the password as proof of identity on any method invocations on J2EE components. The M1 challenge is a cryptographically strong hash associated with the SRP session. Its interception via a third partly cannot be used to obtain the user's password.
  6. At the end of this authentication protocol, the SRPServerSession has been placed into the SRPService authentication cache for subsequent use by the SRPCacheLoginModule

Although SRP has many interesting properties, it is still an evolving component in the JBossSX framework and has some limitations of which you should be aware. Issues of note include the following:

  • Because of how JBoss detaches the method transport protocol from the component container where authentication is performed, an unauthorized user could snoop the SRP M1 challenge and effectively use the challenge to make requests as the associated username. Custom interceptors that encrypt the challenge using the SRP session key can be used to prevent this issue.
  • The SRPService maintains a cache of SRP sessions that time out after a configurable period. Once they time out, any subsequent J2EE component access will fail because there is currently no mechanism for transparently renegotiating the SRP authentication credentials. You must either set the authentication cache timeout very long (up to 2,147,483,647 seconds, or approximately 68 years), or handle re-authentication in your code on failure.
  • By default there can only be one SRP session for a given username. Because the negotiated SRP session produces a private session key that can be used for encryption/decryption between the client and server, the session is effectively a stateful one. Support for multiple SRP sessions per user has been added as of JBoss-3.0.3, but you cannot encrypt data with one session key and then decrypt it with another.

To use end-to-end SRP authentication for J2EE component calls, you need to configure the security domain under which the components are secured to use the org.jboss.security.srp.jaas.SRPCacheLoginModule . The SRPCacheLoginModule has a single configuration option named cacheJndiName that sets the JNDI location of the SRP authentication CachePolicy instance. This must correspond to the AuthenticationCacheJndiName attribute value of the SRPService MBean. The SRPCacheLoginModule authenticates user credentials by obtaining the client challenge from the SRPServerSession object in the authentication cache and comparing this to the challenge passed as the user credentials. See A sequence diagram illustrating the interaction of the SRPCacheLoginModule with the SRP session cache.. illustrates the operation of the SRPCacheLoginModule.login method implementation.

FIGURE 8-12. A sequence diagram illustrating the interaction of the SRPCacheLoginModule with the SRP session cache.
An SRP example

We have covered quite a bit of material on SRP and now its time to demonstrate SRP in practice with an example. The example demonstrates client side authentication of the user via SRP as well as subsequent secured access to a simple EJB using the SRP session challenge as the user credential. The test code deploys an ejb-jar that includes a sar for the configuration of the server side login module configuration and SRP services. As in the previous examples we will dynamically install the server side login module configuration using the SecurityConfig MBean. In this example we also use a custom implementation of the SRPVerifierStore interface that uses an in memory store that is seeded from a Java properties file rather than a serialized object store as used by the SRPVerifierStoreService . This custom service is org.jboss.chap8.ex3.service.PropertiesVerifierStore . See The chap8-ex3 jar contents. shows the contents of the jar that contains the example EJB and SRP services.

The chap8-ex3 jar contents

[orb@toki examples]$ java -cp output/classes ListJar output/chap8/chap8-ex3.jar

output/chap8/chap8-ex3.jar

+- META-INF/MANIFEST.MF

+- META-INF/ejb-jar.xml

+- META-INF/jboss.xml

+- org/jboss/chap8/ex3/Echo.class

+- org/jboss/chap8/ex3/EchoBean.class

+- org/jboss/chap8/ex3/EchoHome.class

+- roles.properties

+- users.properties

+- chap8-ex3.sar (archive)

| +- META-INF/MANIFEST.MF

| +- META-INF/jboss-service.xml

| +- META-INF/login-config.xml

| +- org/jboss/chap8/ex3/service/PropertiesVerifierStore$1.class

| +- org/jboss/chap8/ex3/service/PropertiesVerifierStore.class

| +- org/jboss/chap8/ex3/service/PropertiesVerifierStoreMBean.class

| +- org/jboss/chap8/service/SecurityConfig.class

| +- org/jboss/chap8/service/SecurityConfigMBean.class

The key SRP related items in this example are the SRP MBean services configuration, and the SRP login module configurations. The jboss-service.xml descriptor of the chap8-ex3.sar is given in See The chap8-ex3.sar jboss-service.xml descriptor for the SRP services., while See The chap8-ex3 client side and server side SRP login module configurations. gives the example client side and server side login module configurations.

The chap8-ex3.sar jboss-service.xml descriptor for the SRP services

<server>

<!-- The custom JAAS login configuration that installs

a Configuration capable of dynamically updating the

config settings

-->

<mbean code="org.jboss.chap8.service.SecurityConfig"

name="jboss.docs.chap8:service=LoginConfig-EX3">

<attribute name="AuthConfig">META-INF/login-config.xml</attribute>

<attribute name="SecurityConfigName">jboss.security:name=SecurityConfig</attribute>

</mbean>

<!-- The SRP service that provides the SRP RMI server and server side

authentication cache -->

<mbean code="org.jboss.security.srp.SRPService"

name="jboss.docs.chap8:service=SRPService">

<attribute name="VerifierSourceJndiName">srp-test/chap8-ex3</attribute>

<attribute name="JndiName">srp-test/SRPServerInterface</attribute>

<attribute name="AuthenticationCacheJndiName">srp-test/AuthenticationCache</attribute>

<attribute name="ServerPort">0</attribute>

<depends>jboss.docs.chap8:service=PropertiesVerifierStore</depends>

</mbean>

<!-- The SRP store handler service that provides the user password verifier

information -->

<mbean code="org.jboss.chap8.ex3.service.PropertiesVerifierStore"

name="jboss.docs.chap8:service=PropertiesVerifierStore">

<attribute name="JndiName">srp-test/chap8-ex3</attribute>

</mbean>

</server>

The chap8-ex3 client side and server side SRP login module configurations

// Client side standard JAAS configuration fragment

srp {

org.jboss.security.srp.jaas.SRPLoginModule required

srpServerJndiName="srp-test/SRPServerInterface"

;

org.jboss.security.ClientLoginModule required

password-stacking="useFirstPass"

;

};

// Server side XMLLoginConfig configuration fragment

<application-policy name = "chap8-ex3">

<authentication>

<login-module code = "org.jboss.security.srp.jaas.SRPCacheLoginModule"

flag = "required">

<module-option name = "cacheJndiName">srp-test/AuthenticationCache</module-option>

</login-module>

<login-module code = "org.jboss.security.auth.spi.UsersRolesLoginModule"

flag = "required">

<module-option name = "password-stacking">useFirstPass</module-option>

</login-module>

</authentication>

</application-policy>

The example services are the ServiceConfig and the PropertiesVerifierStore and SRPService MBeans. Note that the JndiName attribute of the PropertiesVerifierStore is equal to the VerifierSourceJndiName attribute of the SRPService , and that the SRPService depends on the PropertiesVerifierStore . This is required because the SRPService needs an implementation of the SRPVerifierStore interface for accessing user password verification information.

The client side login module configuration of See The chap8-ex3 client side and server side SRP login module configurations. makes use of the SRPLoginModule with a srpServerJndiName option value that corresponds to the JBoss server component SRPService JndiName attribute value("srp-test/SRPServerInterface"). Also needed is the ClientLoginModule configured with the password-stacking="useFirstPass" value to propagate the user authentication credentials generated by the SRPLoginModule to the EJB invocation layer.

There are two issues to note about the server side login module configuration. First, note the cacheJndiName=srp-test/AuthenticationCache configuration option tells the SRPCacheLoginModule the location of the CachePolicy that contains the SRPServerSession for users who have authenticated against the SRPService . This value corresponds to the SRPService AuthenticationCacheJndiName attribute value. Second, the configuration includes a UsersRolesLoginModule with the password-stacking=useFirstPass configuration option. It is required to use a second login module with the SRPCacheLoginModule because SRP is only an authentication technology. A second login module needs to be configured that accepts the authentication credentials validated by the SRPCacheLoginModule to set the principal's roles that determines the principal's permissions. The UsersRolesLoginModule is augmenting the SRP authentication with properties file based authorization. The user's roles are coming the roles.properties file included in the EJB jar.

Now, run the example 3 client by executing the following command from the book examples directory:

[starksm@banshee examples]$ ant -Dchap=chap8 -Dex=3 run-example

Buildfile: build.xml

...

run-example3:

[copy] Copying 1 file to /tmp/jboss-3.2.3/server/default/deploy

[echo] Waiting for 5 seconds for deploy...

[java] Logging in using the 'srp' configuration

[java] Created Echo

[java] Echo.echo()#1 = This is call 1

[java] Echo.echo()#2 = This is call 2

In the examples/logs directory you will find a file called ex3-trace.log. This is a detailed trace of the client side of the SRP algorithm. The traces show step-by-step the construction of the public keys, challenges, session key and verification.

Note that the client has taken a long time to run relative to the other simple examples. The reason for this is the construction of the client's public key. This involves the creation of a cryptographically strong random number, and this process takes quite a bit of time the first time it occurs. If you were to log out and log in again within the same VM, the process would be much faster. Also note that "Echo.echo()#2" fails with an Authentication exception. The client code sleeps for 15 seconds after making the first call to demonstrate the behavior of the SRPService cache expiration. The SRPService cache policy timeout has been set to a mere 10 seconds to force this issue. As stated earlier, you need to make the cache timeout very long, or handle re-authentication on failure.

Running JBoss with a Java 2 security manager

By default the JBoss server does not start with a Java 2 security manager. If you want to restrict privileges of code using Java 2 permissions you need to configure the JBoss server to run under a security manager. This is done by configuring the Java VM options in the run.bat or run.sh scripts in the JBoss server distribution bin directory. The two required VM options are as follows:

See The modifications to the Win32 run.bat start script to run JBoss with a Java 2 security manager.. illustrates a fragment of the standard run.bat start script for Win32 that shows the addition of these two options to the command line used to start JBoss.

The modifications to the Win32 run.bat start script to run JBoss with a Java 2 security manager.

...

set CONFIG=%1

@if "%CONFIG%" == "" set CONFIG=default

set PF=../conf/%CONFIG%/server.policy

set OPTS=-Djava.security.manager

set OPTS=%OPTS% -Djava.security.policy=%PF%

echo JBOSS_CLASSPATH=%JBOSS_CLASSPATH%

java %JAXP% %OPTS% -classpath "%JBOSS_CLASSPATH%" org.jboss.Main %*

See The modifications to the UNIX/Linux run.sh start script to run JBoss with a Java 2 security manager.. shows a fragment of the standard run.sh start script for UNIX/Linux systems that shows the addition of these two options to the command line used to start JBoss.

The modifications to the UNIX/Linux run.sh start script to run JBoss with a Java 2 security manager.

...

CONFIG=$1

if [ "$CONFIG" == "" ]; then CONFIG=default; fi

PF=../conf/$CONFIG/server.policy

OPTS=-Djava.security.manager

OPTS="$OPTS -Djava.security.policy=$PF"

echo JBOSS_CLASSPATH=$JBOSS_CLASSPATH

java $HOTSPOT $JAXP $OPTS -classpath $JBOSS_CLASSPATH org.jboss.Main $@

Both start scripts are setting the security policy file to the server.policy file located in the JBoss configuration file set directory that corresponds to the configuration name passed as the first argument to the script. This allows one maintain a security policy per configuration file set without having to modify the start script.

Enabling Java 2 security is the easy part. The difficult part of Java 2 security is establishing the allowed permissions. If you look at the server.policy file that is contained in the default configuration file set, you'll see that it contains the following permission grant statement:

grant {

// Allow everything for now

permission java.security.AllPermission;

};

This effectively disables security permission checking for all code as it says any code can do anything, which is not a reasonable default. What is a reasonable set of permissions is entirely up to you.

The current set of JBoss specific java.lang.RuntimePermissions that are required include:

 

TABLE 8-1.

TargetName

What the permission allows

Risks

org.jboss.security.SecurityAssociation.getPrincipalInfo

Access to the org.jboss.security.SecurityAssociation getPrincipal() and getCredentials() methods.

The ability to see the current thread caller and credentials.

org.jboss.security.SecurityAssociation.setPrincipalInfo

Access to the org.jboss.security.SecurityAssociation setPrincipal() and setCredentials() methods.

The ability to set the current thread caller and credentials.

org.jboss.security.SecurityAssociation.setServer

Access to the org.jboss.security.SecurityAssociation setServer method.

The ability to enable or disable multithread storage of the caller principal and credential.

org.jboss.security.SecurityAssociation.setRunAsRole

Access to the org.jboss.security.SecurityAssociation pushRunAsRole and popRunAsRole methods.

The ability to change the current caller run-as role principal.

To conclude this discussion, here is a little-known tidbit on debugging security policy settings. There are various debugging flag that you can set to determine how the security manager is using your security policy file as well as what policy files are contributing permissions. Running the VM as follows shows the possible debugging flag settings:

[nr@toki bin]$ java -Djava.security.debug=help

 

all turn on all debugging

access print all checkPermission results

combiner SubjectDomainCombiner debugging

jar jar verification

logincontext login context results

policy loading and granting

provider security provider debugging

scl permissions SecureClassLoader assigns

 

The following can be used with access:

 

stack include stack trace

domain dumps all domains in context

failure before throwing exception, dump stack

and domain that didn't have permission

 

Note: Separate multiple options with a comma

Running with -Djava.security.debug=all provides the most output, but the output volume is torrential. This might be a good place to start if you don't understand a given security failure at all. A less verbose setting that helps debug permission failures is to use -Djava.security.debug=access,failure. This is still relatively verbose, but not nearly as bad as the all mode as the security domain information is only displayed on access failures.

Using SSL with JBoss using JSSE

JBoss uses JSSE the Java Secure Socket Extension (JSSE) . JSSE is bundled with JBoss and it comes with JDK 1.4. For more information on JSSE see: http://java.sun.com/products/jsse/index.html . A simple test that you can use the JSSE as bundled with JBoss works is to run a program like the following:

import java.net.*;

import javax.net.ServerSocketFactory;

import javax.net.ssl.*;

public class JSSE_install_check

{

public static void main(String[] args) throws Exception

{

Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());

ServerSocketFactory factory =

SSLServerSocketFactory.getDefault();

SSLServerSocket sslSocket = (SSLServerSocket)

factory.createServerSocket(12345);

String [] cipherSuites = sslSocket.getEnabledCipherSuites();

for(int i = 0; i < cipherSuites.length; i++)

{

System.out.println("Cipher Suite " + i +

" = " + cipherSuites[i]);

}

}

}

The book examples includes a testcase for this which can be run using the following command. This will produce a lot of output as the -Djavax.net.debug=all option is passed to the VM.

[nr@toki examples]$ ant -Dchap=chap8 -Dex=4a run-example

Buildfile: build.xml

...

run-example4a:

[echo] Testing JSSE availablility

[java] keyStore is :

[java] keyStore type is : jks

[java] init keystore

[java] init keymanager of type SunX509

[java] trustStore is: /System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Home/lib/security/cacerts

[java] trustStore type is : jks

[java] init truststore

...

[java] init context

[java] trigger seeding of SecureRandom

[java] done seeding SecureRandom

[java] Cipher Suite 0 = SSL_RSA_WITH_RC4_128_MD5

[java] Cipher Suite 1 = SSL_RSA_WITH_RC4_128_SHA

[java] Cipher Suite 2 = TLS_RSA_WITH_AES_128_CBC_SHA

[java] Cipher Suite 3 = TLS_DHE_RSA_WITH_AES_128_CBC_SHA

[java] Cipher Suite 4 = TLS_DHE_DSS_WITH_AES_128_CBC_SHA

[java] Cipher Suite 5 = SSL_RSA_WITH_3DES_EDE_CBC_SHA

[java] Cipher Suite 6 = SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA

[java] Cipher Suite 7 = SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA

[java] Cipher Suite 8 = SSL_RSA_WITH_DES_CBC_SHA

[java] Cipher Suite 9 = SSL_DHE_RSA_WITH_DES_CBC_SHA

[java] Cipher Suite 10 = SSL_DHE_DSS_WITH_DES_CBC_SHA

[java] Cipher Suite 11 = SSL_RSA_EXPORT_WITH_RC4_40_MD5

[java] Cipher Suite 12 = SSL_RSA_EXPORT_WITH_DES40_CBC_SHA

[java] Cipher Suite 13 = SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA

[java] Cipher Suite 14 = SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA

The JSSE jars include the jcert.jar, jnet.jar and jsse.jar in the JBOSS_DIST/client directory.

Once you have tested that JSSE runs, you need a public key/private key pair in the form of an X509 certificate for use by the SSL server sockets. For the purpose of this example we have created a self-signed certificate using the JDK keytool and included the resulting keystore file in the chap8 source directory as chap8.keystore. It was created using the following command and input:

[nr@toki examples]$ keytool -genkey -alias rmi+ssl -keyalg RSA -keystore chap8.keystore -validity 3650

[orb@toki examples]$ keytool -genkey -alias rmi+ssl -keyalg RSA -keystore chap8.keystore -validity 3650

Enter keystore password: rmi+ssl

What is your first and last name?

[Unknown]: Chapter 8 SSL Example

What is the name of your organizational unit?

[Unknown]: JBoss Book

What is the name of your organization?

[Unknown]: JBoss, Inc.

What is the name of your City or Locality?

[Unknown]: Issaquah

What is the name of your State or Province?

[Unknown]: WA

What is the two-letter country code for this unit?

[Unknown]: US

Is CN=Chapter 8 SSL Example, OU=JBoss Book, O="JBoss, Inc.", L=Issaquah, ST=WA, C=US correct?

[no]: yes

 

Enter key password for <rmi+ssl>

(RETURN if same as keystore password):

This produces a keystore file called chap8.keystore. A keystore is a database of security keys. There are two different types of entries in a keystore:

Listing the src/main/org/jboss/chap8/chap8.keystore examples file contents using the keytool shows one self-signed certificate:

[nr@toki examples]$ keytool -list -v -keystore src/main/org/jboss/chap8/chap8.keystore

Enter keystore password: rmi+ssl

 

Keystore type: jks

Keystore provider: SUN

 

Your keystore contains 1 entry

 

Alias name: rmi+ssl

Creation date: Nov 8, 2001

Entry type: keyEntry

Certificate chain length: 1

Certificate[1]:

Owner: CN=Chapter8 SSL Example, OU=JBoss Book, O="JBoss Group, LLC", L=Issaquah, ST=WA, C=US

Issuer: CN=Chapter8 SSL Example, OU=JBoss Book, O="JBoss Group, LLC", L=Issaquah, ST=WA, C=US

Serial number: 3beb5271

Valid from: Thu Nov 08 21:50:09 CST 2001 until: Sun Nov 06 21:50:09 CST 2011

Certificate fingerprints:

MD5: F6:1B:2B:E9:A5:23:E7:22:B2:18:6F:3F:9F:E7:38:AE

SHA1: F2:20:50:36:97:86:52:89:71:48:A2:C3:06:C8:F9:2D:F7:79:00:36

 

 

*******************************************

*******************************************

With JSSE working and a keystore with the certificate you will use for the JBoss server, your are ready to configure JBoss to use SSL for EJB access. This is done by configuring the EJB invoker RMI socket factories. The JBossSX framework includes implementations of the java.rmi.server.RMIServerSocketFactory and java.rmi.server.RMIClientSocketFactory interfaces that enable the use of RMI over SSL encrypted sockets. The implementation classes are org.jboss.security.ssl.RMISSLServerSocketFactory and org.jboss.security.ssl.RMISSLClientSocketFactory respectively. There are two steps to enable the use of SSL for RMI access to EJBs. The first is to enable the use of a keystore as the database for the SSL server certificate, which is done by configuring an org.jboss.security.plugins.JaasSecurityDomain MBean. The jboss-service.xml descriptor in the chap8/ex4 directory includes the JaasSecurityDomain definition shown in See A sample JaasSecurityDomain config for RMI/SSL..

A sample JaasSecurityDomain config for RMI/SSL

<!-- The SSL domain setup -->

<mbean code="org.jboss.security.plugins.JaasSecurityDomain"

name="jboss.security:service=JaasSecurityDomain,domain=RMI+SSL">

<constructor>

<arg type="java.lang.String" value="RMI+SSL"/>

</constructor>

<attribute name="KeyStoreURL">chap8.keystore</attribute>

<attribute name="KeyStorePass">rmi+ssl</attribute>

</mbean>

The JaasSecurityDomain is a subclass of the standard JaasSecurityManager class that adds the notions of a keystore as well JSSE KeyManagerFactory and TrustManagerFactory access. It extends the basic security manager to allow support for SSL and other cryptographic operations that require security keys. This configuration simply loads the chap8.keystore from the example 4 MBean sar using the indicated password.

The second step is to define an EJB invoker configuration that uses the JBossSX RMI socket factories that support SSL. To do this you need to define a custom configuration for the JRMPInvoker we saw in Chapter EJBs on JBoss - The EJB Container Configuration and Architecture , as well as an EJB setup that makes use of this invoker. The configuration required to enable RMI over SSL access to stateless session bean is provided for you in See The jboss-service.xml and jboss.xml configurations to enable SSL with the example 4 stateless session bean... The top of the listing shows the jboss-service.xml descriptor that defines the custom JRMPInovker , and the bottom shows the example 4 "EchoBean4" configuration needed to use the SSL invoker. You will use this configuration in a stateless session bean example.

The jboss-service.xml and jboss.xml configurations to enable SSL with the example 4 stateless session bean.

// The jboss-service.xml SSL JRMPInvoker MBean Configuration

<mbean code="org.jboss.invocation.jrmp.server.JRMPInvoker"

name="jboss:service=invoker,type=jrmp,socketType=SSL">

<attribute name="RMIObjectPort">14445</attribute>

<attribute name="RMIClientSocketFactory">

org.jboss.security.ssl.RMISSLClientSocketFactory

</attribute>

<attribute name="RMIServerSocketFactory">

org.jboss.security.ssl.RMISSLServerSocketFactory

</attribute>

<attribute name="SecurityDomain">java:/jaas/RMI+SSL</attribute>

<depends>jboss.security:service=JaasSecurityDomain,domain=RMI+SSL</depends>

</mbean>

// The jboss.xml session bean configuration to use the SSL invoker

<?xml version="1.0"?>

<jboss>

<enterprise-beans>

<session>

<ejb-name>EchoBean4</ejb-name>

<configuration-name>Standard Stateless SessionBean</configuration-name>

<home-invoker>jboss:service=invoker,type=jrmp,socketType=SSL

</home-invoker>

<bean-invoker>jboss:service=invoker,type=jrmp,socketType=SSL

</bean-invoker>

</session>

</enterprise-beans>

</jboss>

The example 4 code is located under the src/main/org/jboss/chap8/ex4 directory of the book examples. This is another simple stateless session bean with an echo method that returns its input argument. It is hard to tell when SSL is in use unless it fails, so we'll run the example 4 client in two different ways to demonstrate that the EJB deployment is in fact using SSL. Start the JBoss server using the default configuration and then run example 4b as follows:

[nr@toki examples]$ ant -Dchap=chap8 -Dex=4b run-example

Buildfile: build.xml

...

run-example4b:

[copy] Copying 1 file to /tmp/jboss-3.2.3/server/default/deploy

[echo] Waiting for 15 seconds for deploy...

[java] Exception in thread "main" java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is:

[java] javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found

[java] at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:274)

[java] at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:171)

[java] at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:101)

[java] at org.jboss.invocation.jrmp.server.JRMPInvoker_Stub.invoke(Unknown Source)

[java] at org.jboss.invocation.jrmp.interfaces.JRMPInvokerProxy.invoke(JRMPInvokerProxy.java:135)

[java] at org.jboss.invocation.InvokerInterceptor.invoke(InvokerInterceptor.java:96)

[java] at org.jboss.proxy.TransactionInterceptor.invoke(TransactionInterceptor.java:46)

[java] at org.jboss.proxy.SecurityInterceptor.invoke(SecurityInterceptor.java:45)

[java] at org.jboss.proxy.ejb.HomeInterceptor.invoke(HomeInterceptor.java:173)

[java] at org.jboss.proxy.ClientContainer.invoke(ClientContainer.java:85)

[java] at $Proxy0.create(Unknown Source)

[java] at org.jboss.chap8.ex4.ExClient.main(ExClient.java:31)

[java] Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found

[java] at com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.a(DashoA6275)

...

[java] Caused by: sun.security.validator.ValidatorException: No trusted certificate found

[java] at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)

[java] at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)

[java] at sun.security.validator.Validator.validate(Validator.java:202)

[java] at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(DashoA6275)

[java] at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(DashoA6275)

[java] ... 22 more

[java] Java Result: 1

 

BUILD SUCCESSFUL

Total time: 19 seconds

The resulting exception is expected, and is the purpose of the 4b version of the example. Note that the exception stack trace has been edited to fit into the book format, so expect some difference. The key item to notice about the exception is it clearly shows you are using the Sun JSSE classes to communicate with the JBoss EJB container. The exception is saying that the self-signed certificate you are using as the JBoss server certificate cannot be validated as signed by any of the default certificate authorities. This is expected because the default certificate authority keystore that ships with the JSSE package only includes well known certificate authorities such as VeriSign, Thawte, and RSA Data Security. To get the EJB client to accept your self-signed certificate as valid, you need to tell the JSSE classes to use your chap8.keystore as its truststore. A truststore is just a keystore that contains public key certificates used to sign other certificates. To do this, run example 4 using -Dex=4 rather than -Dex=4b to pass the location of the correct truststore using the javax.net.ssl.trustStore system property:

[nr@toki examples]$ ant -Dchap=chap8 -Dex=4 run-example

Buildfile: build.xml

...

run-example4:

[copy] Copying 1 file to /tmp/jboss-3.2.3/server/default/deploy

[echo] Waiting for 5 seconds for deploy...

[java] 1 [HandshakeCompletedNotify-Thread] DEBUG org.jboss.security.ssl.RMISSLClientSocketFactory - SSL handshakeCompleted, cipher=SSL_RSA_WITH_RC4_128_MD5, peerHost=127.0.0.1

[java] Created Echo

[java] Echo.echo()#1 = This is call 1

 

BUILD SUCCESSFUL

Total time: 15 seconds

This time the only indication that an SSL socket is involved is because of the "SSL handshakeCompleted" message. This is coming from the RMISSLClientSocketFactory class as a debug level log message. If you did not have the client configured to print out log4j debug level messages, there would be no direct indication that SSL was involved. If you note the run times and the load on your system CPU, there definitely is a difference. SSL, like SRP, involves the use of cryptographically strong random numbers that take time to seed the first time they are used. This shows up as high CPU utilization and start up times.

One consequence of this is that if you are running on a system that is slower than the one used to run the examples for the book, such as when running example 4b, you may seen an exception similar to the following:

javax.naming.NameNotFoundException: EchoBean4 not bound

at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer

at sun.rmi.transport.StreamRemoteCall.executeCall

at sun.rmi.server.UnicastRef.invoke

at org.jnp.server.NamingServer_Stub.lookup

at org.jnp.interfaces.NamingContext.lookup

at org.jnp.interfaces.NamingContext.lookup

at javax.naming.InitialContext.lookup

at org.jboss.chap8.ex4.ExClient.main(ExClient.java:29)

Exception in thread "main"

Java Result: 1

The problem is that the JBoss server has not finished deploying the example EJB in the time the client allowed. This is due to the initial setup time of the secure random number generator used by the SSL server socket. If you see this issue, simply rerun the example again or increase the deployment wait time in the chap8 build.xml Ant script.

Configuring JBoss for use Behind a Firewall

JBoss comes with many socket based services that open listening ports. In this section we list the services that open ports that might need to be configured to work when accessing JBoss behind a firewall. The following table See The ports found in the default configuration., shows the ports, socket type, associated service and link to the service configuration for the services in the default configuration file set. See Additional ports in the all configuration. shows the same information for the additional ports that exist in the all configuration file set.

The ports found in the default configuration

Port

Type

Service

Reference

1099

TCP

org.jboss.naming.NamingService

The JBossNS Architecture

1098

TCP

org.jboss.naming.NamingService

The JBossNS Architecture

1162

UDP

org.jboss.jmx.adaptor.snmp.trapd.TrapdService

The Event to Trap Service

4444

TCP

org.jboss.invocation.jrmp.server.JRMPInvoker

The JRMPInvoker - RMI/JRMP Transport

4445

TCP

org.jboss.invocation.pooled.server.PooledInvoker

The PooledInvoker - RMI/Socket Transport

8009

TCP

org.jboss.web.tomcat.tc4.EmbeddedTomcatService

Connector

8080

TCP

org.jboss.web.tomcat.tc4.EmbeddedTomcatService

Connector

8083

TCP

org.jboss.web.WebService

RMI Dynamic Class Loading

8090

TCP

org.jboss.mq.il.oil.OILServerILService

org.jboss.mq.il.oil.OILServerILService (deprecated)

8092

TCP

org.jboss.mq.il.oil2.OIL2ServerILService

None

8093

TCP

org.jboss.mq.il.uil2.UILServerILService

org.jboss.mq.il.uil2.UILServerILService

02

TCP

org.jboss.mq.il.rmi.RMIServerILService

org.jboss.mq.il.rmi.RMIServerILService (deprecated)

03

UDP

org.jboss.jmx.adaptor.snmp.agent.SnmpAgentService

The SNMP Adaptor Service

 

Additional ports in the all configuration

Port

Type

Service

Reference

1100

TCP

org.jboss.ha.jndi.HANamingService

Naming Discovery in Clustered Environments

04

TCP

org.jboss.ha.jndi.HANamingService

Naming Discovery in Clustered Environments

1102

UDP

org.jboss.ha.jndi.HANamingService

Naming Discovery in Clustered Environments

3528

TCP

org.jboss.invocation.iiop.IIOPInvoker

The IIOPInvoker - RMI/IIOP Transport

455665

UDP

org.jboss.ha.framework.server.ClusterPartition

See the Clustering Guide book

Howto Secure the JBoss Server

JBoss comes with several admin access points that need to be secured or removed to prevent unauthorized access to admin functions in a deployment. This section describes the various admin services and how to secure them.

The jmx-console.war

The jmx-console.war found in the deploy directory provides an html view into the JMX microkernel. As such, it provides access to arbitrary admin type access like shutting down the server, stopping services, deploying new services, etc. It should either be secured like any other web application, or removed.

The web-console.war

The web-console.war found in the deploy/management directory is another web application view into the JMX microkernel. This uses a combination of an applet and a html view and provides the same level of access to admin functionality as the jmx-console.war. As such, it should either be secured or removed. The web-console.war contains commented out templates for basic security in its WEB-INF/web.xml as well as commented out setup for a security domain in WEB-INF/jboss-web.xml.

The http-invoker.sar

The http-invoker.sar found in the deploy directory is a service that provides RMI/HTTP access for EJBs and the JNDI Naming service. This includes a servlet that processes posts of marshalled org.jboss.invocation.Invocation objects that represent invocations that should be dispatched onto the MBeanServer. Effectively this allows access to MBeans that support the detached invoker operation via HTTP since one could figure out how to format an approriate HTTP post. To security this access point you would need to secure the JMXInvokerServlet servlet found in the http-invoker.sar/invoker.war/WEB-INF/web.xml descriptor. There is a secure mapping defined for the /restricted/JMXInvokerServlet path by default, one would simply have to remove the other paths and configure the http-invoker security domain setup in the http-invoker.sar/invoker.war/WEB-INF/jboss-web.xml descriptor.

The jmx-invoker-adaptor-server.sar

The jmx-invoker-adaptor-server.sar is a service that exposes the JMX MBeanServer interface via an RMI compatible interface using the RMI/JRMP detached invoker service. The only way for this service to be secured currently would be to switch the protocol to RMI/HTTP and secure the http-invoker.sar as described in the previous section. In the future this service will be deployed as an XMBean with a security interceptor that supports role based access checks. If your so inclined this is a configuration that can setup today following the proceedure demonstrated in XMBean example: Version 3, Adding Security and Remote Access to the JNDIMap XMBean.

 


1. The ProxyLoginModule is generally not needed in JBoss3.x since we have our own JAAS implementation that solves this issue, and the JDK1.4 JAAS implementation behaves in the same way. The ProxyLoginModule remains for backward compatability.

2. This service binds to an anonymous TCP port and does not support configuration of the port or bind interface.

3. This service binds to an anonymous UDP port and does not support configuration of the port or bind interface.

4. Currently anonymous but can be set via the RmiPort attribute.

5. Plus two additional anonymous UDP ports, one can be set using the rcv_port, and the other cannot be set.


© 2002-2004 JBoss Inc. All rights reserved.