This document provides an overview of the implementation details of Jini Specifications included in Apache River and explains how these components fit together. Much of this information is available in the package, interface, and class documentation, but this document provides a higher-level overview and indicates where to find more information.
The Jini system architecture consists of three categories: programming model, infrastructure, and services. The original Jini Architecture Specification Version 1.0 defines these categories as follows:
The infrastructure is the set of components that enables building a federated Jini system, while the services are the entities within the federation. The programming model is a set of interfaces that enables the construction of reliable services, including those that are part of the infrastructure and those that join into the federation.
Originally, the programming model defined models for leasing, event notification, and transactions. The basic infrastructure consisted of the discovery/join protocol and the lookup service. Previous versions delivered implementations of the following Jini technology-enabled services (Jini services):
Apache River releases following and including the Jini v2.0 release add components to the first two categories (programming model and infrastructure) of the Jini architecture, although some components span both of these categories. Additionally, this release provides updates to existing services to account for the additions to the infrastructure and the programming model. The component additions can be summarized as follows:
Additions to Programming Models
Additional Infrastructure
Configuring Services
Each service included in Apache River supports being
configured using a Configuration
. Most of the
services were previously configured via system properties. These
services can now be configured via entries in the configuration.
New types of behavior that can be configured are remote service
exporting and proxy preparation.
Configuration
)
ProxyPreparer
and Exporter
)
The primary motivation for augmenting the Jini architecture is to provide the new programming models and infrastructure necessary to support and manage security in Jini technology-based distributed applications. The previous Jini architecture lacked direct support for a type of security many Jini / Apache River applications need, namely basic network security for remote calls to services. This goal is addressed by the new security infrastructure and constraint-based remote invocation model.
Another important goal for this release is to better unify the
overall programming model for service developers, given the
previous lack of uniformity in the APIs for implementing,
exporting, and deploying services. Securing remote invocation
requires additional client-side verification steps, so this
release introduces a uniform client-side API to handle a
"preparation" step before remote invocation. Additionally,
application developers can ease deployment tasks by separating
out deployment-time information from the application. The
Configuration
API introduced in the Jini 2.0 release
provides a simple and uniform way to obtain deployment-specific
information. The resulting programming model supports a uniform
framework for pluggable security, exporter, transport, and other
service providers.
A final goal is to provide an implementation of the Java RMI programming model that supports constraints on remote invocation (which is crucial to the security model), and supports pluggable invocation and dispatch behavior as well as pluggable transport providers on a per-object basis, features which are lacking in the JRMP implementation of Java 2 SDK, Standard Edition (J2SE(TM)) RMI. The Jini ERI API provides such a pluggable implementation of the Java RMI programming model.
Before delving into the discussion of the security
architecture, it is important to understand initially a few
additional programming models, Configuration
,
Exporter
, and ProxyPreparer
, each of
which simplifies application development in general and secure
application development in particular.
Configuration
The net.jini.config.Configuration
API supports this
approach by providing a simple, uniform way to obtain the objects
needed to configure an application. An application can obtain an
object from a Configuration
instead of explicitly
constructing an instance to avoid tying the application to
deployment-specific implementations. Typical objects that should be
obtained from a configuration are Exporter
and
ProxyPreparer
instances (discussed below).
An application may not want to be tied to a specific implementation
of Configuration
either, so an application can use
net.jini.config.ConfigurationProvider
to obtain a
Configuration
implementation. Using the
ConfigurationProvider
in an application allows the
application's deployer to specify the most suitable
Configuration
implementation at deployment time.
A given Configuration
implementation might
read configuration information from a file, a database,
or from some other source.
The standard, default Configuration
implementation is
net.jini.config.ConfigurationFile
. This implementation
reads configuration information, written in a subset of the syntax of
the Java programming language, from files and URLs to produce objects
from the configuration. See the net.jini.config
package
documentation for an example of how an application can use a
Configuration
and how a deployer can use a
ConfigurationFile
source file to configure that
application.
Exporter
and the Server-Side Implementation Model In Jini technology-based applications, invoking a method on a Jini service proxy has a uniform client-side model: a client simply invokes a method on the service's proxy to initiate remote communication to the service object, an invocation that follows the same general semantics as the Java RMI model.
The server-side implementation model for Jini / Apache River applications, however, is not uniform. An application developer typically implements a service and exports that service in a way that ties the service directly to a specific implementation and model of remote communication. This implementation dependence requires that the application source code be modified if the service needs to be exported in a slightly different way. For example, an application deployment may need to export a service to use a different port or socket factory, or may need to export that service using a different implementation of remote communication entirely, such as a different implementation of the Java RMI programming model.
The net.jini.export.Exporter
interface unifies the
server-side implementation model by providing an abstraction for
exporting and unexporting a remote object. The details of export and
unexport behavior, including communication protocols used for remote
invocation and additional invocation semantics, are defined by the
particular implementation of the Exporter
interface.
Several standard Exporter
implementations, which are
also implementations of the Java RMI programming model, are provided.
The following table lists these
implementations along with the class whose behavior the exporter is
semantically equivalent to.
Exporter
Equivalent Class net.jini.jrmp.JrmpExporter
java.rmi.server.UnicastRemoteObject
net.jini.iiop.IiopExporter
javax.rmi.PortableRemoteObject
net.jini.jeri.BasicJeriExporter
itself
An application can use a Configuration
together with
the Exporter
interface to export remote objects in a way
that can be configured at deployment time. The following example
(similar to those in the net.jini.config.Configuration
package documentation) illustrates configurable exporting:
import java.rmi.*; import net.jini.config.*; import net.jini.export.*; public class Example implements Remote { public static void main(String[] args) throws Exception { Configuration config = ConfigurationProvider.getInstance(args); Exporter exporter = (Exporter) config.getEntry( "Example", "exporter", Exporter.class); Remote proxy = exporter.export(new Example()); System.out.println(proxy); exporter.unexport(true); } }
This application can be configured by specifying the following
configuration file (that uses ConfigurationFile
syntax) as a command line argument. The configuration file
specifies the exporter to be a JrmpExporter
, an
exporter that produces proxies that participate in the JRMP
protocol.
import net.jini.jrmp.*; Example { exporter = new JrmpExporter(); }
Alternatively, the application can be deployed to specify the
exporter to be a BasicJeriExporter
(an exporter for
exporting a remote object to use Jini ERI) constructed to export the
object on an anonymous TCP port.
import net.jini.jeri.*; import net.jini.jeri.tcp.*; Example { exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0)); }
ProxyPreparer
and the Client-Side Invocation Model The new security model (discussed below) requires the application to perform some pre-invocation "preparation" of a service proxy before invoking its methods. This is because an application may receive the service proxy from an untrusted source. Therefore, before an application uses a proxy, that application might need to perform operations such as verifying trust in the proxy or granting permissions to the proxy once its trust has been verified.
These security requirements add an additional step to the
client-side invocation model. The
net.jini.security.ProxyPreparer
interface abstracts the
operation of "proxy preparation" into a single method,
prepareProxy
, so that the client-side invocation model
can be uniform across applications using secure and non-secure
proxies.
A ProxyPreparer
can be used together with a
Configuration
to allow configurable proxy preparation.
The following is a code snippet from the hello
example client, which is part of a comprehensive example included
in the Apache River release:
ProxyPreparer preparer = (ProxyPreparer) config.getEntry( "com.sun.jini.example.hello.Client", "preparer", ProxyPreparer.class, new BasicProxyPreparer()); server = (Hello) preparer.prepareProxy(server); System.out.println("Server says: " + server.sayHello());
The call to getEntry
supplies a default (new
BasicProxyPreparer()
) for the entry that will be returned if
the configuration file does not have the specified entry. A
net.jini.security.BasicProxyPreparer
instance can be
constructed with arguments specifying whether a proxy, passed to its
prepareProxy
method, needs to be verified or needs other
security-related operations to be performed. A
BasicProxyPreparer
constructed with no arguments, which
is the default above, specifies a "do nothing"
ProxyPreparer
that simply returns the proxy passed to the
prepareProxy
method.
If an application deployment has security requirements, the
application can be deployed with a BasicProxyPreparer
constructed with the appropriate arguments or can be
deployed with an alternate ProxyPreparer
implementation.
In a typical Jini technology-based application, a client obtains a service proxy from somewhere (for example, from a lookup service or as a result of communicating with another service) and then invokes methods on that proxy to communicate with the service. Many applications have a need to secure such remote communication, requiring one or more of the following:
These requirements are typical for securing data communication; however, the Jini security model has an additional requirement: code integrity. In the Jini and Java RMI model, an object's data is sent in a remote invocation, but its code is downloaded out-of-band as a result of that remote invocation. An application needs to establish trust for the objects it receives, both in the integrity of an object's code as well as in its data.
This release provides a framework for trust verification as well as a constraint-based remote invocation model that enables an application to specify various requirements (such as security requirements) on remote invocations. The infrastructure also provides mechanisms that can be used to ensure code integrity.
The security requirements described above can be expressed as
constraints on remote invocation. A constraint is a specific
requirement, set by the client, on the behavior of remote invocations
through a proxy to its associated service. A proxy can implement the
interface net.jini.core.constraint.RemoteMethodControl
to
enable a client to set constraints on remote invocation through
that proxy. The constraint-based remote invocation model is general,
providing for security-related constraint types as well as other
constraint types.
One example use of constraints is a client that needs to authenticate
the identity of a service (such as a bank) before communicating with
it through its proxy. In this example, the service proxy can
implement the interface RemoteMethodControl
to enable a
client to set a constraint requiring server authentication on any
remote invocation through that proxy. It is the proxy's
responsibility to satisfy those constraints, but it is the client's
responsibility to verify that it can trust the proxy to carry out its
responsibility (discussed below).
Each constraint is represented as a
net.jini.core.constraint.InvocationConstraint
instance.
An InvocationConstraint
instance expresses "what" but not
"how"; that is, a constraint expresses "what" the constraint
represents, but not "how" the constraint should be satisfied. A proxy
is responsible for using the appropriate transport and protocols to
satisfy the constraints set by the client. Because constraints are
expressed abstractly, the security implementations are pluggable and
thus are capable of supporting a wide variety of protocols such as
Secure Socket Layer (SSL) and Kerberos. See the
net.jini.core.constraint
package documentation for
general information on constraints including a number of pre-defined
basic constraints.
Before a client sets constraints on a proxy, the client first needs to verify that it can trust the proxy to carry out the constraints to be set, because the proxy may have been received from an untrusted source. If the client does not verify that the proxy is trusted, there is a danger that the proxy could simply ignore the client's constraints and perform unencrypted data transfer, misrepresent the client's or server's identity, or worse. After the client verifies trust in the proxy, it can then set constraints on the proxy to specify any desired security requirements, such as server authentication.
Once a client trusts a proxy, it may also need to grant additional
permissions to the proxy, over and above the minimal permissions
typically configured in advance for untrusted code, so that future
invocations through the proxy function properly. A proxy may require
net.jini.security.AuthenticationPermission
so that it can
authenticate with the server, for example. A client must not grant
permissions to the proxy before it trusts the proxy, so that the proxy
cannot abuse the grants in a way that might cause harm.
To summarize, a client receiving a proxy from an untrusted source
needs to "prepare" the proxy in the following manner before invoking
that proxy's methods. The full requirements for each of these steps
are described in the net.jini.security
package documentation:
The steps above can be encapsulated in a single operation known as
proxy preparation, a term which means "preparing the proxy for
remote invocation". The client-side interface
net.jini.security.ProxyPreparer
(discussed above)
encapsulates such proxy preparation into a single method,
prepareProxy
.
The class net.jini.security.BasicProxyPreparer
, a
standard implementation of the ProxyPreparer
interface,
performs the three necessary steps above to prepare a proxy for secure
remote calls. Most deployers can use a
BasicProxyPreparer
instance for proxy preparation and not
worry about all the details behind trust verification (which are
outlined in some detail below). Other applications may need to subclass
BasicProxyPreparer
if verifying trust, setting
constraints, and/or granting permissions require some customization.
BasicProxyPreparer
The BasicProxyPreparer
class encapsulates the three steps
of verifying trust, setting constraints, and granting permissions to a
proxy. A BasicProxyPreparer
instance can be constructed
in a variety of ways, supporting many common cases for proxy
preparation including the following:
Static methods Security.verifyObjectTrust
and
Security.grant
of the
net.jini.security.Security
class implement the trust
verification and permission granting mechanisms, respectively.
Constraints can be set on the proxy implementing the
RemoteMethodControl
interface by calling its
setConstraints
method. A
BasicProxyPreparer
uses these three mechanisms to
implement the specifics of proxy preparation.
The next three sections describe the mechanics of proxy preparation
using a BasicProxyPreparer
instance that performs all three
steps. For the purposes of this discussion, let's assume that a
BasicProxyPreparer
is constructed as follows:
new BasicProxyPreparer(true, constraints, permissions)
where the argument true
indicates that the proxy
should be verified for trust, constraints is a
MethodConstraints
object (an object containing the
per-method constraints to set on the proxy), and permissions is
an array containing the java.security.Permission
s to be
granted to the proxy.
The UML sequence diagrams (PDF files) referred to in the next three sections illustrate the proxy preparation and trust verification processes. Together, these diagrams depict a full scenario of verifying trust, granting permissions, and setting constraints:
ProxyTrustVerifier
.
ProxyTrustVerifier
.
Note: See this tutorial for a description of the syntax of UML sequence diagrams.
Note that the BasicProxyPreparer
class is designed to be
subclassable, therefore the implementation of the
prepareProxy
method invokes protected
methods on the instance (verify
, grant
,
setConstraints
, etc.) to allow a potential subclass to
customize the instance behavior.
The basic model for verifying trust in an object received from an
untrusted source is to consult one or more trust verifiers (of
type net.jini.security.TrustVerifier
) that are
essentially part of the "trusted computing base" (a term which means
the platform's hardware and software that is fully trusted to enforce
the security mechanisms and policies in place) to see if at least one
of those verifiers trusts the object in question. If one trust
verifier is unable to verify trust, then another one is consulted.
This trust verification process is carried out by the
static
method Security.verifyObjectTrust
.
The Security.verifyObjectTrust
method obtains trust
verifiers from a resource,
META-INF/services/net.jini.security.TrustVerifier
, which,
in this distribution, is part of the file
jsk-resources.jar
, which is referenced by
jsk-platform.jar
. These built-in trust verifiers are
provided to verify instances of classes in the Apache River release. The
following table lists these trust verifier implementations along with
a description of the instances they verify:
TrustVerifier
Verified Instances net.jini.constraint.
ConstraintTrustVerifierconstraint classes in net.jini.core.constraint
net.jini.jeri.
BasicJeriTrustVerifierproxy produced by BasicJeriExporter.export
, if the proxy's invocation handler is a standardBasicInvocationHandler
instance containing aBasicObjectEndpoint
instance, the proxy's class loader is trusted, and the proxy's server constraints andEndpoint
are trustednet.jini.jeri.ssl.
SslTrustVerifierclient-related classes in the net.jini.jeri.ssl
packagenet.jini.jeri.kerberos.
KerberosTrustVerifierclient-related classes in the net.jini.jeri.kerberos
packagenet.jini.security.proxytrust.
ProxyTrustVerifierproxy that can be verified by obtaining a trust verifier from the proxy's server net.jini.discovery.
ConstrainableLookupLocatorTrustVerifierConstraintableLookupLocator
instancescom.sun.jini.discovery.
DiscoveryConstraintTrustVerifierdiscovery constraints
Most clients will use one or more of the trust verifiers above
(indirectly) when verifying trust in a proxy. However, a service will
need to implement a special trust verifier for a smart proxy, or for a
proxy produced by BasicJeriExporter
.
Proxy preparation is initiated by invoking the
prepareProxy
method on a BasicProxyPreparer
instance (constructed as above, with verify set to
true
), passing it the proxy to be prepared (see Figure 1). The
BasicProxyPreparer
instance initiates trust verification
by invoking the Security.verifyObjectTrust
method,
passing the proxy, the value null
(loader) to
specify the context class loader, and the constraints (obtained
from the instance itself) to use on any remote communication by the
trust verification mechanism.
The Security.verifyObjectTrust
method obtains the
trust verifiers from the resource in the specified loader (if
the trust verifiers have not already been obtained) and creates a
net.jini.security.TrustVerifier.Context
(context)
containing the ordered list of trust verifiers. Next, the
isTrustedObject
method of each trust verifier is invoked,
passing it the proxy to be verified and the trust verifier context,
until one trust verifier returns true
. A given trust
verifier will return true
if it can verify trust in the
object passed to it. A trust verifier may also use the context passed
to verify other component objects recursively, before deciding
whether it trusts the object.
The Security.verifyObjectTrust
method returns
true
if there is at least one trust verifier in the
context that trusts the proxy.
A net.jini.security.proxytrust.ProxyTrustVerifier
can verify a service proxy for which a trusted verifier can be obtained
from the service's remote object in a pre-defined manner,
requiring cooperation with the proxy and the service's
remote object. The two typical cases that
ProxyTrustVerifier
handles are: verifying non-smart
proxies (for example, a simple proxy returned by
BasicJeriExporter
) and verifying smart proxies that
wrap simple (non-smart) proxies. These cases are described in
the following two sections.
ProxyTrustVerifier
implements a trust verification
mechanism that uses a trusted bootstrap proxy (either
derived or obtained from the original proxy) to obtain a
trust verifier from the server to use to verify the
original proxy.
The reasoning behind this trust verification approach is as follows: if you trust the service (like your bank), and you obtain the trust verifier by communicating with the service through a trusted bootstrap proxy with server authentication and integrity, then you can trust the verifier returned by the service to verify the service proxy. After all, it is the service that knows best how to verify its own proxies.
If a non-smart proxy that is a dynamic proxy would be directly
trusted by a local trust verifier if only its proxy class were
defined by a class loader trusted by such a verifier, then the
non-smart proxy can implement
net.jini.security.proxytrust.ProxyTrust
so that it
can be verified by a
net.jini.security.proxytrust.ProxyTrustVerifier
. In
this case, the service's remote object must also cooperate
in ProxyTrustVerifier
's verification scheme by
providing a TrustVerifier
for its proxies as
described in the section "Implementation Requirements for Trust
Verification".
The mechanism implemented by ProxyTrustVerifier
verifies non-smart proxies by performing the following:
The details of this mechanism are implemented by the
ProxyTrustVerifier.isTrustedObject
method (depicted in Figure 2).
The proxy itself implements ProxyTrust
and, as such,
is considered a bootstrap proxy. However, this proxy is not
typically trusted because, in most cases, the proxy's class is
defined by an untrusted class loader when the proxy is received
by a remote virtual machine. In this case if certain conditions
are met, the ProxyTrustVerifier
creates a
"derivative bootstrap proxy" using a dynamic proxy class defined
by the parent class loader (which is typically a trusted class
loader) with the identical invocation handler as the bootstrap
proxy. If the derivative bootstrap proxy is trusted by one of
the locally trusted trust verifiers available in the context
(determined by invoking isTrustedObject
on the
TrustVerifier.Context
), then the derivative
bootstrap proxy is the bootstrap proxy.
If the derivative bootstrap proxy is trusted, then the
ProxyTrustVerifier
can obtain the trust verifier
from the service. The ProxyTrustVerifier
first sets
constraints (obtained from the context) on the derivative
bootstrap proxy so that it will obtain the trust verifier from
the server in a secure way. Next, the
ProxyTrustVerifier
invokes the
ProxyTrust.getProxyVerifier
method on the bootstrap
proxy to obtain the trust verifier. This invocation is a remote
call to a "bootstrap remote object" that implements the
ProxyTrust
interface. The
getProxyVerifier
implementation can then call the
ServerProxyTrust.getProxyVerifier
method on the
service (which is a local method call) to obtain the trust
verifier and then return the result. Note that the bootstrap
remote object and the service remote object could be the same object; that is,
the service object can implement ProxyTrust
.
If the service's remote object is exported to use
BasicInvocationDispatcher
, a separate bootstrap
remote object is not needed, nor does the remote object's class
need to implement ProxyTrust
, because
BasicInvocationDispatcher
takes care of these details.
In this case, however, the service's remote object will still
need to implement ServerProxyTrust
as described in
the section "Implementing
ServerProxyTrust
".
After the ProxyTrustVerifier
obtains the trust
verifier from the service, it can then invoke the trust verifier's
isTrustedObject
method, passing it the original service proxy and
the context, to determine whether the proxy is really a proxy that the
service trusts.
Smart proxies or other proxies that do not implement
ProxyTrust
and that cannot be verified directly by the
built-in trust verifiers can also be verified by
net.jini.security.proxytrust.ProxyTrustVerifier
. A
ProxyTrustVerifier
, in verifying smart proxies, uses
some mechanisms in addition to those described above for
non-smart proxies that implement ProxyTrust
directly. As with non-smart proxies verified by
ProxyTrustVerifier
, a smart proxy and its corresponding
remote object must cooperate in
ProxyTrustVerifier
's verification mechanism. The
implementation requirements for service proxies and their remote objects
are described in the section "Implementation
Requirements for Trust Verification".
The mechanism implemented by ProxyTrustVerifier
verifies smart proxies by performing the following:
The details of this mechanism are implemented by the
ProxyTrustVerifier.isTrustedObject
method (depicted in Figure 3). The
ProxyTrustVerifier
uses a special iterator,
ProxyTrustIterator
, to obtain the bootstrap proxy. This
special iterator and the bootstrap proxy (from the iterator) are
obtained as follows.
The ProxyTrustVerifier
reflectively invokes a
(typically private) method, getProxyTrustIterator
, on the
supplied proxy or the supplied proxy's invocation handler to obtain a
ProxyTrustIterator
. This iterator is used to obtain the
bootstrap proxy, an object that must implement the interface
ProxyTrust
, which is a remote interface for obtaining a
proxy trust verifier from the service. If the object returned from
the iterator is not a ProxyTrust
instance, then a
ProxyTrustIterator
is obtained from the returned object
recursively. Otherwise, if the returned object does not implement the
interface RemoteMethodControl
, the
ProxyTrustIterator
is used again to obtain another
candidate bootstrap proxy.
When the iterator returns a bootstrap proxy implementing
ProxyTrust
and RemoteMethodControl
,
the ProxyTrustVerifier
verifies trust in the bootstrap proxy by using the locally trusted
trust verifiers available in the context. This trust verification
process is the same as described for the method
Security.verifyObjectTrust
.
If the bootstrap proxy is not trusted by the locally trusted
verifiers available in the context (that is, the method
isTrustedObject
on the
TrustVerifier.Context
returns false
),
then if certain conditions are met, the
ProxyTrustVerifier
creates a "derivative bootstrap
proxy" using a dynamic proxy class defined by the parent class
loader (which is typically a trusted class loader) with the
identical invocation handler as the bootstrap proxy. If the
derivative bootstrap proxy is trusted by one of the locally
trusted verifiers (determined by invoking
isTrustedObject
on the
TrustVerifier.Context
), then the derivative
bootstrap proxy can be used as the bootstrap proxy.
Once trust in the bootstrap proxy (or derivative) has been established, the
ProxyTrustVerifier
can obtain the trust verifier from the
service. The ProxyTrustVerifier
first sets constraints
(obtained from the context) on the bootstrap proxy so that it will
obtain the trust verifier from the server in a secure way. Next, the
ProxyTrustVerifier
invokes the
ProxyTrust.getProxyVerifier
method on the bootstrap proxy
to obtain the trust verifier. This invocation is a remote call to a
"bootstrap remote object" that implements the ProxyTrust
interface. The getProxyVerifier
implementation can then
call the
ServerProxyTrust.getProxyVerifier
method on the service
(which is a local method call) to obtain the trust verifier and then
return the result. Note that the bootstrap remote object and the
service's remote object could be the same object; that is, the service object can
implement ProxyTrust
.
After the ProxyTrustVerifier
obtains the trust
verifier from the service, it can then invoke the trust verifier's
isTrustedObject
method, passing it the service proxy and
the context, to determine whether the proxy is really a proxy that the
service trusts.
There are two main kinds of service proxies: a "simple" proxy, that is, a "non-smart" proxy returned from export (like a JRMP stub), and a smart proxy that wraps the proxy returned from export. This section describes the verifiers that can be used to verify different categories of proxies, based on their characteristics, and explains the implementation requirements for service proxies and their respective remote objects.
A BasicJeriTrustVerifier
can be used as a trust
verifier for a Jini ERI proxy (a proxy returned by
BasicJeriExporter
) if that proxy's class is defined by a
trusted class loader and uses BasicInvocationHandler
and BasicObjectEndpoint
containing locally
verifiable endpoint instances (such as
net.jini.jeri.ssl.HttpsEndpoint
and
net.jini.jeri.kerberos.KerberosEndpoint
). This is
not a typical case, though, because in most cases, the class of a
Jini ERI proxy received by a remote VM will be defined by an
untrusted class loader.
If a Jini ERI proxy satisfies all of
BasicJeriTrustVerifier
's conditions for trust, except
the condition regarding its dynamic proxy class's loader, then
(on the asumption that the parent of that loader will be locally
trusted) the service proxy and its remote object can cooperate
in the verification mechanism provided by
ProxyTrustVerifier
. In this case, the proxy itself
serves as the bootstrap proxy and must implement
ProxyTrust
. To facilitate this, the service's
remote object can be exported with a
net.jini.jeri.ProxyTrustILFactory
which ensures that
the service proxy implements ProxyTrust
(in addition
to RemoteMethodControl
). The service's remote object
must also implement the
ServerProxyTrust.getProxyVerifier
method to return a
verifier for the proxy (as described below in the section "Implementing
ServerProxyTrust
").
If a Jini ERI proxy that could be trusted by clients (perhaps
by ProxyTrustVerifier
as described above) is used
inside a smart proxy that will not be trusted directly by
clients, a ProxyTrustILFactory
can be used to cause
the inner Jini ERI proxy to also be an instance of
ProxyTrust
(in addition to
RemoteMethodControl
). To fit into the
ProxyTrustVerifier
mechanism, the outer proxy must
implement the getProxyTrustIterator
method to return
a ProxyTrustIterator
whose next
method
returns the inner proxy (referred to as the "bootstrap proxy"
above). The
net.jini.security.proxytrust.SingletonProxyTrustIterator
is a simple implementation of ProxyTrustIterator
whose next
method returns the object the instance is
constructed with. Additionally, the service's remote object must
implement the ServerProxyTrust
interface to return a
trust verifier that verifies the proxy (as described below in the
section "Implementing
ServerProxyTrust
").
For other smart proxies or Jini ERI proxies containing a custom
invocation handler or custom endpoint that will not be trusted
directly by clients, a
net.jini.security.proxytrust.ProxyTrustExporter
can be
used to combine that proxy with a trustable bootstrap proxy, such that
the client can use ProxyTrustVerifier
to verify that the
aggregate proxy can be trusted. The service's remote object would
need to implement ServerProxyTrust.getProxyVerifier
to return a verifier for the aggregate proxy.
ServerProxyTrust
A service's remote object should implement
the ServerProxyTrust
interface
to provide a trust verifier for its proxies that simply
compares a known canonical service proxy with the yet-to-be-trusted proxy,
testing that the proxies are trust equivalent. An object is
considered trust equivalent to a known, trusted object if it is
equivalent in trust, content, and function to that trusted object. The
net.jini.security.proxytrust.TrustEquivalence
interface
can be implemented by trusted objects (say component objects in the
proxy) that need to allow other objects to be checked for trust
equivalence. The TrustEquivalence
interface has a
single method, checkTrustEquivalence
, for checking trust
equivalence.
An implementation of a basic proxy trust verifier, which could be
returned by an implementation of the
ServerProxyTrust.getProxyVerifier
method, is the com.sun.jini.proxy.BasicProxyTrustVerifier
(see the source code BasicProxyTrustVerifier.java
for implementation details).
Once trust in a proxy has been verified, the client may wish to
grant permissions to the proxy so that future invocations on the proxy
will function correctly. This task can also be accomplished through a
BasicProxyPreparer
.
Permissions can only be granted to a proxy if dynamic
permission grants are supported by the installed security policy
implementation. This release provides a
java.security.Policy
implementation,
net.jini.security.policy.DynamicPolicyProvider
, that
supports such dynamic granting of permissions at runtime. The
DynamicPolicyProvider
implements the interface
net.jini.security.policy.DynamicPolicy
, which defines
methods for granting permissions on the granularity of a class loader.
For an application to grant a specific permission, the calling context
must have net.jini.security.GrantPermission
for the
permission being granted. Note that the Security
class
has a static
method, grantSupported
, that
can be called to check whether dynamic permission grants are
supported.
A BasicProxyPreparer
grants permissions to the proxy
on the granularity of the proxy's class loader. The preparer invokes
its own getPermissions
method, passing the
proxy, to obtain the permissions to grant. If there are permissions
to grant, the preparer invokes Security.grant
with the
proxy's class (proxyClass) and the permissions to grant
(see Figure 1).
The Security.grant
operation will delegate to the
security policy implementation to grant the permissions if the policy
supports dynamic permission grants via DynamicPolicy
.
This Security.grant
method grants the specified
permissions to all protection domains that are associated with the
class loader of the given class (which, in this example, is the
proxy's class) and passes at least the principals of the current
subject (if any). If grants are not supported or the calling context
does not have the requisite GrantPermission
for each
permission to grant, then a SecurityException
is thrown.
Once trust is verified and permissions (if any) have been granted
to the proxy, constraints should be set on the proxy to ensure the
desired level of security on future remote invocations through the
proxy. A BasicProxyPreparer
's final task is to set
constraints (if any) on the proxy, resulting in a new proxy (with the
constraints set), and to return that proxy as the result of the
prepareProxy
operation (see Figure 1).
A proxy must implement the interface
net.jini.core.constraint.RemoteMethodControl
to enable
the setting of constraints. Constraints are set by invoking the
proxy's setConstraints
method, passing a
MethodConstraints
object containing the constraints to
set. A BasicProxyPreparer
obtains the method constraints
from itself by invoking its own getMethodConstraints
method and then sets these constraints on the proxy. The
copy of the proxy (new proxy) returned by the
setConstraints
method is returned as the result of
preparing the proxy.
net.jini.core.constraint.Integrity
constraint should
be set on a proxy if remote calls through that proxy require integrity
protection on both the in-band call data, transmitted as part of the
remote call, and the code downloaded out-of-band as a result of the
call. A communication mechanism enforcing an Integrity
constraint can use the Security.verifyCodebaseIntegrity
method to determine if all URLs in a codebase provide content
integrity, based on whatever
net.jini.security.IntegrityVerifier
instances are
configured.
The application does not have to worry about verifying codebase
integrity itself; that task is carried out by the underlying
communication mechanism enforcing the Integrity
constraint. Such communication mechanisms should verify the
codebase URLs for integrity when unmarshalling objects during a remote
call.
The Security.verifyCodebaseIntegrity
method obtains
integrity verifiers from a resource,
META-INF/services/net.jini.security.IntegrityVerifier
,
which, in this distribution (and similar to trust verifiers),
is part of the file jsk-resources.jar
. The following
table lists the standard integrity verifier implementations along
with the type of URLs they verify.
IntegrityVerifier
Verified URLs net.jini.url.httpmd.HttpmdIntegrityVerifier
HTTPMD net.jini.url.https.HttpsIntegrityVerifier
HTTPS net.jini.url.file.FileIntegrityVerifier
FILE
The mechanism implemented by
Security.verifyCodebaseIntegrity
is pluggable (since it
uses a resource), so that other IntegrityVerifier
implementations can be configured if a deployment requires other URL
types to have their integrity protection verified.
A deployment requiring integrity protection for downloaded code may wish to make use of HTTPMD URLs, introduced in this release. An HTTPMD URL includes a message digest for the data retrieved from the URL. The URL input stream ensures that the data has the correct message digest when the end of file for the stream is reached.
The reasoning behind why HTTPMD URLs can be used to verify code integrity is as follows. An HTTPMD URL (configured in a codebase path for an application and subsequently used by the underlying communication mechanism as a class annotation) is transmitted in band as part of a remote call. Because the HTTPMD URL (both the URL and message digest component) is transmitted in band, the message digest can be trusted to the same extent as any other data transmitted in the call. If the HTTPMD URL is communicated with integrity, the value of the message digest must be correct. Therefore, the URL input stream can use the message digest to verify correctly the integrity of the stream contents.
The URL protocol handlers in J2SE can be configured with additional protocol handlers, over and above built-in handlers (such as for HTTP and FILE URLs). To configure the use of HTTPMD URLs for an application, the "java.protocol.handler.pkgs" system property should be set to "net.jini.url".
For convenience, the tool ComputeHttpmdCodebase
can be used to compute message digests for a codebase with HTTPMD
URLs.
Jini ERI, defined in the net.jini.jeri
package, is a
pluggable implementation of the Java RMI programming model that
supports the remote object export model defined in the
net.jini.export
package and supports the security model
described above, including invocation constraints, remote method
control, and the trust verification model. Jini ERI allows
customization of client-side and server-side remote invocation
behavior and also allows the use of a variety of communication
transports.
See the net.jini.jeri
package
documentation for more information.
While this release provides a pluggable infrastructure with many avenues for customization, it also includes many provider implementations that are sufficient for most applications. These providers are summarized in this section.
Many applications will use the supplied providers, having one or more of them specified in deployment-time information in a resource or configuration. Other applications may implement one or more of their own providers if their application deployment requires a more specific feature that is not implemented by the built-in providers.
In general, an application developer or deployer does not need to understand all APIs used by providers. Such APIs only need to be well-understood if one is developing a new service provider or customizing some aspect of an existing one.
The following is a list of provider interfaces and associated
implementations that are located via resources (and specified in
jsk-resources.jar
). For the most part, these
provider interfaces and implementations are used by other
infrastructure implementations. Applications generally don't
need to program to either the interfaces or the implementations
of these kinds of providers with some minor exceptions: to
implement TrustVerifier
for a smart/custom proxy,
and to reference a Configuration
.
java.rmi.server.RMIClassLoaderSpi
(defined in J2SE)
net.jini.loader.pref.PreferredClassProvider
net.jini.loader.pref.RequireDlPermProvider
net.jini.export.ServerContext.Spi
net.jini.jrmp.JrmpServerContext
net.jini.security.TrustVerifier
net.jini.constraint.ConstraintTrustVerifier
net.jini.jeri.BasicJeriTrustVerifier
net.jini.jeri.ssl.SslTrustVerifier
net.jini.jeri.kerberos.KerberosTrustVerifier
net.jini.security.proxytrust.ProxyTrustVerifier
net.jini.discovery.ConstrainableLookupLocatorTrustVerifier
com.sun.jini.discovery.DiscoveryConstraintTrustVerifier
net.jini.security.IntegrityVerifier
net.jini.url.httpmd.HttpmdIntegrityVerifier
net.jini.url.https.HttpsIntegrityVerifier
net.jini.url.file.FileIntegrityVerifier
net.jini.config.ConfigurationFile
net.jini.security.policy.DynamicPolicy
andjava.security.Policy
net.jini.security.policy.DynamicPolicyProvider
The providers in this section are those that a deployer may specify explicitly in a configuration. Applications typically program to the interfaces of these providers, but not their implementations. Many applications can utilize the implementations available in the Apache River release, and therefore, don't need to worry about how to implement a specific interface, only how to use it.
net.jini.jeri.BasicJeriExporter
net.jini.jrmp.JrmpExporter
net.jini.iiop.IiopExporter
net.jini.activation.ActivationExporter
net.jini.security.proxytruxt.ProxyTrustExporter
net.jini.security.ProxyPreparer
net.jini.security.BasicProxyPreparer
net.jini.core.constraint.InvocationConstraint
net.jini.core.constraint.ClientAuthentication
net.jini.core.constraint.ClientMaxPrincipal
net.jini.core.constraint.ClientMaxPrincipalType
net.jini.core.constraint.ClientMinPrincipal
net.jini.core.constraint.ClientMinPrincipalType
net.jini.core.constraint.Confidentiality
net.jini.core.constraint.ConnectionAbsoluteTime
net.jini.core.constraint.ConnectionRelativeTime
net.jini.core.constraint.ConstraintAlternatives
net.jini.core.constraint.Delegation
net.jini.core.constraint.DelegationAbsoluteTime
net.jini.core.constraint.DelegationRelativeTime
net.jini.core.constraint.Integrity
net.jini.core.constraint.RelativeTimeConstraint
net.jini.core.constraint.ServerAuthentication
net.jini.core.constraint.ServerMinPrincipal
net.jini.jeri.ssl.SslServerEndpoint
net.jini.jeri.ssl.HttpsServerEndpoint
net.jini.jeri.tcp.TcpServerEndpoint
net.jini.jeri.kerberos.KerberosServerEndpoint
net.jini.jeri.http.HttpServerEndpoint