Hello Example

Introduction

The Hello example was created to illustrate how a typical Jini technology-based application can be configured and optionally secured using the configuration and Jini extensible remote invocation (Jini ERI) facilities that arrived in version 2.0 of the Jini Technology Starter Kit . The components built from the files making up this example are a server component that implements a Hello interface with a sayHello() method, and a client component that employs a lookup service to obtain a reference to the server through which the client invokes the sayHello() method.

One of the primary purposes of this example is to demonstrate how an application can be written in such a way that it needs to be built only once, but can be run to use various communication protocols and security policies by merely deploying under different configurations without having to change a single line of Java(TM) programming language source code! Thus, although there is only a single example presented in this document, there are a number of scenarios under which this example can be run. The first part of this document provides instructions for getting started quickly, and the second part provides more detailed descriptions of each scenario.

The combinations of ways in which the components of this simple example can be configured are derived from options like the following:

When run under any of the configurations provided in this example, the application simply says, "Hello, world!".

This example also demonstrates the use of the Configuration, Exporter, and ProxyPreparer interfaces to configure the client, server, lookup service, and activation system (Phoenix) for the desired remote communication and security. Additionally, references to the source files for the application as well as the various configuration and policy files that can be used to run the application in different ways are also presented.

To aid in working with the example, this page is divided into the following sections:

Although running the example under the secure configurations requires a little bit of one-time setup, you should be able to run each of the non-secure configurations "out of the box", with no setup at all. For those who wish to jump right in, the quick start section presents the basic steps necessary to run the example under each of the possible configurations without a lot of additional explanation. That section also contains a convenient table that presents the various scripts one can use to run the example. In that table, the scripts are organized in a fashion that is intended to make it obvious which scripts should be executed for a particular configuration.

If, after acquiring some experience with running the example, you would like to experiment with modifying the example's source code, the section on making changes presents a set of steps that can be followed to rebuild the example to include those changes. Should problems be encountered while running the example, a small of set of troubleshooting tips are presented that may be helpful in diagnosing and solving some of the more common problems. Finally, for those who desire a deeper understanding and are looking for a starting point for designing and configuring applications, the contents of the source code, configuration files, and policy files employed in this example can be explored by accessing the references contained in the section that takes a deeper look.


Setup (Secure Configurations Only)

To run this example under the secure configurations that are provided, one can choose to use either the Secure Sockets Layer Protocol (SSL) as provided by the Java Secure Socket Extension (JSSE), or the Kerberos provider through the Java GSS-API. Additionally, when run under any of the secure configurations, this example exploits the dynamic permission granting features of the security policy provider included in the Apache River release.

SSL setup

No setup is necessary to run this example when configured to use SSL through JSSE. This is because the example comes with a set of pre-generated private keystores with associated passwords and self-signed public key certificates (certificates), as well as a public keystore (truststore) that contains the certificate from each of the private keystores. Whereas each keystore is intended to represent private information associated with a particular component of the example, the truststore file that aggregates all of the public key certificates is treated as public information, shared by each component. Using this pre-generated data, the various SSL configurations can be run out of the box, with no setup required.

Please note that the keystores, truststore, and associated certificates are provided only for convenience. Using those items in anything but an example environment could pose a security risk because all parties who obtain this example have access to the same information; in particular, the same set of pre-generated private keys and passwords. To address any concerns one might have regarding this situation, the pre-generated keystores, password files, and truststore can be replaced with new versions in which the old private keys and passwords are replaced with different values that you supply, and the associated certificates are replaced with new certificates that are either self-signed or are signed by a valid certificate authority (CA). To generate your own private keys, as well as to create your own self-signed certificates, you can use the keytool provided in the JDK(TM) software.

Kerberos setup

Unlike the SSL configurations that are provided, some setup must be performed to run this example under the configurations that use Kerberos through the Java GSS-API.

A Kerberos environment typically includes not only the clients and servers wishing to securely communicate with each other, but also an authentication server referred to as a Key Distribution Center, or KDC. The KDC runs on a KDC host and belongs to a realm. Each client and server - referred to as Kerberos servers for the purposes of this discussion - is associated with a Kerberos principal name as well as a key. Using its principal name, each Keberos server retrieves its associated key from a keytab file and uses that key to request the services of the authentication server running on the KDC host. This example assumes that either a new or pre-existing authentication server, with the appropriate principal names, is operational in the environment in which the example runs.

In addition to an authentication server, the following steps must be taken to set up this example to run under the configurations that use Kerberos:

For more information on Kerberos and JAAS, see the Kerberos tutorial. For instructions on how to create keytab files and TGT caches, see krb-setup.html.

Installing the security policy provider for dynamic permission granting

In order to enable dynamic permission granting, the security policy provider included in the Apache River release must be installed as an extension in the Java 2 SDK (or JRE) used to run this example. Although the top level documentation of the Apache River release discusses two different strategies for installing the policy provider, the strategy that this example requires is the strategy in which the JAR file jsk-policy.jar is copied into the Java extensions directory; specifically:

  1. Locate the jsk-policy.jar file in the lib-ext directory of the Apache River release.
  2. Locate the jre/lib/ext directory inside your Java 2 SDK (or JRE) distribution.
  3. Copy the jsk-policy.jar file from the Apache River release to the jre/lib/ext directory as located in step 2.

After following the steps outlined above, if the virtual machine for the Java platform associated with the ext directory is available in your path, then the features provided by the security policy provider included in the Apache River release will be available when running the example.


Quick Start

This example can be run on a single machine or distributed among multiple machines. The client, server, and lookup service can each be run on separate machines if desired, but the activatable form of the server and the activation system (Phoenix) must run on the same machine. Note also that when the example is distributed among multiple machines, a class server must be run on each machine in the system. For simplicity, the instructions below will assume the example is run in separate command windows on a single machine.

To start, open five command windows, one for each of the following:

  1. httpd
  2. phoenix
  3. lookup service
  4. server
  5. client
That is, the instructions below assume the class server will be run in window a., Phoenix in window b., the lookup service in window c., etc. Note also that the instructions assume the operating system is UNIX, Linux, or OS X. If running on Windows, simply replace the file separator '/' with '\', and either remove the UNIX shell script suffix '.sh', or replace it with the Windows command file suffix '.bat'. For example, based on the instructions below, to start the lookup service using JRMP on UNIX, Linux, or OS X, one would type the following at the prompt ('>>') in the c. window,
  c. >> scripts/jrmp-reggie.sh
whereas on Windows, one would type one of the following:
  c. >> scripts\jrmp-reggie
  c. >> scripts\jrmp-reggie.bat
Upon opening the five command windows described above, in each window, change the directory to the hello directory. That is,
  a. >> cd <riverDir>/examples/hello
  b. >> cd <riverDir>/examples/hello
  c. >> cd <riverDir>/examples/hello
  d. >> cd <riverDir>/examples/hello
  e. >> cd <riverDir>/examples/hello

Running the class server

To run this example under any of the possible configurations, a single class server should be started to serve up files from both the example's lib directory and the Apache River release lib-dl directory. The class server provided in the Apache River release is used for this purpose. Note that once started, there is no need to shut down the class server (cntrl-c) until it is no longer needed. This is because that single class server can be shared by each configuration. To start the class server, type the following in the a. command window:
  a. >> scripts/httpd.sh

Running the configurations

This example presents eleven different configurations that can be run. The primary characteristic that distinguishes one configuration from another is the protocol used for communication by the example's server application; that is,
  1. JRMP
  2. Jini ERI (JERI)
  3. SSL (JSSE)
  4. Kerberos (GSS)
In some cases, the protocols can be mixed. For example, in each of the non-secure configurations, the lookup service uses only JRMP for its remote communication, even when the server and the client use Jini ERI. For the secure configurations, all parties must use the same communication protocol; that is, SSL or Kerberos.

For each primary characteristic, there are secondary characteristics associated with a particular configuration that further distinguishes one configuration from the others. As with the primary characteristic, the secondary characteristics are generally related to behavior demonstrated by the example's server. For example, is the server activatable or non-activatable? Are remote calls confirmed or not?

The quick start instructions in the table below are organized and presented based on these primary and secondary characteristics. Each row represents one of the configurations under which the example may be run. The column labelled Execution presents the scripts that must be executed to run the example under that particular configuration. The column labelled Upon Completion presents what must be done to "clean up" and prepare for executing another configuration. The indicated letters refer to the window in which the associated script or command should be executed.

Note that with respect to the username that must be provided in the Kerberos configurations, as well as the password that is required by both the Kerberos and the SSL configurations, the values shown in the table do not necessarily represent the actual values that must be input by the user. The password that the table shows for the SSL configurations is the same as the password used when generating the private keystore supplied with this example for the client component. Thus, as long as the client's keystore is not replaced with a new keystore with a different password, the password shown below can be used as shown.

With respect to the Kerberos configurations, the actual values to input for the username and password, are the Kerberos principal name and corresponding password that are registered with the particular Kerberos authentication server (KDC) that will be used when running the example; and which are associated with the client component through the krb-setenv.sh script (or krb-setenv.bat command file).

Running Each Configuration of the Hello Example
ID Distinguishing Characteristic(s) Execution Upon Completion
A-1 JRMP server c. >> scripts/jrmp-reggie.sh
d. >> scripts/jrmp-server.sh
e. >> scripts/client.sh
d. >> cntrl-c
c. >> cntrl-c
B-2 Jini ERI server c. >> scripts/jrmp-reggie.sh
d. >> scripts/jeri-server.sh
e. >> scripts/client.sh
d. >> cntrl-c
c. >> cntrl-c
B-3 Jini ERI server

client displays call-confirming dialog box
server displays call-confirming dialog box
c. >> scripts/jrmp-reggie.sh
d. >> scripts/confirming-jeri-server.sh
e. >> scripts/client.sh

click "OK" in dialog displayed by client
click "OK" in dialog displayed by server
d. >> cntrl-c
c. >> cntrl-c
B-4 Activatable Jini ERI server
JRMP Phoenix
b. >> scripts/jrmp-phoenix.sh
c. >> scripts/jrmp-reggie.sh
d. >> scripts/activatable-jeri-server.sh
e. >> scripts/client.sh
c. >> cntrl-c
b. >> cntrl-c
-- UNIX/Linux/OS X --
b. >> rm -rf lib/phoenix-log
-- windows --
b. >> rmdir /S /Q lib\phoenix-log
B-5 Activatable Jini ERI server
Jini ERI Phoenix
b. >> scripts/jeri-phoenix.sh
c. >> scripts/jrmp-reggie.sh
d. >> scripts/activatable-jeri-server.sh
e. >> scripts/client.sh
c. >> cntrl-c
b. >> cntrl-c
-- UNIX/Linux/OS X --
b. >> rm -rf lib/phoenix-log
-- windows --
b. >> rmdir /S /Q lib\phoenix-log
C-6 SSL

client displays login dialog box
c. >> scripts/ssl-reggie.sh
d. >> scripts/ssl-server.sh
e. >> scripts/ssl-client.sh

Keystore password: clientpw
d. >> cntrl-c
c. >> cntrl-c
C-7 SSL

client displays login dialog box

client displays call-confirming dialog box
server displays call-confirming dialog box
c. >> scripts/ssl-reggie.sh
d. >> scripts/confirming-ssl-server.sh
e. >> scripts/ssl-client.sh

Keystore password: clientpw

click "OK" in dialog displayed by client
click "OK" in dialog displayed by server
d. >> cntrl-c
c. >> cntrl-c
C-8 SSL

Activatable SSL server
SSL Phoenix

client displays login dialog box
b. >> scripts/ssl-phoenix.sh
c. >> scripts/ssl-reggie.sh
d. >> scripts/activatable-ssl-server.sh
e. >> scripts/ssl-client.sh

Keystore password: clientpw
c. >> cntrl-c
b. >> cntrl-c
-- UNIX/Linux/OS X --
b. >> rm -rf lib/phoenix-log
-- windows --
b. >> rmdir /S /Q lib\phoenix-log
D-9 Kerberos

client displays login dialog box
c. >> scripts/krb-reggie.sh
d. >> scripts/krb-server.sh
e. >> scripts/krb-client.sh

Kerberos username[<default>]: client
Kerberos password for client: clientpw
d. >> cntrl-c
c. >> cntrl-c
D-10 Kerberos

client displays login dialog box

client displays call-confirming dialog box
server displays call-confirming dialog box
c. >> scripts/krb-reggie.sh
d. >> scripts/confirming-krb-server.sh
e. >> scripts/krb-client.sh

Kerberos username[<default>]: client
Kerberos password for client: clientpw
click "OK" in dialog displayed by client
click "OK" in dialog displayed by server
d. >> cntrl-c
c. >> cntrl-c
D-11 Kerberos

Activatable Kerberos server
Kerberos Phoenix

client displays login dialog box
b. >> scripts/krb-phoenix.sh
c. >> scripts/krb-reggie.sh
d. >> scripts/activatable-krb-server.sh
e. >> scripts/krb-client.sh

Kerberos username[<default>]: client
Kerberos password for client: clientpw
c. >> cntrl-c
b. >> cntrl-c
-- UNIX/Linux/OS X --
b. >> rm -rf lib/phoenix-log
-- windows --
b. >> rmdir /S /Q lib\phoenix-log

Table 1: Quick Start Table


Making Changes

Out of the box, this example includes prebuilt class and JAR files. If you wish to experiment with modifying and rebuilding this example, the following files are provided to allow you to recompile the source and rebuild the JAR files:

For running build.xml, the version of the Java operating environment used must be at least 1.4 and the version of Ant used must be at least 1.6.2.

Note that all operations shown below are performed from the hello directory of the example, and assume a UNIX, Linux, or OS X operating system. If the operating system is Windows, then replace $ANT_HOME with %ANT_HOME%, and replace all instances of the UNIX file separtor '/' with the Windows file separator '\'.

Compiling the source

If you edit one or more of the source files belonging to this example, typing the following will cause the appropriate file(s) to be automatically recompiled:
  >> $ANT_HOME/bin/ant compile

Generating the JAR files

The JAR files associated with this example can be generated individually, in selected subsets, or all in a single command. For example, the following commands will separately generate all of the example's JAR files:
  >> $ANT_HOME/bin/ant client.jar
  >> $ANT_HOME/bin/ant server.jar
  >> $ANT_HOME/bin/ant server-act.jar
  >> $ANT_HOME/bin/ant server-dl.jar
  >> $ANT_HOME/bin/ant mdprefld.jar
The following command will generate a selected subset of the JAR files:
  >> $ANT_HOME/bin/ant client.jar server.jar mdprefld.jar
Finally, the following command will generate all of the JAR files of the example in a single command:
  >> $ANT_HOME/bin/ant jars

Generating Javadoc

To create Javadoc documentation for the example, type the following:
  >> $ANT_HOME/bin/ant doc

Generating prebuilt keys

To generate keys and certificates for the example, type the following:
  >> $ANT_HOME/bin/ant trust

Clean builds

To remove all class files, recompile all source files, and then generate all of the JAR files of the example, type the following:
  >> $ANT_HOME/bin/ant clean
  >> $ANT_HOME/bin/ant

Troubleshooting

While running the example, you may encounter problems. This section attempts to describe some of the more common problems and their possible causes and solutions. Please note that the information contained in this section is by no means complete or exhaustive.

Class server not started or improperly configured

If you encounter output containing information like the following, you should verify that you have started a class server listening on the correct port and serving classes from the correct directories.
java.lang.ClassNotFoundException: com.sun.jini.reggie.RegistrarProxy
                                  (could not obtain preferred value)

Dynamic permission grants not supported

If you encounter a stack trace that contains information like that shown below, the problem is usually related to the security policy provider for dynamic permission granting.
java.lang.SecurityException: Dynamic permission grants are not supported
....
Caused by: java.lang.UnsupportedOperationException: grants not supported
The cause of the above problem is typically an incorrectly installed (or not installed at all) jsk-policy.jar file. Another (only slightly) less common cause is a 'typo' in the system property java.security.properties referenced on the command line. For example,
-Djava.security.properties=config/dynamic-policy.security-properties
If the file referenced in the property above does not exist or is unreachable, or if there is a typo in either the property name or the file name, an exception trace like that above will occur.

No trusted certificates found

INFO: exception while preparing lookup service proxy
java.rmi.ConnectIOException: I/O exception connecting to BasicObjectEndpoint[...]; 
        nested exception is:  net.jini.io.UnsupportedConstraintException:
        sun.security.validator.ValidatorException: No trusted certificate found
This problem occurs when running the SSL configurations and is typically encountered when one or more public key certificates cannot be found. A common cause of this problem is an incorrect javax.net.ssl.trustStore system property. For example,
-Djavax.net.ssl.trustStore=prebuiltkeys/truststore
Although a missing or inaccessible file can cause this problem, one of the most common mistakes made regarding this property is to not capitalize the second 'S' in the 'trustStore' component of javax.net.ssl.trustStore.

Mixing protocols

Although protocols can be mixed when using JRMP and Jini ERI, protocols usually cannot be mixed when using the secure protocols. Depending on which party is running securely and which is not, different exception conditions will be encountered. For example, if the lookup service is run under a secure configuration, and the client or server is run under one of the basic configurations, output like the following may be encountered:
java.net.MalformedURLException: unknown protocol: httpmd
On the other hand, if the lookup service is run under one of the basic configurations, but the client or server is run under a secure configuration, the output will probably contain information like the following:
INFO: exception while preparing lookup service proxy
java.lang.SecurityException: object is not trusted: com.sun.jini.reggie.RegistrarProxy[...]
        at net.jini.security.Security.verifyObjectTrust(Security.java:<lineNo>)

Unsynchronized clocks

When running the example under one of the Kerberos configurations, an exception indicating something like the following may be encountered:
Pre-authentication information was invalid (24) - Preauthentication failed. 
Often this is an indication of skew between the clock on the KDC host (running the authentication server) and the clock on the host running the Kerberos server wishing to authenticate. Those clocks should be set to the same time zones and synchronized to be within 5 minutes of each other.


Looking Deeper

Once again, although only one example is being presented, that single example can be run under a variety of configurations to behave in ways that satisfy different requirements; requirements such as, This section goes a bit beyond what was presented in the quick start section by diving a little deeper, analyzing the purpose and contents of the various files provided with this example; source, configuration and policy files, manifests, preferred lists, etc.

Overview

The example presented here consists of distributed components that are federated by a Jini technology infrastructure to interact in a service-oriented manner. One of the components making up the example is a Jini technology lookup service, through which the example's client and service components are federated. Note that for the purposes of this discussion, the term service and the term server will often be used interchangably, with server referring to the actual incarnation of the service.

Recall that a common way to view a service is as a provider of some sort of resource. A client of a service can then be viewed as a user (or consumer) of the resource provided by the service. For example, consider the Jini technology lookup service itself. The lookup service provides multiple resources that can be useful to both clients and other services. One resource provided by the lookup service is "residency" in the lookup service. Through the residency a lookup service provides to another service, that other service advertises the availability of its own resource(s). Another resource provided by the lookup service is a "query resource". Clients can query the lookup service for, and obtain references to, other services having resources the client wishes to use.

For each and every configuration in this example, the service component of the example discovers the lookup service and then requests and is granted residency in the lookup service. The client also discovers the lookup service, but rather than requesting residency, the client queries the lookup service for a service of type Hello, and the lookup service provides the client with a reference (a proxy) to that service. Once the client has obtained a proxy to the service of interest, through that proxy, the client uses the service to display a greeting, which is the resource the service provides. To see the implementation details, examine the contents of the following source files. Note that it is the proxy object that implements the Hello interface, which is the service type the client requests from the lookup service.

Understanding the configurations

To understand the differences in the various configurations under which this example can be run, it's important to first recognize that interactions between the components in the federation occur primarily through remote invocations. Recall that when a remote invocation is made from one component to another, the call is initiated in the first component's virtual machine (VM), then communication occurs from one VM to the other, with the call ultimately executing in the second component's VM. In particular, when a service requests residency in the lookup service, when a client queries the lookup service for a service of interest, and when a client uses (the remote methods of) that service (for example, to display the greeting in this example), remote calls are made. Because communication is occurring between remote, possibly untrusted, parties, the ability to configure and control how that communication occurs can be important.

The point of this example is to show how the source can be written and built only once, yet executed using different (possibly mixed) remote communication mechanisms with different controls and constraints placed on how that communication occurs; by merely running under different configurations that are specified at runtime. No matter what configuration is used to run the example, the client's goal is always the same: to use a lookup service to find a Hello service, and then to use the Hello service to display a greeting. As will be further explained in the sub-sections below, the differences in the configurations lie in how the remote calls between the components are implemented and controlled.

Tip: with respect to the emphasis on remote communication between the components of the system, if the example is being run on a single machine, it might help to view the separate windows as separate machines on which each component runs and communicates with the others.

Basic configurations

Consider the configurations A-1, B-2, B-4, and B-5 as presented in the quick start table. For the purposes of this discussion, those configurations will be referred to as the basic configurations because no access control (either manual or provided through the protocol) and no authentication occurs when two components in the system attempt to communicate with each other. These configurations demonstrate the basic operation of the example, highlighting the ability of the JRMP and Jini ERI protocols to coexist in a system. With any of these configurations, one should simply see the "Hello, world!" greeting displayed in the command window from which the client was run.

Configuration files

Because this example emphasizes the concept of "one source, different configurations", it may be helpful to examine and compare the contents of the basic configuration files that are provided.

Basic configuration files

Recall that remote objects must be exported. The process of exporting a remote object results in the creation of a proxy object, which is passed to other components so that those other components may communicate with (make remote calls on) the component that was exported. When examining the configuration files above, notice that the configuration files for the lookup service (Reggie) and the server each specifies an exporter. From this, one can conclude that the lookup service as well as the server each export a remote object. Note that although the client's configuration file doesn't specify an exporter, it happens that a remote object actually is exported by the client component. Upon examining the client's configuration file, one can see that the client specifies a ServiceDiscoveryManager (SDM). The SDM utility exports a remote listener object, using a default exporter if no exporter is specified in the configuration. Of course, the client configuration file above could have explicitly specified an exporter in a way similar to what is done in the client's secure configurations, which can be seen by examining the files in the next section.

Through the configured exporter, one can specify the protocol to use for remote communication; in this case, JRMP or Jini ERI. Thus, when the remote object is exported using the exporter specified in the configuration, a proxy is created through which the other components in the system can communicate with the remote object using the protocol that is specified with that exporter. For example, to configure a basic exporter that will create a proxy that communicates back to the remote object using JRMP, the configuration would contain an entry like the following:

  exporter = new JrmpExporter();
Compare this with an exporter that specifies its remote communication to occur over Jini ERI,
  private static endPoint  = TcpServerEndpoint.getInstance(0);
  private static ilFactory = new BasicILFactory();

  exporter = new BasicJeriExporter(endPoint, ilFactory);
Note that the values of the private entries in the configuration snippet above will be automatically substituted for the parameters of the exporter specification when the exporter is instantiated. Using substitution in this way is not required; that is, the private values could have been entered directly in the exporter specification. Private entries are used here and in the other snippets presented in this document to aid in readability.

From the snippets above, it should be clear that one specifies the use of JRMP or Jini ERI by configuring either a "JRMP exporter" or a "Jini ERI exporter" respectively. As will be further demonstrated in the Secure configuration files section below, when using a BasicJeriExporter, one has additional flexibility with respect to the transport over which communication will occur. Through the endPoint parameter of BasicJeriExporter, one can specify non-secure transports such as TCP (as was done above) and HTTP, or transports that support security, such as SSL, HTTPS, and Kerberos.

Secure configurations

The configurations with IDs that begin with 'C' or 'D' from the quick start table will be referred to as secure configurations because access control, integrity, and authentication are enforced when components of the system communicate with each other under those configurations.

Dynamic permissions, integrity, access control

To install the security policy provider included in the Apache River release (to enable dynamic permission granting) the secure configurations of the lookup service, the server, and Phoenix each use the security properties file, config/dynamic-policy.security-properties.

With respect to the example's server component, each of that component's secure configurations employs a specially defined preferred class provider, which uses HTTPMD URLs to ensure integrity of data and downloaded code. Additionally, each secure configuration of the server component specifies a ServerPermission to control access to the resource(s) the server provides.

Principals

Whether running one of the SSL configurations or one of the Kerberos configurations, each component is associated with a principal - an X500Principal in the case of SSL, or a KerberosPrincipal in the case of Kerberos. A principal essentially represents a component's identity; that is, "who" the component is. Constraints can then be imposed and permissions can be granted based on a component's identity.

Constraints

One way to view constraints is as a mechanism - in conjunction with the standard Java security policy mechanism - for expressing what a principal can do, what it cannot do, and/or what it must do. In particular, through constraints and Java security policy, the components of the example can be configured so that the desired security is enforced when the components attempt to communicate with each other. For example, before communication between two components is allowed, one (or both) component(s) may be required to authenticate itself to the other component; that is, the component may be required to not only tell the other component who it is, but also prove who it is. Based on who the component authenticates as, access to the resources of the component with which it is communicating is either granted or refused; that is, access is controlled based on who (what principal) is asking for that access.

Login configuration files

Each component of the example is associated with a different principal representing who that component is while it executes. Each principal is associated with a corresponding password. Take a look at the files in the example's prebuiltkeys directory that have names of the form *.password. Notice that the prebuiltkeys directory contains no password files associated with the client component of the example. When running the example, you should observe that only the client component prompts for a password; thus, no password file is required for the client in the prebuiltkeys directory. This is because, unlike the client, the lookup service, the server, and Phoenix are each configured to automatically retrieve their respective passwords from a file, rather than from an interactive dialog box in which a human user supplies the principal name and appropriate password.

Whether running an SSL configuration or a Kerberos configuration, the authentication mechanism used is the Java Authentication and Authorization Service (JAAS). To use JAAS, a component "logs in"; a process that requires configuration. To understand how each component of the example is configured for JAAS login, compare the contents of the following files:

JAAS login configuration files for SSL configurations

JAAS login configuration files for Kerberos configurations If you compare these files, you will see some parallels between the SSL login configuration files and the Kerberos login configuration files. For example, there is an indication of the principal name for the corresponding component (keyStoreAlias for SSL, principal for Kerberos), where to find the credentials that will be used for authentication (keyStoreURL and keyTab), and for SSL, the location from which to retrieve the associated password (keyStorePasswordURL). If you examine the client's SSL login configuration file, you will see that there is no keyStorePasswordURL, and its Kerberos login configuration file sets doNotPrompt to false (by default); both of which will cause the client's login module to prompt for a password.

Configuration files

As discussed previously, the secure configurations of this example emphasize security in the face of remote calls and downloaded code. Furthermore, when security is introduced into the system, it is introduced through a mechanism of constraints in conjunction with the standard security policy mechanism. Concrete examples of this can be seen when examining and comparing the contents of the following configuration files:

SSL configuration files

Kerberos configuration files While examining the above configuration files, one should notice the relationship between the export process, the proxies that result from exporting, and the constraint mechanism in conjunction with the standard security policy mechanism. As with the basic configurations, each component in the example exports a remote object; thus, each secure configuration specifies an exporter. Furthermore, the exporter that is configured is the mechanism through which one can specify the security requirements that will be enforced from the point of view of the exported remote object.

Because components that receive a proxy produced by an exporter will use the proxy to communicate with the exported remote object, those components will want to specify the security requirements that will be enforced from the point of view of the component that receives and uses the proxy. Additionally, because acquisition and reconstruction of the proxy, as well as the invocation of any remote methods on the proxy, typically involve not only data transmitted in-band as part of the remote call, but also code downloaded out-of-band, enforcing object integrity (both data and code) will become a priority for many components that interact with the proxies they receive.

Note that whether viewing constraints from the point of view of an exported remote object, or from the point of view of a component that receives and uses the proxy associated with a remote object, when a set of constraints specify integrity (Integrity.YES) on the remote calls between a proxy and its remote object, the integrity of both the data and any downloaded code must be verified. That is, during the execution of a remote call in which serialized objects are transmitted and reconstructed, it must be verified that neither the serialized data that is tranmitted in-band, nor the code defining each object's behavior that is downloaded out-of-band (if any), has not been compromised.

Whereas security requirements are specified through an exporter for a remote object's communication, the security requirements for communication through a proxy are specified through what is referred to as a proxy preparer. Thus, while examining the contents of the secure configuration files above, you will notice not only exporters being specified, but also proxy preparers. For a particular configuration file, the exporters are specified for the remote objects that are exported by the specific component associated with that configuration; whereas the proxy preparers are specified for the proxies that that component receives from the other components in the system.

Upon examining the exporters specified in the secure configurations, you will see that in addition to specifying the transport to use, through the invocation layer, constraints can be set and aspects of access control can be specified. Compare this to the security-related items that are specified for the proxy preparers. When configuring a proxy preparer, the following is specified:

Exporters and security

To make this discussion regarding exporters and proxy preparers more concrete, first consider the following snippet showing the configuration of an exporter one might wish to use to export the remote object defined by the example's server. This exporter is configured for SSL. Note that, as before, private entries are used for readability.

  private static endPoint      = SslServerEndpoint.getInstance(0);
  private static integrityOnly = new InvocationConstraints(Integrity.YES, null);
  private static constraints   = new BasicMethodConstraints(integrityOnly);
  private static ilFactory     = new ProxyTrustILFactory(constraints, ServerPermission.class);

  exporter = new BasicJeriExporter(endPoint, ilFactory);
This exporter is similar to the Jini ERI exporter presented in the basic configurations section. Note the differences though. This exporter specifies the use of secure sockets (SSL) for communication and, through the invocation layer factory, specifies that when a component attempts to invoke a remote method on the exported object, integrity must be enforced, and the component must have permission - specifically, a ServerPermission - to access the particular method on which the invocation is being attempted.

There are a couple of additional things to note about the configuration of the exporter above. First, with respect to the exporter's constraints, the only constraint that is specified is integrity. This is not an uncommon security model for remote server objects. Rather than using additional constraints to enforce access control, the strategy demonstrated here is to exploit the standard Java security policy mechanism to control access to the exported object's remote methods using a ServerPermission. The second thing to note is that to change the above SSL exporter to a Kerberos exporter, one merely has to change the endpoint to a Kerberos endpoint. That is,

  private static endPoint = KerberosServerEndpoint.getInstance(0);

Proxy preparers

Next, consider a snippet showing the configuration of the proxy preparer one might wish to apply to the proxy the client receives from the server (by way of the lookup service):

  private static verifyTrust           = true;
  private static requirements          = new InvocationConstraint[] 
                                              { Integrity.YES,
                                                ClientAuthentication.YES,
                                                ServerAuthentication.YES,
                                                new ServerMinPrincipal(serverUser) };
  private static preferences           = null;
  private static invocationConstraints = new InvocationConstraints(requirements,preferences);
  private static methodConstraints     = new BasicMethodConstraints(invocationConstraints);
  private static dynamicPermissions    =
       new Permission[] { new AuthenticationPermission(clientUser,serverUser,"connect") };

  preparer = new BasicProxyPreparer(verifyTrust, methodConstraints, dynamicPermissions);
Upon examining the snippet above, one can see that before the proxy of interest can be used, it must be verified that the proxy can be trusted. That is, the first step in proxy preparation is trust verification (if requested). Once it is known that the proxy can be trusted, constraints can be attached to the proxy. This example presents four constraints that represent a common model for proxy preparation: After trust has been established and constraints have been set, the required permissions that allow the proxy to communicate with the remote object must be granted to the proxy. Although the remote server from which the proxy originated authenticates as the principal serverUser, when the proxy is used to communicate back to that server, the proxy is granted permission to connect to the server as the principal clientUser. Think of the proxy as foreign code executing in the client's environment; code produced by an entity - the server - that authenticates as the server's principal. The value of the last argument of the proxy preparer configuration shown above specifies that the proxy is allowed to communicate back to that server under the client's principal; as if it were the client itself. The proxy is allowed to do this because the first step in the proxy preparation process established that the client trusts that proxy to act on the client's behalf.

In addition to exporters and proxy preparers, there are a number of other items specified in the configuration files, along with some in-line explanatory documentation. You should examine and compare the contents of all the configuration files.

Call confirming configurations

An additional set of configurations under which this example can be run are what will be referred to as the call confirming configurations. Each of the call confirming configurations employs the Jini ERI protocol for communication, using a custom invocation layer factory to produce a custom invocation handler paired with a custom invocation dispatcher. When the client component, through the proxy, attempts to make a remote call to the server component, the custom invocation handler causes a dialog window to pop up so that manual confirmation from the client side of the call can be performed before allowing the call to proceed. Similarly, after allowing the call to proceed from the client side, when the call is dispatched on the server side, the dispatcher also displays a dialog window so that manual confirmation from the server side of the call can be performed before allowing the call to complete.

To see the details of how the custom invocation layer factory, the custom dispatcher, and the custom invocation handler are implemented, examine the contents of the following source files.

Note that the call confirming configurations span both the basic and secure configurations described above. That is, like the basic configurations, there is a call confirming configuration that specifies basic TCP/IP as the transport and, like the secure configurations, there are two call confirming configurations that specify SSL and Kerberos (configurations with IDs B-3, C-7, and D-10 respectively from the quick start table). To see the details, examine the contents of the following configuration files:

Activatable configurations

Another set of configurations provided with this example are what will be referred to as the activatable configurations. The distinguishing feature of the activatable configurations is that the server component is activatable; that is, the server is configured to be executed by the Phoenix implementation of the activation system.

To understand the explanations below, it might be instructive to review some terminology. As described previously, when a remote object is exported, a proxy object is produced. Through a reference to that proxy object, other components of an application can communicate with the exported remote object (the server). The proxy object is often referred to as the front end of the server, and the various aspects of its communication with the remote object are referred to as the client side communication. As one might expect, when the proxy is referred to as the front end of the server, the associated remote object with which that proxy communicates is referred to as the back end of the server, and its communication with the proxy is referred to as the server side communication. Additionally, the term client is generally used to refer to the component holding the reference to the proxy, but can also refer to the proxy object itself (depending on context). Similarly, the term server generally refers to the remote object that was exported, or the component that performed the export and holds the remote object. Given this terminology, it should come as no surprise then that this example provides a class named Client and a class named Server.

With the above terms in mind, one way to view the activation system is as a "container" that can be used to execute server back ends in shared or separate virtual machines embodied as activation groups. That is, the activation system (Phoenix) is started, and then the server one wishes to execute is registered with the activation system, which ultimately executes the server in a VM that the activation system spawns and maintains. As one might expect, for the example being described in this document, the only component with a configuration that supports this model is the server component. To support the server component's interaction with activation, the following class is provided:

ActivatableServer extends Server, providing additional functionality for registering the server with the activation system. Thus, ActivatableServer acts as both the server component of this example and the mechanism used to register the server with the activation system. Note that this strategy of combining server functionality with the activation registration mechanism is just one possible strategy for implementing the activatable form of the server component. Another common strategy is to provide a separate registration mechanism (for example, the class SharedActivatableServiceDescriptor from the Apache River release's service starter framework).

As with the confirming configurations, the activatable configurations associated with ActivatableServer span both the basic and secure configurations described above. For the details, see the configurations with IDs B-4, B-5, C-8, and D-11 from the quick start table, and examine the contents of the following configuration files:

Configuration files for ActivatableServer

Configuration files for the server component when activated The lists above reveal an interesting pattern for configuration when Phoenix is used to execute a server's back end. Notice that for each configuration represented in the lists above (Jini ERI, SSL, Kerberos), there are two configuration files: one that specifies the configuration of the mechanism used to register the server with Phoenix, and one that specifies the configuration of the server itself. It is important to understand that each of the start configuration files from the first list specifies how ActivatableServer is to interact with Phoenix to register the server (in this case, to register itself) for activation, not how either Phoenix or the server are themselves configured. For example, if you examine config/start-activatable-jeri-server.config, you will see that it is in this file that one specifies the actual configuration to use for the server that is to be registered and ultimately activated (look for the entry that references config/activatable-jeri-server.config). When the server is activated, the server configures itself from the entries it retrieves from that second configuration. Note that it is also in that start configuration file that options and system properties can be specified for the VM in which the server runs.

To specifically configure Phoenix, for each configuration under which Phoenix may be run in this example (JRMP, Jini ERI, SSL, Kerberos), there are Phoenix-specific configuration file pairs: one for configuring the activation group created by Phoenix, and one that specifies the actual configuration of Phoenix itself. That is,

Configuration files for activation group, created by Phoenix, in which server runs

Configuration files for Phoenix itself This pattern of configuration file pairs, where one configuration specifies how a server is to be started and the other configuration specifies the behavior of the server itself, does not apply only in activatable scenarios. This pattern often occurs whenever any sort of container-type facility is used to initiate the execution of another component. For example, consider the lookup service implementation employed in this example (Reggie). To start Reggie under any of the configurations of this example, a container-type application, referred to as the service starter framework, is used. As when Phoenix, through the ActivatableServer class, is used to start the activatable form of the server component of this example, when the service starter framework is used to start a server such as Reggie, each configuration is represented by a pair of configuration files. Specifically,

Configuration files for service starter framework used to start Reggie

Configuration files for Reggie itself Finally, note that because the activatable form of the server component actually runs in a VM (activation group) spawned by Phoenix, the mechanism for stopping the activatable server is to actually stop Phoenix. This is why the instructions in the quick start table indicate that a cntrl-c (kill signal) should be executed in the command window in which Phoenix was started, rather than the server's command window. Additionally, also note that to support the management of the environment in which the activatable server runs, Phoenix persists certain state. Thus, for convenience, and to avoid unnecessary attempts at restarting any old, unwanted configurations of the server component, the instructions in the quick start table also recommend removing the current persisted state (log files and directory) whenever Phoenix is shut down.

Security policy and access control

In the Java security architecture, an access control mechanism is presented in which the concept of a protection domain, encapsulated by the ProtectionDomain class, is discussed. Through the security policy, a protection domain is mapped to a desired set of permissions, and based on the particular security policy in place, the access control mechanism can be used to allow or deny access to various resources. To understand security policy and its relation to the protection domain of currently executing code, one can view it as a mechanism in which a set of permissions are granted to: Note that code source is characterized not only by a location - a URL - but also by any possible signers.

Through the security policy, permissions may be granted to any combination of the items in the list above. That is, whereas permissions may certainly be granted to a set of classes signed by some set of signers, obtained from a particular URL and loaded by a given class loader, and running as a particular set of principals, permisssions may also be granted to code simply executing on behalf of a set of principals, with no regard to class loader or code source; or to code based on only the location from which the code was obtained, etc.

Security policy file format

Security policy file format provides the mechanism for specifying the "who" and "where" to which permissions will be granted. With respect to the "what" of the protection domain, note that class loaders provide no means for unique identification; therefore, there is no way to reference a specific class loader in a security policy file. For example, consider the following grant entries that might appear in a security policy file:
  grant codeBase "file:lib/phoenix.jar" principal "phoenix" {
      permission java.lang.RuntimePermission "accessDeclaredMembers";
      permission java.lang.RuntimePermission "shutdownHooks";
  };

  grant principal "phoenix" {
      permission com.sun.jini.phoenix.SystemPermission "com.sun.jini.phoenix.activeGroup";
      permission com.sun.jini.phoenix.SystemPermission 
                                       "java.rmi.activation.ActivationSystem.activeGroup";
      permission com.sun.jini.phoenix.SystemPermission 
                                          "java.rmi.activation.ActivationSystem.shutdown";
  };

  grant codebase "file:lib/phoenix.jar" {
      permission java.lang.RuntimePermission "getClassLoader";
      permission java.lang.RuntimePermission "getProtectionDomain";
  };

  grant {
      permission java.util.PropertyPermission "customProp1", "read";
      permission java.util.PropertyPermission "customProp2", "read";
  };

  grant codebase "file:lib/jsk-platform.jar" {
      permission java.security.AllPermission;
  };

  grant {
      permission java.security.AllPermission;
  };
Because security policy file format does not allow one to specifically indicate a class loader to which to grant permissions, the above grant entries present examples of permissions being granted to various combinations of principal(s) and code source. For example, the first entry grants the indicated permissions to only the code from the local JAR file indicated in the file: URL, with the additional restriction that the code from that source must be executing as the indicated principal. Compare this with the second entry. That entry grants the indicated permissions to code from any source, so long as that code is running as the given principal. Alternatively, in the third entry, the indicated permissions are granted to code running as any principal, but only so long as the code was obtained from the indicated code source location.

Moving down the list, the fourth entry grants the indicated permissions (but only those specific permissions) to code from any source, running as any principal, and the entry after that, grants all permissions (java.security.AllPermission) to specific code with no restriction on principal; that is, code from the specific source indicated, executing as any principal. Finally, the last entry grants all permissions to all code, no matter what principal(s) that code may, or may not, be running as.

Terminology

In the discussions below, the term party (or parties) may be used at times to generally refer to the entities to which a particular set of permissions are granted. For example, when discussing a grant entry like the first one above, one might refer to granting the permissions contained in that entry to the party who is both located at lib/phoenix.jar, and who has logged in (or authenticated) as the principal phoenix. Similarly, when discussing the second grant entry, one might talk about granting the indicated permissions to any parties who have simply authenticated as the principal phoenix.

Security policy principles

The following principles embody the general philosophy of the security policy demonstrated in this example through the configurations and security policy files discussed below. The model presented here is general in nature and fairly broad in its applicability. Therefore, your own applications may require the specification of a different set of principles.
  1. All permissions is never ever granted to all parties (any code source, any principal).
  2. All permissions is only granted to parties from local, trusted sources.
  3. For non-secure configurations, under certain conditions, specific permissions may be granted to all parties (all code, any principal).
  4. For secure configurations, server-side access control is provided through security policy. That is, from the server side of a remote call, access permission allowing the invocation of a remote method should only be granted to parties who are trusted to make use of that method.
Recall that the security model specified by Jini technology provides mechanisms that emphasize security in the face of remote calls and downloaded code. Each of the principles above attempts to acknowledge this aspect of security policy in a Jini technology-based application.

Principle 1 - never grant "all permissions" to all parties

To understand the importance of the first principle, consider the situation where that principle is violated. When an entity grants "all permissions" to all code, the code to which "all permissions" is granted will include any code that is downloaded from remote sources; that is, foreign code, which is not necessarily trusted. This can pose a significant security risk because access to all of the granting entity's resources is being provided to parties that may be both unknown and untrusted. Thus, to prevent exposure to the risks presented when "all permissions" is granted to all code, each of the policy files provided with this example satisfies the first principle.

Principle 2 - only grant "all permissions" to local code

The second principle is designed to provide some flexibility to the policy, without sacrificing security. What is meant by the term local code, as it is used in that principle, is code that has been obtained from a trusted source and that has been installed locally on your system. For example, consider the JDK (or JRE) code you are running. Presumably, you obtained this code from a vendor that you trust will not supply a JDK or JRE that does "bad things". Even if the version of Java you are using was not installed by you, it was probably pre-installed by the (trusted) vendor who supplied your system. Similarly, the Apache River release itself was obtained from a trusted source (the Jini team), and was installed by someone you must certainly trust - you! Code that is installed and runs locally (including your own application code) can be safely granted "all permissions" because you know its source, and you trust that it will not behave badly.

Another way of viewing local code is as "code that is not downloaded during the execution of the application". To understand this, consider the policy file for both the basic configuration and the SSL configuration of Reggie from the lists below. In both files, you will see that "all permissions" is granted to the JAR files from only the lib directory of the Apache River release, with no entries granting "all permissions" to http: URLs referencing any of the JAR files from the lib-dl directory. Recall that files such as jsk-platform.jar, reggie.jar, etc. are contained in the Apache River release's lib directory whereas only downloadable JAR files (such as jsk-dl.jar, reggie-dl.jar, etc.) are contained in the lib-dl directory. The pattern promoted by the Apache River release is that the JAR files from the lib directory are expected to be locally installed, not downloaded; whereas the JAR files contained in the lib-dl directory are expected to be downloaded. Thus, another way of stating the second principle is, "never grant all permissions to downloaded code".

Principle 3 - specific permissions may be granted to all parties (non-secure configurations)

With respect to an application of the third principle, one might wonder why configurations that are labelled as "non-secure" are concerned with fine grained security such as that expressed in the principle; or any security at all for that matter. It should be clear from the first two principles that the term "non-secure", as it is applied to the configurations described in this document, does not equate with "no security at all". Rather, that term is meant to imply that the mechanisms of the Jini technology security model, mechanisms that provide items such as authentication, authorization, confidentiality, integrity, and trust verification, are not employed when remote calls are made and code is downloaded. But this does not mean that access to all other aspects of an entity's resources should be granted to other parties. The position taken by this example, as expressed in the third principle, is that it still makes good sense to use the mechanisms of the standard Java security model to allow open access to only those resources that are necessary and can be considered low risk. In this way, functionality can be provided while minimizing one's vulnerability to attacks from third parties.

A concrete example of the third principle above can be seen in the policy file for the basic configuration of Phoenix. In that file you will notice an entry that grants various instances of ExecOptionPermission to all parties. To understand the need for that permission, recall that Phoenix spawns a VM, referred to as an activation group, in which the server to be activated will be executed. When starting the activation group, a number of options and properties are usually set. To see the properties and options that are set for the activation group under the basic configuration, examine the contents of the configuration file for the class that is used to start the activatable form of the server under Jini ERI. To see how those options and properties are actually set, take a look at the code in ActivatableServer.java.

In order to register the server with Phoenix for activation, a proxy to the activation system is obtained and, through the remote registerGroup method on that proxy, an activation group descriptor, containing the necessary options and properties, is registered with Phoenix. For simplicity (to avoid the complications that can arise when separating the server's classpath and codebase from that of the activation group), the classpath and codebase properties that the ActivatableServer sets for the activation group are also the classpath used to execute the server, and the codebase used to annotate the server's classes. Thus, after identifying the options and properties specified in the configuration file, and then examining the contents of Phoenix's policy file as well as the source from ActivatableServer.java, the role of ExecOptionPermission should be apparent. Using ExecOptionPermission, Phoenix expresses the access control policy for what activation group descriptors can be registered. That is, through ExecOptionPermission, Phoenix controls which options and properties can be set.

In the case of the non-secure configurations, the reason that Phoenix grants the various instances of ExecOptionPermission to all parties rather than specific parties is because the registration is performed through a remote call made by an entity with no client subject; that is, the entity does not authenticate as any principal(s) to Phoenix. Because there are no principals to which Phoenix can grant the required permissions, and because the source of the code making the call is not in Phoenix's access control context, the necessary permissions must be granted to the empty protection domain (any principal, any code source); otherwise Phoenix's access control policy will reject the call. Compare this to the policy files provided with the secure configurations of Phoenix. When interacting with Phoenix under the secure configurations, the activatable form of the server authenticates itself to Phoenix as the principal server, and in Phoenix's associated policy files, the necessary instances of ExecOptionPermission are granted to any code source that authenticates as that specific principal. As described below, this is a result of the fourth principle.

Before discussing the fourth principle, another question that might have occurred while examining Phoenix's non-secure configurations in light of the third principle is why, in the file config/jeri-phoenix-group.config, is the Phoenix activation group configured to export its ActivationInstantiator with an AccessILFactory rather than a BasicILFactory? As explained in the Phoenix documentation, by default, the activation group exports itself as a JRMP unicast remote object, which is a limitation of the existing activation system design. If the configuration for the activation group specifies an exporter for the ActivationInstantiator through the configuration entry named instantiatorExporter, the activation group is unexported from the JRMP runtime and then re-exported using the exporter specified in the configuration. So this explains why an exporter that employs Jini ERI is specified for that configuration. To understand why that exporter is configured to use an AccessILFactory rather than a BasicILFactory, recall that BasicILFactory returns an invocation dispatcher that accepts calls from any host (local or remote), whereas AccessILFactory returns an invocation dispatcher that only accepts calls from the local host. Thus, by configuring the exporter for the ActivationInstantiator to employ an AccessILFactory, additional control can be enforced on access to the activation group.

Principle 4 - server-side access control is provided through security policy (secure configurations)

The fourth principle of the policy employed in this example addresses the issue of restricting access to remote calls; that is, granting or denying permission to invoke a remote method on an object. Recall that the proxy through which a remote call is made is created when a remote object is exported, and is obtained by the caller through some means (for example, through a lookup service). As noted previously, such a call is initiated on the client side of the remote call, and is ultimately executed on the server side, in the remote object that was exported. It is the policy file of the remote object on the server side of the call that is addressed by this last principle. Rather than configuring the object's exporter with constraints to enforce access control, the remote object uses policy and access permissions to restrict access to each of its remote methods to only those parties who are trusted to make use of that method.

To see a concrete example of the fourth principle, examine the contents of any of the policy files for the secure configurations from the lists below. In particular, take a look at the following snippets from the policy files for the SSL configuration of the client component, the server component, and the lookup service:

Client policy

  grant principal "reggie" {
      permission net.jini.security.AccessPermission "notify";
      permission net.jini.security.AccessPermission "getProxyVerifier";
  };

Server policy

  grant principal "client" {
      permission com.sun.jini.example.hello.ServerPermission "sayHello";
  };
  grant {
      permission com.sun.jini.example.hello.ServerPermission "getProxyVerifier";
  };

Lookup service policy

  grant {
      permission com.sun.jini.reggie.RegistrarPermission "getProxyVerifier";
  };
  grant principal "server" {
      permission com.sun.jini.reggie.RegistrarPermission "register";
      permission com.sun.jini.reggie.RegistrarPermission "cancelServiceLease";
      permission com.sun.jini.reggie.RegistrarPermission "renewServiceLease";
      permission com.sun.jini.reggie.RegistrarPermission "cancelLeases";
      permission com.sun.jini.reggie.RegistrarPermission "renewLeases";
  };
  grant principal "client" {
      permission com.sun.jini.reggie.RegistrarPermission "lookup";
      permission com.sun.jini.reggie.RegistrarPermission "notify";
      permission com.sun.jini.reggie.RegistrarPermission "cancelEventLease";
      permission com.sun.jini.reggie.RegistrarPermission "renewEventLease";
      permission com.sun.jini.reggie.RegistrarPermission "cancelLeases";
      permission com.sun.jini.reggie.RegistrarPermission "renewLeases";
  };
To help with understanding the grant entries in the snippets above, a review of the remote interactions between the components of this example might be of value. When running under the secure configurations, the server component, represented by the principal named server, and the client component, represented by the principal named client, both invoke various remote methods on the lookup service, represented by the principal named reggie. As for the interaction between the client and server, that interaction is restricted to the client's invocation of the server's remote methods; the server makes no remote calls on the client. Finally, although the lookup service makes no remote calls into the server component, the lookup service does make remote calls into the client.

To understand how the lookup service makes remote calls into the client, recall that because this example's client component wishes to receive remote events from the lookup service, it registers a remote event listener with the lookup service. Thus, the client must export a remote listener object whose proxy will be used by the lookup service to make remote calls back into the client to provide the client with event notifications. This means that with respect to the client's participation in the lookup service's event mechanism, the client is actually on the server side of the remote calls, and the lookup service is on the client side.

The next thing to consider when examining the grant entries above is the AccessPermission class. That class represents permission to invoke the remote method whose name is indicated in the target component of the permission. In the client snippet above, there is a single entry in which the principal reggie is granted permission to invoke two remote methods on the listener proxy provided by the client component: getProxyVerifier and notify. Permission to invoke the listener's getProxyVerifier method must be granted so that the lookup service can perform trust verification on that listener's proxy, and permission to invoke the listener's notify method must be granted so that the lookup service can send events to the client.

Upon comparing the snippet from the client's policy file to the remaining snippets, one will notice that whereas the client policy uses AccessPermission to control who can invoke the client's remote methods, both the server and the lookup service enforce that control using their own custom subclasses of AccessPermission (the subclasses ServerPermission and RegistrarPermission respectively). Using a simple subclass of AccessPermission such as those used in this example is a common mechanism that entities can use to express separation of grants for the various remote objects those entities export. The choice of using AccessPermission or a custom subclass is a matter of taste, and thus, is left to the discretion of the developer; although this example employs a common pattern whereby only the servers (the hello service and the lookup service) use a custom, server-specific permission.

The last thing to understand in the snippets above is "who" is being granted permission to invoke the indicated remote methods provided by the associated component. Considering how the components interact with each other in this example, the pattern should not be surprising. In general, the parties who are granted the permissions represent the entities from which the granting entity expects to receive remote calls. Additionally, rather than granting such parties permission to call all remote methods provided by the granting entity, those parties are granted permission to call only those methods the granting entity expects a particular party to need. The criteria used to determine the parties and the permissions they should be granted is generally based on the granting entity's perception of a particular party's "role" or expected model of interaction, together with the general level of risk that access to particular methods might pose to the granting entity. Organizing grant entries in this way allows the entity to express its access control policy as a separation of concerns.

Once again, consider the client policy. Because the client expects only the lookup service to interact with it using the remote event listener exported by the client, the client grants to the principal reggie (and only that principal), permission to call the two remote methods provided by the listener. Compare this with the lookup service's policy. With respect to how each interacts with the lookup service, the concerns of the client are separate from those of the server. That is, there are methods the client is allowed to call that the server is not allowed to call, and vice versa. Additionally, although there is no overlap of concerns in this particular case, there is nothing to preclude two components such as this example's client and server from also needing access to a number of common methods in order to fulfill their respective roles as Jini technology-enabled client and service.

When examining the snippets from the lookup service's policy and the server's policy, one might wonder why the entries that grant permission to call getProxyVerifier grant that permission to all parties, rather than a specific principal, as was done in the client's policy. Recall that when running under the secure configurations, before using a proxy, that proxy should be prepared; where the first step in proxy preparation is verifying that the proxy can be trusted. A fundamental element of trust verification is an object referred to as a trust verifier. With respect to the remote object that provides the proxy, the trust verification mechanism defined in the Jini technology security model requires that some means be provided for other parties to verify that the proxy can be trusted. In this example, each remote object provides that means through the implementation of the ProxyTrust interface, which requires the remote object to implement the remote method getProxyVerifier. Thus, each component that exports a remote object must grant permission to call that method to any parties that are expected to attempt to verify trust in the resulting proxy.

The policy snippets above express two different philosophies regarding who to grant permission to call getProxyVerifier. The first philosphy says that permission to call any remote method - including getProxyVerifier - should only be granted to specific principals. The client's policy is an example of this first philosphy. Both the server's policy and the lookup service's policy are examples of the second philosophy, which allows for all parties to be granted permission to call certain low risk methods. This allows for better separation of concerns, and can be a bit more flexible and convenient. This example takes the position that both philosophies have merit; therefore, the fourth security policy principle presented above was written to allow for both.

Note that the method getProxyVerifier is considered low risk, no matter who calls it, because all that method allows the calling party to do is verify trust in the proxy. There is nothing more that can be done through that method. Thus, with respect to the invocation of getProxyVerifier, all parties are considered trusted (as required by the fourth principle).

Finally, one might observe that unlike the policy presented in this example, other applications specify a very fine grained policy, in which only the minimum set of permissions that are needed are granted in the policy file; no more, no less. As alluded to above, such a policy can be quite inflexible when addressing code changes that require new permissions be granted. This is because to accommodate any new permissions resulting from changes to code running under a fine grained policy, changes in the various policy files will also be required. Thus, although not necessarily the appropriate model for every application, the principles followed by the policy employed in this example provide a flexible mechanism that allows for a wide array of changes in the future.

For easy reference, a list of all of the policy files employed in the various configurations of this example follows:

Policy file for service starter framework

Policy files for ActivatableServer that registers server with activation system Policy files for Basic configurations Policy files for SSL configurations Policy files for Kerberos configurations

Preferred list(s)

The downloadable JAR file of the server component of this example (server-dl.jar) specifies preferred classes to be recognized by a preferred class loader implementation to ensure that the downloaded versions of invocation handlers and proxy classes are used. The loader implementation employed in this example is the PreferredClassLoader class, and the preferred settings are specified by the following file:

Logging

Logging is performed in this example using the Java platform's core logging facilities. This example does not specify any of its own loggers. All logging that occurs in this example occurs through the various loggers specified in the Apache River release itself, which are configured using the following logger configuration file: In that file, all loggers are initially commented out, and so are configured to log at a default log level of INFO. To modify the levels of each logger, simply edit the above file, and then un-comment the desired logger(s), and set the current level to the desired level. For example, to turn on all logging related to Jini ERI processing, change the line,
  # net.jini.jeri.level = INFO
to
  net.jini.jeri.level = FINEST

Additional resources

Please visit jini.org to explore the many resources provided by the Jini Community(SM).


Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.