t f a Dr Java™ Remote Method Invocation Security Extension This document is an early draft of a proposed standard extension to add security to Java™ Remote Method Invocation (RMI). The specification is incomplete, and will change to align with a future version of the Java™ platform. The document is being published at this time to provide the community with an early look at our design and to solicit feedback on that design, so that we can incorporate any changes as a result of that feedback prior to any official public review. Comments about this draft should be sent to
[email protected].
Early Look Draft 2 9/24/99
9/24/99
Copyright © 1999 Sun Microsystems, Inc. 901 San Antonio Road, Palo Alto, CA 94303 USA All rights reserved. Copyright in this document is owned by Sun Microsystems, Inc. Sun Microsystems, Inc. has patent and other intellectual property rights relating to implementations of the technology described in this Specification ("Sun IPR"). Your limited right to use this Specification does not grant you any right or license to Sun IPR. THIS SPECIFICATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY YOU AS A RESULT OF USING THE SPECIFICATION. THIS SPECIFICATION COULD INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS. CHANGES ARE PERIODICALLY ADDED TO THE INFORMATION HEREIN; THESE CHANGES WILL BE INCORPORATED IN NEW EDITIONS OF THE SPECIFICATION. SUN MICROSYSTEMS, INC. MAY MAKE IMPROVEMENTS AND/OR CHANGES IN THE SPECIFICATIONS AT ANY TIME, IN ITS SOLE DISCRETION. SUN IS UNDER NO OBLIGATION TO PRODUCE FURTHER VERSIONS OF THE SPECIFICATION OR ANY PRODUCT OR TECHNOLOGY BASED UPON THE SPECIFICATION. NOR IS SUN UNDER ANY OBLIGATION TO LICENSE THE SPECIFICATION OR ANY ASSOCIATED TECHNOLOGY, NOW OR IN THE FUTURE, FOR PRODUCTIVE OR OTHER USE. RESTRICTED RIGHTS LEGEND Use, duplication, or disclosure by the U.S. Government is subject to restrictions of FAR 52.227-14(g)(2)(6/ 87) and FAR 52.227-19(6/87), or DFAR 252.227-7015(b)(6/95) and DFAR 227.7202-1(a). TRADEMARKS Copyright © 1999 Sun Microsystems, Inc., All rights reserved. Sun, Sun Microsystems, the Sun logo, and Java are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
REPORT As an Evaluation Posting of this Specification, you may wish to report any ambiguities, inconsistencies, or inaccuracies you may find in connection with your evaluation of the Specification (“Feedback”). To the extent that you provide Sun with any Feedback, you hereby: (i) agree that such Feedback is provided on a non-proprietary and non-confidential basis and (ii) grant to Sun a perpetual, non-exclusive, worldwide, fully paid-up, irrevocable license to incorporate, disclose, and use without limitation the Feedback for any purpose relating to the Specification and future versions, implementations, and test suites thereof.
2
3
JAVA™ RMI SECURITY EXTENSION
Significant changes from previous draft: ◆
added getElementType to AlternativeSecurityConstraints
◆
removed SecureExportDesc parameter from register method of SecureActivatable
◆
added port parameter to getClientFactory method of SecureServerSocketFactory
◆
removed host and port parameters from createSocket method of SecureClientSocketFactory
◆
allowed getActivatorConstraints method of ActivatorSecurity to be specified in SecureExportDesc for secure activatable objects
◆
specified that default constraints in SecureExportDesc are used for DGC
◆
allow choosePreferences method of SecureClientSocketFactory to throw IOException
◆
changed ActivatorSecurity to not extend Remote
◆
changed establishProxyTrust so that the object returned by getSecureProxy does not have to be a secure RMI stub, and added searching of security providers so that what’s trusted is extensible
◆
specified use of security providers to control what classes are trusted for constraints, principals, and client socket factories
◆
augmented the provider search rules to support controlled ordering and subsets
◆
removed doAsClientSubject methods from SecureUnicastRemoteObject, and clarified delegation mechanics
◆
removed DelegationTimeException
◆
moved provider-related classes into javax.rmi.security.spi
◆
reflected JAAS change from JAAS-specific Action classes to those already used by doPrivileged
◆
added checkConnect method to SecureClientSocketFactory
◆
fixed SecureSessionSocket to take client subject instead of server subject
◆
allow parameterTypes to be null in MethodConstraints and RemoteSecurity
◆
changed description of remote call parameter unmarshalling to use the new doAsPrivileged method of Subject
EARLY LOOK DRAFT 2
4
INTRODUCTION
Java™ RMI Security Extension 1
Introduction
The Java™ 2 platform provides mechanisms for defining and using fine-grained access control policies, built around the notion of what code is being executed. Permission is granted to code, based on what location the code came from and who signed that code. Missing from the security architecture is any notion of who is executing the code. The Java Authentication and Authorization Service (http://java.sun.com/ security/jaas) extends the security architecture, providing mechanisms to authenticate subjects (login), execute code on behalf of subjects, and grant permissions to subjects. The result is that access control policies can be based on both what code is being executed and who is executing that code. The RMI Security Extension further extends the security architecture to distributed systems, providing mechanisms to mutually authenticate client and server subjects during a remote call, protect the communication from third parties, and execute code in the server on behalf of the client’s subject. The API for this extension is intentionally at a very high level; cryptographic mechanisms and protocols are not exposed, so that code written to the API is more portable. An underlying service provider interface allows specific mechanisms and protocols to be configured into the framework. Although this is an extension to RMI, the API is designed to be applicable more generally to remote services that are defined in terms of interfaces. A remote service has a front-end proxy object that implements one or more interfaces; the proxy is downloaded into clients that want to use the service. An RMI stub is one form of proxy, but a proxy may also be a more sophisticated object that performs additional computation surrounding an RMI call, or one that uses alternate network protocols.The proxy is responsible for network communication with the back-end remote service, and the implementation of the proxy, including the details of the protocol it uses to communicate with the remote service, can be specialized to the particular implementation of the service. The client does not know EARLY LOOK DRAFT 2
5
JAVA™ RMI SECURITY EXTENSION
about these implementation details; it only knows about the interfaces that define the service.
1.1
Package
The new classes and interfaces defined here are in a new package, javax.rmi.security, and in subpackages thereof.
1.2
Imports
The following imports are assumed throughout: import import import import import import import import import import import import import import import import import import import import import
java.io.Serializable; java.lang.reflect.Method; java.lang.reflect.Proxy; java.lang.reflect.InvocationHandler; java.net.ServerSocket; java.rmi.MarshalledObject; java.rmi.Remote; java.rmi.RemoteException; java.rmi.activation.ActivationException; java.rmi.activation.ActivationID; java.rmi.server.ObjID; java.rmi.server.RemoteServer; java.rmi.server.ExportException; java.rmi.server.ServerNotActiveException; java.security.Principal; java.security.PrivilegedAction; java.security.PrivilegedActionException; java.security.PrivilegedExceptionAction; java.util.Collection; java.util.Set; javax.security.auth.Subject;
EARLY LOOK DRAFT 2
6
INTRODUCTION
EARLY LOOK DRAFT 2
7
JAVA™ RMI SECURITY EXTENSION
2
Constraints
This extension allows both clients and servers to express security constraints to be applied to remote calls. The basic security constraints are very simple: ◆
Communication integrity – making sure that the bits sent over the network are not tampered with by third parties
◆
Server authentication – proving to the client that the server is executing on behalf of some subject, so that the call can be terminated if the subject is not trusted by the client
◆
Client authentication – proving to the server that the client is executing on behalf of some subject, so that the server can perform only those tasks that the subject is authorized for. The flip side of this is anonymity: allowing a client to remain anonymous when it wants to be.
◆
Delegation – allowing the server to authenticate as the client, thus allowing the server to act on a client’s behalf when dealing with third party remote servers, in cases where the identity of the client (not the server) is what matters to the third party
Additional constraints can be placed on the use of delegation over time: ◆
Delegation duration – the delegation is valid only during a given duration, measured from the start of the remote call
◆
Delegation end time – the delegation is valid only until a specific end time
Constraints can also be placed on which principals of a client or server subject must be authenticated: ◆
Principal class – authentication can be restricted to principals that are instances of specific classes
◆
Principal – authentication can be restricted to specific principals
EARLY LOOK DRAFT 2
8
CONSTRAINTS
The constraint mechanism is designed to be extensible; additional kinds of constraints may be provided in specific implementations. Constraints come in two basic flavors: requirements and preferences. A requirement is a mandatory constraint that must be satisfied for the remote call. A preference is a desired constraint, to be satisfied if possible, but it will not be satisfied if it conflicts with a requirement, and if two preferences conflict, it is generally arbitrary as to which one will be satisfied. A server can specify different constraints for each remote method, if desired. Clients can attach constraints directly to a proxy, in which case they apply to all remote calls made through that proxy by any thread, or clients can specify constraints contextually, in which case they apply to all secure remote calls made anywhere within that scope by that thread. Overall, constraints come from four sources: ◆
Server constraints attached to a proxy
◆
Client constraints attached to a proxy
◆
Client constraints specified contextually within the current thread’s stack frames
◆
Client constraints attached to the thread (inherited from the creating thread)
The constraint mechanisms are designed such that a client cannot weaken constraints set by the server, nested code cannot weaken constraints set by enclosing code in the call chain, and code within a thread cannot weaken constraints inherited by the thread. However, it is certainly possible to specify conflicting requirements, in which case the remote call will not be made. A secure remote call will be performed only if the server is capable of providing all of the client requirements (from all sources), and if all of the client and server requirements are supported by the client’s underlying RMI implementation. In addition, both client and server preferences will be satisfied, to the extent possible.
2.1
SecurityConstraint
Security constraints are represented as instances of classes that implement the following interface: package javax.rmi.security; public interface SecurityConstraint { boolean implies(SecurityConstraint c);
EARLY LOOK DRAFT 2
9
JAVA™ RMI SECURITY EXTENSION
Object COMPLEX_INTERSECT = new Object(); Object intersect(SecurityConstraint c); }
In general, most developers do not need to be concerned with the detailed semantics of these methods; they are primarily intended for use within the extension to reduce complex combinations of constraints into simpler forms for interpretation by socket factories. As such, only the general semantics of these methods are described here, and Sections 2.14 and 2.15 provide detailed semantics for these methods with respect to each constraint class. Given two SecurityConstraint instances c1 and c2, c1.implies(c2) is true if every security configuration that could satisfy c1 also satisfies c2. Note that c1.implies(c2) is sometimes true even if c1 and c2 are not instances of the same class. Given two SecurityConstraint instances c1 and c2, c1.intersect(c2) returns the constraint c3 such that ◆ c3.implies(c1)
is true, and
◆ c3.implies(c2)
is true, and
◆
there is no other constraint c4 for which ◆
c4.implies(c1)
is true, and
◆
c4.implies(c2)
is true, and
◆
c3.implies(c4)
is true.
If such a constraint could exist in theory, but cannot be represented as an instance of any existing constraint class, then COMPLEX_INTERSECT is returned. If no such constraint could exist, then null is returned; in this case the two constraints are said to conflict. This method is symmetric: c1.intersect(c2) and c2.intersect(c1) return the same result.
2.2
RelativeTimeConstraint
Constraints that are expressed in terms of relative time are represented as instances of classes that implement the following interface: package javax.rmi.security; public interface RelativeTimeConstraint extends SecurityConstraint
EARLY LOOK DRAFT 2
10
CONSTRAINTS
{ SecurityConstraint makeAbsolute(long baseTime); }
The makeAbsolute method takes an absolute time, specified in milliseconds from midnight, January 1, 1970 UTC, and returns a constraint that has the relative times converted to absolute times using the specified absolute time as a base.
2.3
Basic Constraints
The basic security constraints are provided as constants of the following classes in the javax.rmi.security package: public final class Integrity implements SecurityConstraint, Serializable { public static final Integrity YES; public static final Integrity NO; } public final class AuthenticateClient implements SecurityConstraint, Serializable { public static final AuthenticateClient YES; public static final AuthenticateClient NO; } public final class AuthenticateServer implements SecurityConstraint, Serializable { public static final AuthenticateServer YES; public static final AuthenticateServer NO; } public final class Delegation implements SecurityConstraint, Serializable { public static final Delegation YES; public static final Delegation NO; }
The semantics of these constraints are as follows:
EARLY LOOK DRAFT 2
11
JAVA™ RMI SECURITY EXTENSION ◆ Integrity.YES – detect when message contents (both requests and replies)
have been altered, and refuse to process altered messages ◆ Integrity.NO
– ensure that no integrity mechanism is used (normally this constraint should not be used)
◆ AuthenticateClient.YES ◆ AuthenticateClient.NO
– ensure that the client remains anonymous
◆ AuthenticateServer.YES ◆ AuthenticateServer.NO
– authenticate the client to the server – authenticate the server to the client
– ensure that the server remains anonymous
◆ Delegation.YES
– if the client authenticates to the server, then delegate from the client to the server, such that the server can authenticate using the client’s identity
◆ Delegation.NO
– ensure that the client does not delegate to the server
Delegation.YES does not directly imply an AuthenticateClient.YES constraint; that must be specified separately. Serialization for these classes is guaranteed to produce instances that are comparable with ==.
2.4
DelegationDuration
Delegation duration constraints are represented with the following class: package javax.rmi.security; public final class DelegationDuration implements RelativeTimeConstraint, Serializable { public DelegationDuration(long min, long max); public long getMin(); public long getMax(); } DelegationDuration
represents a constraint that, if delegation is used, the delegation be permitted for at least the min duration, but no longer than the max duration (the range is inclusive at both ends). This constraint does not directly imply a Delegation.YES constraint; that must be specified separately. Durations are specified in milliseconds. Durations are translated into absolute end times at the point of a remote call, by adding the caller’s current time to both min and max values. As such, in order to accommodate clock skew between systems, negative durations are permitted, and may be desirable for minimum durations. EARLY LOOK DRAFT 2
12
CONSTRAINTS
The constructors throw IllegalArgumentException if max is less than min. The makeAbsolute method returns a DelegationEndTime instance with the base time parameter added to both min and max values. Two DelegationDuration instances are equal if they have the same min and max values.
2.5
DelegationEndTime
Delegation time constraints are represented with the following class: package javax.rmi.security; public final class DelegationEndTime implements SecurityConstraint, Serializable { public DelegationEndTime(long min, long max); public long getMin(); public long getMax(); } DelegationEndTime
represents a constraint that, if delegation is used, the delegation be permitted until at least the min end time, but no longer than the max end time (the range is inclusive at both ends). This constraint does not directly imply a Delegation.YES constraint; that must be specified separately. End times are specified in milliseconds from midnight, January 1, 1970 UTC. In order to accommodate clock skew between systems, end times earlier than the current time are permitted, and may be desirable for minimum end times. The constructors throw IllegalArgumentException if max is less than min. Two DelegationEndTime instances are equal if they have the same min and max values.
2.6
ClientMinPrincipal package javax.rmi.security; public final class ClientMinPrincipal implements SecurityConstraint, Serializable { public ClientMinPrincipal(Principal principal); public ClientMinPrincipal(Principal[] principals);
EARLY LOOK DRAFT 2
13
JAVA™ RMI SECURITY EXTENSION
public ClientMinPrincipal(Collection c); public Set elements(); } ClientMinPrincipal represents a constraint that, if the client authenticates itself, then it must authenticate itself as at least all of the specified principals. This constraint does not directly imply an AuthenticateClient.YES constraint; that must be specified separately. The first constructor is equivalent to calling the second constructor with a single-element array containing the specified principal. The second and third constructors throw NullPointerException if the parameter is null, and throw IllegalArgumentException if the parameter is empty or if the elements are not all instances of trusted principal classes (see Section 2.13). The parameter passed to the constructor is not retained; subsequent changes to that parameter have no effect on the instance created. Duplicate principals are removed. The elements method returns an immutable set of all of the principals. Any attempt to modify the set results in UnsupportedOperationException being thrown. Two ClientMinPrincipal instances are equal if they have equal principals (ignoring order).
2.7
ClientMaxPrincipal package javax.rmi.security; public final class ClientMaxPrincipal implements SecurityConstraint, Serializable { public ClientMaxPrincipal(Principal principal); public ClientMaxPrincipal(Principal[] principals); public ClientMaxPrincipal(Collection c); public Set elements(); }
ClientMaxPrincipal represents a constraint that, if the client authenticates itself, then it must authenticate itself as at most the specified principals. This constraint does not directly imply an AuthenticateClient.YES constraint; that must be specified separately. The first constructor is equivalent to calling the second constructor with a single-element array containing the specified principal.
EARLY LOOK DRAFT 2
14
CONSTRAINTS
The second and third constructors throw NullPointerException if the parameter is null, and throw IllegalArgumentException if the parameter is empty or if the elements are not all instances of trusted principal classes (see Section 2.13). The parameter passed to the constructor is not retained; subsequent changes to that parameter have no effect on the instance created. Duplicate principals are removed. The elements method returns an immutable set of all of the principals. Any attempt to modify the set results in UnsupportedOperationException being thrown. Two ClientMaxPrincipal instances are equal if they have equal principals (ignoring order).
2.8
ClientMinPrincipalType package javax.rmi.security; public final class ClientMinPrincipalType implements SecurityConstraint, Serializable { public ClientMinPrincipalType(Class clazz); public ClientMinPrincipalType(Class[] classes); public ClientMinPrincipalType(Collection c); public Set elements(); } ClientMinPrincipalType
represents a constraint that, if the client authenticates itself, then it must authenticate itself such that, for each specified class, at least one authenticated principal is an instance of that class. This constraint does not directly imply an AuthenticateClient.YES constraint; that must be specified separately. The first constructor is equivalent to calling the second constructor with a single element array containing the specified class. The second and third constructors throw NullPointerException if the parameter is null, and throw IllegalArgumentException if the parameter is empty or if the elements are not all trusted principal classes (see Section 2.13). The parameter passed to the constructor is not retained; subsequent changes to that parameter have no effect on the instance created. Redundant classes are removed as follows: for any two specified classes c1 and c2, if c1.isAssignableFrom(c2) is true, then c1 is removed.
EARLY LOOK DRAFT 2
15
JAVA™ RMI SECURITY EXTENSION
The elements method returns an immutable set of all of the classes. Any attempt to modify the set results in UnsupportedOperationException being thrown. Two ClientMinPrincipalType instances are equal if they have equal classes (ignoring order).
2.9
ClientMaxPrincipalType package javax.rmi.security; public final class ClientMaxPrincipalType implements SecurityConstraint, Serializable { public ClientMaxPrincipalType(Class clazz); public ClientMaxPrincipalType(Class[] classes); public ClientMaxPrincipalType(Collection c); public Set elements(); }
ClientMaxPrincipalType represents a constraint that, if the client authenticates itself, then each authenticated principal must be an instance of at least one of the specified classes. This constraint does not directly imply an AuthenticateClient.YES constraint; that must be specified separately. The first constructor is equivalent to calling the second constructor with a single element array containing the specified class. The second and third constructors throw NullPointerException if the parameter is null, and throw IllegalArgumentException if the parameter is empty or if the elements are not all trusted principal classes (see Section 2.13). The parameter passed to the constructor is not retained; subsequent changes to that parameter have no effect on the instance created. Redundant classes are removed as follows: for any two specified classes c1 and c2, if c1.isAssignableFrom(c2) is true, then c2 is removed. The elements method returns an immutable set of all of the classes. Any attempt to modify the set results in UnsupportedOperationException being thrown. Two ClientMaxPrincipalType instances are equal if they have equal classes (ignoring order).
EARLY LOOK DRAFT 2
16
CONSTRAINTS
2.10
ServerMinPrincipal
package javax.rmi.security; public final class ServerMinPrincipal implements SecurityConstraint, Serializable { public ServerMinPrincipal(Principal principal); public ServerMinPrincipal(Principal[] principals); public ServerMinPrincipal(Collection c); public Set elements(); } ServerMinPrincipal represents a constraint that, if the server authenticates itself, then it must authenticate itself as at least all of the specified principals. This constraint does not imply directly an AuthenticateServer.YES constraint; that must be specified separately. It is important to understand that specifying AuthenticateServer.YES as a requirement does not, in and of itself, ensure that a server is to be trusted; it does ensure that the server authenticates itself as someone, but it does not ensure that the server authenticates itself as anyone in particular. Without knowing who the server authenticated itself as, there is no basis for actually trusting the server. The client generally needs to specify a ServerMinPrincipal requirement in addition, or else verify that the server has specified a satisfactory ServerMinPrincipal requirement for each of the methods that the client cares about. The first constructor is equivalent to calling the second constructor with a single element array containing the specified principal. The second and third constructors throw NullPointerException if the parameter is null, and throw IllegalArgumentException if the parameter is empty or if the elements are not all instances of trusted principal classes (see Section 2.13). The parameter passed to the constructor is not retained; subsequent changes to that parameter have no effect on the instance created. Duplicate principals are removed. The elements method returns an immutable set all of the principals. Any attempt to modify this set results in UnsupportedOperationException being thrown. Two ServerMinPrincipal instances are equal if they have equal principals (ignoring order). Note that ServerMaxPrincipal, ServerMinPrincipalType, and ServerMaxPrincipalType classes do not exist. A server can constrain the maximum set of principals it will authenticate as by explicitly specifying a subject at export time. Restricting the class of a server principal is not useful, because ulti-
EARLY LOOK DRAFT 2
17
JAVA™ RMI SECURITY EXTENSION
mately a client needs to specify exact principal instances that a server must authenticate as, in order to establish any useful basis for trusting the server.
2.11
AlternativeSecurityConstraints
Alternative constraints can be aggregated into a single overall constraint using the AlternativeSecurityConstraints class: package javax.rmi.security; public final class AlternativeSecurityConstraints implements RelativeTimeConstraint, Serializable { public static SecurityConstraint create( SecurityConstraint[] constraints); public static SecurityConstraints create(Collection c); public Set elements(); public Class getElementType(); public boolean impliedBy(SecurityConstraint c); }
The semantics of this aggregate constraint is that at least one of the individual constraints must be satisfied. The create methods throw NullPointerException if the parameter value is null. The methods throw IllegalArgumentException if: the parameter is empty; or if any of the elements are instances of AlternativeSecurityConstraints; or if the elements are not all instances of the same concrete class; or if the elements are not all instances of trusted constraint classes (see Section 2.13). The parameter passed to the method is not retained; subsequent changes to that parameter have no effect on the instance created. Redundant constraints are removed as follows: for any two specified constraints c1 and c2, if c1.implies(c2) is true, then c1 is removed. If a single constraint remains, then that constraint is returned, otherwise an instance of AlternativeSecurityConstraints containing the resulting constraints is returned. Note that this class implements RelativeTimeConstraint even though the constraint elements might not implement RelativeTimeConstraint. The makeAbsolute method simply returns the AlternativeSecurityConstraints instance if the constraint elements are not instances of RelativeTimeConstraint, otherwise it converts the constraint elements using their makeAbsolute method and returns the result of invoking the create method of AlternativeSecurityConstraints with the converted constraints. EARLY LOOK DRAFT 2
18
CONSTRAINTS
The elements method returns an immutable set of all the constraints. Any attempt to modify this set results in UnsupportedOperationException being thrown. The getElementType method returns the class of the constraint elements. Two AlternativeSecurityConstraints instances are equal if they have equal constraints (ignoring order). The semantics of the impliedBy method is described in Section 2.14.9.
2.12
SecurityConstraints
Constraints are aggregated using the SecurityConstraints class: package javax.rmi.security; public final class SecurityConstraints implements Cloneable, Serializable { public static final SecurityConstraints EMPTY; public SecurityConstraints(); public SecurityConstraints(SecurityConstraint req, SecurityConstraint pref); public SecurityConstraints(SecurityConstraint[] reqs, SecurityConstraint[] prefs); public SecurityConstraints(Collection reqs, Collection prefs); public SecurityConstraints( SecurityConstraints constraints); public Set requirements(); public Set preferences(); public boolean implies(SecurityConstraint c); public boolean implies(SecurityConstraints constraints); public void setReadOnly(); public boolean isReadOnly(); public boolean isEmpty(); public static SecurityConstraints copyReadOnly( SecurityConstraints constraints); public static SecurityConstraints combineAbsolute( SecurityConstraints constraints1, SecurityConstraints constraints2); public Object clone(); }
EARLY LOOK DRAFT 2
19
JAVA™ RMI SECURITY EXTENSION
A SecurityConstraints instance can be mutable or read-only (immutable). Any attempt to alter a read-only instance will result in UnsupportedOperationException being thrown. The implementation is not synchronized; if multiple threads access a mutable instance concurrently, and at least one of the threads modifies the instance, external synchronization must be used. Only instances of trusted constraint classes (see Section 2.13) can be added to a SecurityConstraints instance; attempts to add other objects will result in IllegalArgumentException being thrown. The static field EMPTY is an empty read-only instance, one that has no requirements and no preferences. The first constructor creates an empty mutable instance. The second constructor creates a read-only instance that has the first parameter (if non-null) added as a requirement and has the second parameter (if non-null) added as a preference. The third and fourth constructors create a read-only instance that has all of the constraints from the first parameter (if non-null) added as requirements and has all of the constraints from the second parameter (if non-null) added as preferences. The fifth constructor creates a mutable copy of the parameter, containing the same requirements and preferences. The requirements method returns the set of all requirements. The preferences method returns the set of all preferences. The iterators of these sets are fail-fast: if the set is modified after an iterator is created, the iterator will throw ConcurrentModificationException. All Set methods are fully supported, but adding a constraint to either set has special semantics. Adding a new requirement c1 to the requirements set results in a simplification of both the requirements and preferences sets, as follows. If any existing requirement implies c1, then c1 is not actually added, and there is no other side effect. Otherwise, for each existing requirement and each existing preference c2: ◆
if c1.implies(c2) is true, then c2 is removed, otherwise
◆
if c1.intersect(c2) returns null and c2 is a preference, then c2 is removed, otherwise
◆
if c1.intersect(c2) returns a constraint (not COMPLEX_INTERSECT), then c2 is removed and the returned constraint is added.
Finally, if none of the calls to intersect with an existing requirement returned a constraint (or if no calls were made), then c1 is added to the requirements set. Adding a new preference c1 to the preferences set results in a simplification of the preferences set, as follows. If c1 is equal to any existing preference, or any EARLY LOOK DRAFT 2
20
CONSTRAINTS
existing requirement implies c1, or the intersection of any existing requirement with c1 is null, then c1 is not actually added, and there is no other side effect. If there is any existing requirement c2 such that c1.intersect(c2) returns a constraint that is not equal to c1, then that constraint is added as a preference instead. Otherwise, c1 is added as a preference. SecurityConstraints instance s implies SecurityConstraint instance c if there exists a requirement r in s such that r implies c. For two SecurityConstraints instances c1 and c2, c1 implies c2 if c1 implies each requirement in c2. The setReadOnly method makes the instance read only. It has no effect if the instance is already read-only. The isReadOnly method returns true if the instance is read only, and false if the instance is mutable. The isEmpty method returns true if the instance has no requirements and no preferences, and returns false otherwise. The copyReadOnly method returns a read-only instance that is equal to the parameter, or a read-only empty instance if the parameter value is null. The combineAbsolute method returns a read-only SecurityConstraints instance with all of the requirements and preferences of both parameters added, plus all of the requirements and preferences of the current context constraints (as returned by Security.getContextConstraints, see Section 3.2) added, and with every requirement and preference that is an instance of RelativeTimeConstraint replaced by the result of invoking the constraint’s makeAbsolute method with the current time (as returned by System.currentTimeMillis). A null parameter value is treated the same as an empty instance. This method can be used by a custom proxy implementation to combine the current context constraints with the proxy’s client and server constraints, in preparation for a remote call. Two SecurityConstraints instances are equal if they have equal requirements sets and equal preferences sets, independent of whether either or both instances are mutable or read-only. If the clone method is invoked on a read-only instance, the return value may be the identical object.
2.13
Trusted Classes
The SecurityConstraint classes that are trusted can be extended beyond those defined in this specification using security providers. A list of TrustVerifier instances (see Section 5.1) is obtained as specified in Section 5. The trustedConstraintClass method of each one is called with the constraint class. EARLY LOOK DRAFT 2
21
JAVA™ RMI SECURITY EXTENSION
If any method returns true, the class is trusted. If none of the methods returns true, SecurityException is thrown. The Principal classes that are trusted are defined in a similar fashion to constraint classes. The trustedPrincipalClass method of each TrustVerifier instance is called with the principal class. If any method returns true, the class is trusted. If none of the methods returns true, SecurityException is thrown.
2.14
Implies
The detailed semantics of the implies method for each constraint class, and the impliedBy method of AlternativeSecurityConstraints, are given in this Section. In general, application developers do not need to be concerned with the details provided in this Section. 2.14.1
Basic Constraints
For each basic constraint class (those with YES and NO constants), instance c1 of the class implies instance c2 of the same class if the two instances are equal. AuthenticateClient.NO implies any instance of class Delegation, DelegationDuration, DelegationEndTime, ClientMinPrincipal, ClientMinPrincipalType, ClientMaxPrincipal, or ClientMaxPrincipalType. AuthenticateServer.NO implies any instance of class ServerMinPrincipal. Delegation.NO implies any instance of class DelegationDuration or DelegationEndTime. For each basic constraint class, instance c1 of the class implies AlternativeSecurityConstraints instance c2 if c2.impliedBy(c1) is true. For any other instance c2, c1.implies(c2) is false. 2.14.2
DelegationDuration
DelegationDuration
instance c1 implies DelegationDuration instance c2 if
c1.getMin() >= c2.getMin() && c1.getMax() <= c2.getMax()
is true. DelegationDuration instance c1 implies AlternativeSecurityConstraints instance c2 if c2.impliedBy(c1) For any other instance c2, c1.implies(c2) is false.
is true.
EARLY LOOK DRAFT 2
22
CONSTRAINTS
2.14.3
DelegationEndTime
DelegationEndTime
instance c1 implies DelegationEndTime instance c2 if
c1.getMin() >= c2.getMin() && c1.getMax() <= c2.getMax()
is true. DelegationEndTime instance c1 implies AlternativeSecurityConstraints instance c2 if c2.impliedBy(c1) For any other instance c2, c1.implies(c2) is false.
2.14.4
is true.
ClientMinPrincipal
ClientMinPrincipal instance c1 implies ClientMinPrincipal instance c2 if, for each principal in c2, there exists an equal principal in c1. ClientMinPrincipal instance c1 implies ClientMinPrincipalType instance c2 if, for each class c in c2, there exists a principal p in c1 such that c.isInstance(p) is true. ClientMinPrincipal instance c1 implies AlternativeSecurityConstraints instance c2 if c2.impliedBy(c1) is true. For any other instance c2, c1.implies(c2) is false.
2.14.5
ClientMaxPrincipal
ClientMaxPrincipal instance c1 implies ClientMaxPrincipal instance c2 if, for each principal in c1, there exists an equal principal in c2. ClientMaxPrincipal instance c1 implies ClientMaxPrincipalType instance c2 if, for each principal p in c1, there exists a class c in c2 such that c.isInstance(p) is true. ClientMaxPrincipal instance c1 implies AlternativeSecurityConstraints instance c2 if c2.impliedBy(c1) is true. For any other instance c2, c1.implies(c2) is false.
2.14.6
ClientMinPrincipalType
ClientMinPrincipalType instance c1 implies ClientMinPrincipalType instance c2 if, for each class t2 in c2, there exists a class t1 in c1 such that t2.isAssignableFrom(t1) is true. ClientMinPrincipalType instance c1 implies AlternativeSecurityConstraints instance c2 if c2.impliedBy(c1) is true.
EARLY LOOK DRAFT 2
23
JAVA™ RMI SECURITY EXTENSION
For any other instance c2, c1.implies(c2) is false. 2.14.7
ClientMaxPrincipalType
ClientMaxPrincipalType instance c1 implies ClientMaxPrincipalType instance c2 if, for each class t1 in c1, there exists a class t2 in c2 such that t2.isAssignableFrom(t1) is true. ClientMaxPrincipalType instance c1 implies AlternativeSecurityConstraints instance c2 if c2.impliedBy(c1) is true. For any other instance c2, c1.implies(c2) is false.
2.14.8
ServerMinPrincipal
ServerMinPrincipal instance c1 implies ServerMinPrincipal instance c2 if, for each principal in c2, there exists an equal principal in c1. ServerMinPrincipal instance c1 implies AlternativeSecurityConstraints instance c2 if c2.impliedBy(c1) is true. For any other instance c2, c1.implies(c2) is false.
2.14.9
AlternativeSecurityConstraints
AlternativeSecurityConstraints instance c1 implies constraint c2 (of any type, not just AlternativeSecurityConstraints) if for each constraint c in c1, c.implies(c2) is true. AlternativeSecurityConstraints instance c1 is implied by constraint c2 (that is, c1.impliedBy(c2) is true) if there exists at least one constraint c in c1 such that c2.implies(c) is true. This method exists primarily as a convenience to help both specify and implement the implies methods of other constraint
classes when the method’s parameter is an instance of AlternativeSecurityConstraints.
2.15
Intersect
The detailed semantics of the intersect method for each constraint class are given in this Section. In general, application developers do not need to be concerned with the details provided in this Section.
EARLY LOOK DRAFT 2
24
CONSTRAINTS
2.15.1
Basic Constraints
For any two instances c1 and c2 of the same basic constraint class (those with YES and NO constants), c1.intersect(c2) returns c1 if the instances are equal, and returns null otherwise. For any Delegation instance c, c.intersect(AuthenticateClient.NO) returns AuthenticateClient.NO. For any other instance c1 of a basic constraint class and any other constraint instance c2, c1.intersect(c2) returns c1 if c1.implies(c2) is true, and returns COMPLEX_INTERSECT otherwise. 2.15.2
DelegationDuration
For DelegationDuration instances c1 and c2, c1.intersect(c2) returns a DelegationDuration instance with a min duration of Math.max(c1.getMin(), c2.getMin())
and a max duration of Math.min(c1.getMax(), c2.getMax())
if the computed min is less than or equal to the computed max. Otherwise the result is null. For DelegationDuration instance c, c.intersect(Delegation.NO) returns Delegation.NO and c.intersect(AuthenticateClient.NO) returns AuthenticateClient.NO. For DelegationDuration instance c1 and AlternativeSecurityConstraints instance c2, the semantics of c1.intersect(c2) is described under AlternativeSecurityConstraints for the symmetric c2.intersect(c1). For DelegationDuration instance c1 and any other constraint instance c2, c1.intersect(c2) returns COMPLEX_INTERSECT. 2.15.3
DelegationEndTime
For DelegationEndTime instances c1 and c2, c1.intersect(c2) returns a DelegationEndTime instance with a min end time of Math.max(c1.getMin(), c2.getMin())
and a max end time of Math.min(c1.getMax(), c2.getMax()) EARLY LOOK DRAFT 2
25
JAVA™ RMI SECURITY EXTENSION
if the computed min is less than or equal to the computed max. Otherwise the result is null. For DelegationEndTime instance c, c.intersect(Delegation.NO) returns Delegation.NO and c.intersect(AuthenticateClient.NO) returns AuthenticateClient.NO. For DelegationEndTime instance c1 and AlternativeSecurityConstraints instance c2, the semantics of c1.intersect(c2) is described under AlternativeSecurityConstraints for the symmetric c2.intersect(c1). For DelegationEndTime instance c1 and any other constraint instance c2, c1.intersect(c2) returns COMPLEX_INTERSECT. 2.15.4
ClientMinPrincipal
For ClientMinPrincipal instances c1 and c2, c1.intersect(c2) returns a ClientMinPrincipal constructed from the combined principals of both c1 and c2. For ClientMinPrincipal instance c1 and ClientMinPrincipalType instance c2, if c1.implies(c2) is true, then the result of c1.intersect(c2) is equal to c1, otherwise the result is COMPLEX_INTERSECT. For ClientMinPrincipal instance c1 and ClientMaxPrincipal instance c2, c1.intersect(c2) returns null if the principals in c1 are not a subset of the principals in c2, and returns COMPLEX_INTERSECT otherwise. For ClientMinPrincipal instance c1 and ClientMaxPrincipalType instance c2, c1.intersect(c2) returns null if there exists a principal in c1 that is not an instance of any class in c2, and returns COMPLEX_INTERSECT otherwise. For ClientMinPrincipal instance c, c.intersect(AuthenticateClient.NO) returns AuthenticateClient.NO. For ClientMinPrincipal instance c1 and AlternativeSecurityConstraints instance c2, the semantics of c1.intersect(c2) is described under AlternativeSecurityConstraints for the symmetric c2.intersect(c1). For ClientMinPrincipal instance c1 and any other constraint instance c2, c1.intersect(c2) returns COMPLEX_INTERSECT. 2.15.5
ClientMaxPrincipal
For ClientMaxPrincipal instances c1 and c2, c1.intersect(c2) returns a ClientMaxPrincipal constructed from the principals common to both c1 and c2, or null if there are no principals in common. EARLY LOOK DRAFT 2
26
CONSTRAINTS
For ClientMaxPrincipal instance c1 and ClientMaxPrincipalType instance c2, c1.intersect(c2) returns a ClientMaxPrincipal constructed from the principals of c1 that are instances of at least one of the classes in c2, or null if there are no such principals. For ClientMaxPrincipal instance c1 and ClientMinPrincipal instance c2, c1.intersect(c2) returns null if the principals in c2 are not a subset of the principals in c1, and returns COMPLEX_INTERSECT otherwise. For ClientMaxPrincipal instance c1 and ClientMinPrincipalType instance c2, c1.intersect(c2) returns null if there exists a class in c2 such that no principal in c1 is an instance of that class, and returns COMPLEX_INTERSECT otherwise. For ClientMaxPrincipal instance c, c.intersect(AuthenticateClient.NO) returns AuthenticateClient.NO. For ClientMaxPrincipal instance c1 and AlternativeSecurityConstraints instance c2, the semantics of c1.intersect(c2) is described under AlternativeSecurityConstraints for the symmetric c2.intersect(c1). For ClientMaxPrincipal instance c1 and any other constraint instance c2, c1.intersect(c2) returns COMPLEX_INTERSECT. 2.15.6
ClientMinPrincipalType
For ClientMinPrincipalType instances c1 and c2, c1.intersect(c2) returns a ClientMinPrincipalType constructed from the combined classes of both c1 and c2. For ClientMinPrincipalType instance c1 and ClientMinPrincipal instance c2, if c2.implies(c1) is true, then the result of c1.intersect(c2) is equal to c2, otherwise the result is COMPLEX_INTERSECT. For ClientMinPrincipalType instance c1 and ClientMaxPrincipal instance c2, c1.intersect(c2) returns null if there exists a class in c1 such that no principal in c2 is an instance of that class, and returns COMPLEX_INTERSECT otherwise. For ClientMinPrincipalType instance c1 and ClientMaxPrincipalType instance c2, c1.intersect(c2) returns null if there exists a class t1 in c1 such that for every class t2 in c2, t2.isAssignableFrom(t1) is false, and returns COMPLEX_INTERSECT otherwise. For ClientMinPrincipalType instance c, c.intersect(AuthenticateClient.NO) returns AuthenticateClient.NO. For ClientMinPrincipalType instance c1 and AlternativeSecurityConstraints instance c2, the semantics of EARLY LOOK DRAFT 2
27
JAVA™ RMI SECURITY EXTENSION
c1.intersect(c2) is described under AlternativeSecurityConstraints for the symmetric c2.intersect(c1). For ClientMinPrincipalType instance c1 and any other constraint instance c2, c1.intersect(c2) returns COMPLEX_INTERSECT.
2.15.7
ClientMaxPrincipalType
For ClientMaxPrincipalType instances c1 and c2, a reduced set of classes is computed as follows: for each pair of class t1 in c1 and class t2 in c2, if t1.isAssignableFrom(t2) then t2 is included in the reduced set, or if t2.isAssignableFrom(t1) then t1 is included in the reduced set. If the reduced set is empty, then the result of c1.intersect(c2) is null, otherwise the result is a ClientMaxPrincipalType constructed from the reduced set. For ClientMaxPrincipalType instance c1 and ClientMaxPrincipal instance c2, c1.intersect(c2) returns a ClientMaxPrincipal constructed from the principals of c2 that are instances of at least one of the classes in c1, or null if there are no such principals. For ClientMaxPrincipalType instance c1 and ClientMinPrincipal instance c2, c1.intersect(c2) returns null if there exists a principal in c2 that is not an instance of any class in c1, and returns COMPLEX_INTERSECT otherwise. For ClientMaxPrincipalType instance c1 and ClientMinPrincipalType instance c2, c1.intersect(c2) returns null if there exists a class t2 in c2 such that for every class t1 in c1, t1.isAssignableFrom(t2) is false, and returns COMPLEX_INTERSECT otherwise. For ClientMaxPrincipalType instance c, c.intersect(AuthenticateClient.NO) returns AuthenticateClient.NO. For ClientMaxPrincipalType instance c1 and AlternativeSecurityConstraints instance c2, the semantics of c1.intersect(c2) is described under AlternativeSecurityConstraints for the symmetric c2.intersect(c1). For ClientMaxPrincipalType instance c1 and any other constraint instance c2, c1.intersect(c2) returns COMPLEX_INTERSECT. 2.15.8
ServerMinPrincipal
For ServerMinPrincipal instances c1 and c2, c1.intersect(c2) returns a ServerMinPrincipal constructed from the combined principals of both c1 and c2. For ServerMinPrincipal instance c, c.intersect(AuthenticateServer.NO) returns AuthenticateServer.NO. EARLY LOOK DRAFT 2
28
CONSTRAINTS
For ServerMinPrincipal instance c1 and AlternativeSecurityConstraints instance c2, the semantics of c1.intersect(c2) is described under AlternativeSecurityConstraints for the symmetric c2.intersect(c1). For ServerMinPrincipal instance c1 and any other constraint instance c2, c1.intersect(c2) returns COMPLEX_INTERSECT. 2.15.9
AlternativeSecurityConstraints
For AlternativeSecurityConstraints instance c1 and any other constraint instance c2 that is not itself an AlternativeSecurityConstraints instance, the result of c1.intersect(c2) is obtained in the following manner. For each constraint c in c1, c.intersect(c2) is invoked. If any return value is COMPLEX_INTERSECT, then the result of c1.intersect(c2) is COMPLEX_INTERSECT. Otherwise, the null values are discarded and redundant constraints are removed from the non-null values in the same way as in the AlternativeSecurityConstraints create method. If what remains is a single constraint, then the result of c1.intersect(c2) is that single constraint. Otherwise the result is an AlternativeSecurityConstraints instance containing the remaining constraints. For AlternativeSecurityConstraints instances c1 and c2, the result of c1.intersect(c2) is obtained in the following manner. For each possible combination of constraint x1 in c1 and constraint x2 in c2, x1.intersect(x2) is invoked. If any return value is COMPLEX_INTERSECT, then the result of c1.intersect(c2) is COMPLEX_INTERSECT. Otherwise, the null values are discarded and redundant constraints are removed from the non-null values in the same way as in the AlternativeSecurityConstraints create method. If what remains is a single constraint, then the result of c1.intersect(c2) is that single constraint. Otherwise the result is an AlternativeSecurityConstraints instance containing the remaining constraints.
EARLY LOOK DRAFT 2
29
JAVA™ RMI SECURITY EXTENSION
3
Client Interfaces
3.1
RemoteSecurity
Client security constraints can be attached to individual stubs, and server constraints can be queried, using the RemoteSecurity interface: package javax.rmi.security; public interface RemoteSecurity { RemoteSecurity setClientConstraints( SecurityConstraints constraints); SecurityConstraints getClientConstraints(); SecurityConstraints getServerConstraints(String name, Class[] parameterTypes) throws NoSuchMethodException, RemoteException; boolean equalsIgnoreConstraints(Object obj); }
Secure RMI stubs implement this (local) RemoteSecurity interface. Servers that define proxy interfaces to remote services are encouraged to implement this interface as well, to give applications a uniform way to control security constraints. The setClientConstraints method allows client constraints to be attached to a copy of the proxy; these constraints completely replace (in the copy) any current client constraints attached to the proxy. The constraints are copied at the time it is passed in, and null is treated the same as an empty instance. The method returns the new copy of the proxy. The getClientConstraints method returns the current client constraints, as a non-null read-only instance. Client constraints attached to a proxy are included in the serialized state of the proxy; this is to allow a service to be transparent to the client’s needs. For example, if remote object S 1 obtains a proxy for remote object S 2 , and passes that proxy to remote object S 3 , expecting S 3 to invoke a method on S 2 , then S 1 can control the security of that call by attaching its constraints directly to the proxy before passing it to S 3 . If a service does not wish to be transparent in this way, EARLY LOOK DRAFT 2
30
CLIENT INTERFACES
then it should explicitly reset the constraints on received proxies to whatever is appropriate to implement its own security policy. The getServerConstraints method returns the server’s constraints. The values can vary with the remote method, so the particular method must be specified by giving the method name and parameter types. The value of parameterTypes can be null, which is treated the same as an empty array. The method returns a non-null read-only instance. IllegalArgumentException is thrown if the specified method exists but is not a remote method. For stubs created using SecureUnicastRemoteObject (see Section 4.1), this method will never actually throw RemoteException; the state is constant, and is maintained inside the stub’s reference. For stubs created using SecureActivatable (see Section 4.2), this method can throw RemoteException, and the return values can change over time; the state is maintained inside the transient live reference. Proxies that implement RemoteSecurity can choose whether this state is constant or variable. The equalsIgnoreConstraints method of a stub ignores both client and server security constraints; it simply checks that the two stubs are for the same remote object. The equals method includes both client and server constraints. Proxy classes that implement RemoteSecurity should implement equivalent semantics for equals and equalsIgnoreConstraints.
3.2
Security
The Security class provides methods for executing actions with contextual client security constraints, and for establishing trust in downloaded stubs and proxies: package javax.rmi.security; public final class Security { public static Object doConstrained( PrivilegedAction action, SecurityConstraints constraints); public static Object doConstrained( PrivilegedExceptionAction action, SecurityConstraints constraints) throws PrivilegedActionException; public static SecurityConstraints getContextConstraints(); public static void establishProxyTrust( Object proxy, SecurityConstraints constraints) throws RemoteException; }
EARLY LOOK DRAFT 2
31
JAVA™ RMI SECURITY EXTENSION
Client constraints can be set contextually using the doConstrained methods. A null parameter will result in NullPointerException being thrown. Constraints set in this way will be applied to all secure RMI calls made by this thread during execution of the action’s run method. The object returned by the run method is returned by doConstrained. The constraints are effectively copied before the action is performed; changes to the parameter instance while the action is executing have no effect on the constraints applied to the action. When a new thread is created, the active client constraints for the creating thread become the base constraints for the new thread. When an RMI remote object is exported, the active client constraints at the time of export become the base constraints for all subsequent incoming remote calls to that object. The getContextConstraints method returns all of the active client constraints for this thread, including all active doConstrained frames and any base constraints for the thread. The method returns a non-null read-only instance. The establishProxyTrust method should be called by a client with a downloaded proxy, before the client makes any other use of the proxy, in order to establish basic trust that the proxy will correctly implement the RemoteSecurity interface. In the simplest case, the proxy is implemented using only trusted code, and establishProxyTrust simply has to verify that the proxy code is in fact trusted. In the more general case, the server should first be authenticated (using code that is known to be trusted) as a subject trusted by the client, and then the server is asked if it trusts the proxy code. If the server trusts the proxy, and the client trusts the server, then the client can transitively trust the proxy. If the proxy relies on activation, then the activator should be authenticated as a subject trusted by the server, and it must be verified that all future calls to the activator will continue to authenticate the activator as a subject trusted by the server. If the server trusts the activator, and the client trusts the server, then the client again can transitively trust the activator. A proxy is a secure RMI stub if Proxy.isProxyClass(proxy.getClass()) is true, and if its InvocationHandler instance is an instance of a class trusted by the secure RMI implementation. (See http://java.sun.com/products/jdk/1.3/docs/ guide/reflection/proxy.html for the specification of Proxy and InvocationHandler.) The establishProxyTrust method first checks to see if the proxy implements RemoteSecurity. If it does not, SecurityException is thrown. If the proxy is a secure RMI stub for a server exported as a SecureUnicastRemoteObject (see Section 4.1), each SecureClientSocketFactory instance (see Section 5.4) contained in the proxy is checked to ensure it is an instance of a trusted class, by consulting a list of TrustVerifier instances (see Section 5.1). The trustedClientSocketFactoryClass method of each TrustVerifier instance EARLY LOOK DRAFT 2
32
CLIENT INTERFACES
is called with the factory class as a parameter. If any method returns true, the class is trusted. If none of the methods return true, SecurityException is thrown. In this case, no remote communication takes place, and the constraints passed as a parameter are not used. If the proxy is a secure RMI stub for a server exported as a SecureActivatable (see Section 4.2) the following checks are made: ◆
A remote call is made through the InvocationHandler instance for the getActivatorConstraints method of the ActivatorSecurity interface (see Section 4.3), activating the server as necessary. This call must return normally. The client constraints used for the call are the constraints passed as a parameter to establishProxyTrust. The caller is responsible for specifying AuthenticateServer.YES and an appropriate ServerMinPrincipal instance as requirements, if it wants the server to be authenticated. Prior to the call, each SecureClientSocketFactory instance contained in the proxy is checked to ensure it is an instance of a trusted class, in the same manner as described above.
◆
A call to establishProxyTrust, passing the ActivationID (as the proxy) and the constraints returned by getActivatorConstraints, must return normally. The server is responsible for specifying AuthenticateServer.YES and an appropriate ServerMinPrincipal instance as requirements in those constraints, if it wants the activator to be authenticated.
◆
The constraints returned by getActivatorConstraints must be the same as the client constraints obtained from the ActivationID using its RemoteSecurity getClientConstraints method.
If these checks do not succeed, SecurityException is thrown. If the call to or the nested call to establishProxyTrust throws an exception, that exception is thrown to the caller. If the proxy is not a secure RMI stub, but the proxy’s class has a non-static non-public declared (not inherited) method with signature getActivatorConstraints
CheckProxySource getSecureProxy();
the following checks are made: ◆
The value returned by that getSecureProxy method must itself be trusted; that is, a call to establishProxyTrust, passing that return value and the same constraints parameter, must return normally.
◆
A call to that return value’s checkProxySource method (see Section 4.4), passing the codebase and signers of the proxy’s class, must return normally; EARLY LOOK DRAFT 2
33
JAVA™ RMI SECURITY EXTENSION
the client constraints used for the call are the constraints passed as a parameter to establishProxyTrust. The client is responsible for specifying AuthenticateServer.YES and an appropriate ServerMinPrincipal instance as requirements, if it wants the server to be authenticated. If these checks do not succeed, SecurityException is thrown. If the call to checkProxySource or the nested call to establishProxyTrust throws an exception that is thrown to the caller. If the proxy is not a secure RMI stub, and does not have the appropriate getSecureProxy method, then an ordered list of TrustVerifier instances (see Section 5.1) is obtained as specified in Section 5. The trustedProxy method of each one is called (in order) with the proxy and constraints parameters. If any method returns true, establishProxyTrust returns normally. If any method throws an exception, that exception is thrown to the caller. If none of the methods returns true, SecurityException is thrown. 3.3
UnsupportedSecurityException
There is one new security-related exception: package javax.rmi.security; public class UnsupportedSecurityException extends RemoteException { public SecurityConstraints constraints; public UnsupportedSecurityException( SecurityConstraints constraints); public UnsupportedSecurityException( String s, SecurityConstraints constraints); }
This exception is only checked and thrown at the point a remote method is invoked. For example, it is possible to attach a client requirement to a stub that is not supported by the server, or that is not supported by the RMI implementation in which the client is executing, without an exception immediately being thrown. UnsupportedSecurityException can be thrown at the point a remote method is invoked for any of the following reasons: ◆
A client requirement is not supported by the server.
EARLY LOOK DRAFT 2
34
CLIENT INTERFACES ◆
A client or server requirement conflicts with some other client or server requirement.
◆
A client or server requirement cannot be satisfied by the RMI implementation in which the client is executing.
◆
For a client or server requirement, the RMI implementation in which the client is executing does not implement any algorithm in common with the server.
◆
A delegated remote call is being attempted, and the current time is either earlier than the minimum granted delegation time or later than the maximum granted delegation time.
The combined client and server constraints for the call are given as a non-null read-only instance in a field of the exception. No specific indication is given as to which requirements could not be satisfied. The UnsupportedSecurityException constructor produces a non-null readonly copy of the constraints (which can be null) passed as a parameter.
EARLY LOOK DRAFT 2
35
JAVA™ RMI SECURITY EXTENSION
4
Server Interfaces
4.1
SecureUnicastRemoteObject
There is a new class for implementing secure unicast remote objects: package javax.rmi.security.server; public class SecureUnicastRemoteObject extends RemoteServer { protected SecureUnicastRemoteObject(SecureExportDesc desc) throws RemoteException; public static Remote exportObject(Remote obj, SecureExportDesc desc) throws RemoteException; public static SecurityConstraints getSecurityConstraints() throws ServerNotActiveException; public static Subject getClientSubject() throws ServerNotActiveException; }
As with UnicastRemoteObject, it is not necessary to extend SecureUnicastRemoteObject; you can use the static exportObject
method instead. An active remote method can use the getSecurityConstraints method to obtain the constraints that are in force for the current call. A non-null read-only instance is returned that has only requirements, no preferences, and no relativetime constraints. ServerNotActiveException is thrown if the current thread is not executing an incoming remote method. An active remote method can use the getClientSubject method to obtain the authenticated identity of the client for the current call. This requires a TBD security permission. Null is returned if the client was not authenticated. ServerNotActiveException is thrown if the current thread is not executing an incoming remote method. If the client delegated to the server, then the returned subject contains any derived delegation credentials. The server can then impersonate the client by performing outbound secure calls (or by receiving incoming
EARLY LOOK DRAFT 2
36
SERVER INTERFACES
secure calls) in the context of a Subject.doAs invoked with the client subject as a parameter. The parameters passed to the remote method are automatically unmarshalled within the scope of either a Subject.doAs or a Subject.doAsPrivileged with the client subject. If the server implements CallAccessControl (see Section 4.5) and the return value from its checkCallAccessControl method is true, then doAsPrivileged is used, otherwise doAs is used.
4.2
SecureActivatable
There is a new class for implementing secure activatable objects: package javax.rmi.security.activation; public abstract class SecureActivatable extends RemoteServer { protected SecureActivatable( String location, MarshalledObject data, boolean restart, SecureExportDesc desc, SecurityConstraints activatorConstraints) throws ActivationException, RemoteException; protected SecureActivatable( ActivationID id, SecureExportDesc desc, SecurityConstraints activatorConstraints) throws RemoteException; protected ActivationID getID(); public static Remote register( ActivationDesc desc, SecurityConstraints activatorConstraints) throws ActivationException, RemoteException; public static ActivationID exportObject( Remote obj, String location, MarshalledObject data, boolean restart, SecureExportDesc desc, SecurityConstraints activatorConstraints) throws ActivationException, RemoteException; public static Remote exportObject(
EARLY LOOK DRAFT 2
37
JAVA™ RMI SECURITY EXTENSION
Remote obj, ActivationID id, SecureExportDesc desc) throws RemoteException; }
The first constructor can be used to register a new object and export it. A subclass of SecureActivatable must call this constructor to register and export the object during initial construction. The second constructor is used to activate and export an object that has already been registered. A subclass of SecureActivatable must call this constructor when the object itself is activated using its special “activation” constructor. The getID method returns the object’s activation identifier. This activation identifier implements RemoteSecurity, and the activator constraints passed to the constructor will have been set as client constraints by the constructor. These constraints will also be set as client constraints in the ActivationID contained in the stub for this activatable object. The register method registers a new activatable object with the activation system without having to first create that activatable object; it will subsequently be activated on demand. The activator constraints are set as client constraints on the ActivationID contained in the returned stub. As with Activatable, it is not necessary to extend SecureActivatable; you can use an exportObject method instead. The first exportObject method both registers the object as a new activatable object, and exports it; the activator constraints are set as client constraints on the returned ActivationID, and will also be set as client constraints in the ActivationID contained in the stub for this activatable object. The second exportObject method exports an activatable object that has already been registered; it is assumed that any desired activator constraints have already been set as client constraints on the ActivationID.
4.3
ActivatorSecurity
The ActivatorSecurity interface is defined as: package javax.rmi.security.activation; public interface ActivatorSecurity { SecurityConstraints getActivatorConstraints() throws RemoteException; }
EARLY LOOK DRAFT 2
38
SERVER INTERFACES
The getActivatorConstraints method returns a non-null read-only instance. An activatable object does not implement the ActivatorSecurity interface directly. Instead, the dispatcher used to handle incoming remote calls will automatically respond to the getActivatorConstraints method with the activator constraints that were set as client constraints on the ActivationID when the object was exported. However, the getActivatorConstraints method can be specified in a MethodConstraints in the SecureExportDesc when exporting a secure activatable object, and preinvocation access control (see Section 4.5) is also supported for this method.
4.4
CheckProxySource
The CheckProxySource interface is defined as: package javax.rmi.security.server; public interface CheckProxySource extends Remote { void checkProxySource(String codebase, Object[] signers) throws RemoteException; }
Remote objects that use specialized proxies (proxies that are not secure RMI stubs) must implement this interface in order to allow clients to use Security.establishProxyTrust. Either or both parameters to the checkProxySource method may be null. If these parameters identify proxy code that is considered trusted by the server, then the method should just return normally. Otherwise it should throw an exception.
4.5
CallAccessControl
A remote object that exports itself as a SecureUnicastRemoteObject or SecureActivatable can perform preinvocation access control, and exercise control over parameter unmarshalling, by implementing the CallAccessControl interface: package javax.rmi.security.server; public interface CallAccessControl { boolean checkCallAccessControl(Subject clientSubject, Method method); }
EARLY LOOK DRAFT 2
39
JAVA™ RMI SECURITY EXTENSION
The checkCallAccessControl method will be called prior to each remote method invocation, before the parameters of the call are unmarshalled. The first parameter is the authenticated identity of the client, or null if the client was not authenticated. The second parameter is the method that will be invoked. To prevent the invocation, the method should throw a runtime exception; the exception will be propagated back to the client. To allow the invocation to proceed, the checkCallAccessControl method should return normally. The return value is used as the privileged mode for unmarshalling parameters of the remote call (see Section 4.1). RemoteServer.getClientHost can be called from within checkCallAccessControl.
4.6
SecureExportDesc
The SecureExportDesc class is defined as: package javax.rmi.security.server; public class SecureExportDesc { public SecureExportDesc( SecurityConstraints defaultConstraints) throws UnsupportedSecurityException; public SecureExportDesc( SecurityConstraints defaultConstraints, MethodConstraints[] methodConstraints, Subject serverSubject) throws UnsupportedSecurityException; public SecureExportDesc( SecurityConstraints defaultConstraints, MethodConstraints[] methodConstraints, Subject serverSubject, Int[] ports, SecureServerSocketFactory[] factories) throws UnsupportedSecurityException; public SecurityConstraints getDefaultConstraints(); public MethodConstraints[] getMethodConstraints(); public Subject getSubject(); public int[] getPorts(); public SecureServerSocketFactory[] getFactories(); }
The semantics of the constructor parameters are: EARLY LOOK DRAFT 2
40
SERVER INTERFACES ◆ defaultConstraints – the default constraints for all DGC calls and for any
remote method that is not specified in methodConstraints. This value can be null, which is treated the same as the empty instance. ◆ methodConstraints
– constraints for specific remote methods. If null or unspecified, defaults to the empty array. ExportException is thrown at export time if any of the methods specified are not actually remote methods for the exported object.
◆ serverSubject – the maximal subject that will be used when authenticating
the server to clients. The authenticated principals obtained by clients will always be a subset of the principals in this subject. This value can be null. If null or unspecified, defaults to the current executing subject (the value of Subject.getSubject(AccessController.getContext())). ◆ ports
– specific ports to export the object on. If null or unspecified, anonymous ports are used as needed. If non-null, then factories must also be nonnull and the same length, or IllegalArgumentException is thrown. Each port is paired with the corresponding server factory at the same index. A port value of zero means that an anonymous port should be used. No two factories can be assigned the same non-zero port, or IllegalArgumentException is thrown.
◆ factories – server factories (see Section 5.2) for generating secure sockets.
If null or unspecified, suitable factories are found as specified below. In general, portable implementations will leave both ports and factories either null or unspecified, and most developers do not need to be concerned with the detailed semantics of socket factories. Two SecureExportDesc instances are equal only if they are the identical (==) object. The getDefaultConstraints method returns a non-null read-only instance. The getMethodConstraints method always returns a new non-null array every time it is called. Given a secure RMI stub for a remote object that was exported with this export descriptor, the getServerConstraints method (see Section 3.1) of that stub will return the constraints given in methodConstraints if there is a match, otherwise the default constraints will be returned. The getSubject method returns a non-null instance, or null if the serverSubject was null or unspecified and the value of Subject.getSubject(AccessController.getContext()) is null. The getPorts method returns a new non-null array every time it is called. If the ports constructor parameter was null or unspecified, this method returns an array filled with zeros. EARLY LOOK DRAFT 2
41
JAVA™ RMI SECURITY EXTENSION
The getFactories method returns a new non-null array every time it is called. If server factories are not specified in the constructor, an ordered list of candidate factories is obtained as specified in Section 5. A factory is selected from this list if there are constraints for at least one remote method such that the factory’s supportsConstraints method returns true when invoked with the remote method’s constraints and the serverSubject. The selected factories remain in their original order in the list. If server factories are specified in the constructor, then for each factory, there must be at least one remote method such that the factory’s supportsConstraints method returns true when invoked with the remote method’s constraints and the serverSubject, or IllegalArgumentException is thrown. Regardless of whether the server factories are specified or generated, it must also be the case that for each remote method, there is at least one factory whose supportsConstraints method returns true when invoked with the remote method’s constraints and the serverSubject, or UnsupportedSecurityException is thrown. Multiple objects can be exported on the same port, provided that the server factory and server subject are always the same (using the equals method). The server factories are not transmitted in the remote reference; only the derived client factories are transmitted. The order of factories is significant. When a remote call is attempted, the “best” supported preferences are computed for each derived client factory using the choosePreferences method (see Section 5.4). The client factory used for the call will be the first client factory that supports all of the requirements, and whose resulting constraints are either not implied by or are equal to the resulting constraints of every other factory. MethodConstraints
4.7
MethodConstraints
is defined as:
package javax.rmi.security.server; public class MethodConstraints implements Serializable { public MethodConstraints(String name, Class[] parameterTypes, SecurityConstraints constraints); public String getName(); public Class[] getParameterTypes(); public SecurityConstraints getConstraints(); } EARLY LOOK DRAFT 2
42
SERVER INTERFACES
The name and parameterTypes specify the particular remote method to which the specified security constraints apply. The constraints can be null, which is treated the same as the empty instance. The value of parameterTypes can be null, which is treated the same as an empty array. The getParameterTypes method returns a new non-null array every time it is called. The getConstraints method returns a non-null read-only instance. Two MethodConstraints instances are equal if they have equal names, equal parameter types, and equal constraints.
EARLY LOOK DRAFT 2
43
JAVA™ RMI SECURITY EXTENSION
5
Provider Interfaces
Security providers are specified in this section. In general, application developers do not need to be concerned with the details provided in this section. Security providers are used to obtain ordered lists of trust verifiers (see Section 3.2) and socket factories (see Section 4.6). The general algorithm used to obtain instances from providers is the same in both cases, and is based on a property name prefix (prefix) and an interface (iface). For trust verifiers, the prefix is “rmi.verifier” and the interface is TrustVerifier (see Section 5.1). For socket factories, the prefix is “rmi.socket” and the interface is SecureServerSocketFactory (see Section 5.2). If there is a security property (as defined by the getProperty method of java.security.Security) named “prefix.1”, then for each security property named “prefix.n” (for sequential values of n starting from 1), if: ◆
the property value is of the form “name.type” such that the getProvider method of java.security.Security returns a provider for name, and
◆
that provider has a property named “prefix.type”, and
◆
the value of that property is the name of a class that implements iface and has a no-arg constructor, and
◆
no instance of that class already exists in the list,
then an instance of that class is created and added to the list. In this case, the order of instances is given by the security property order. If there is no security property named “prefix.1”, then each of the providers returned by the getProviders method of java.security.Security is searched, and for each property that a provider has with name prefixed by “prefix.”, if: ◆
the property value is the name of a class that implements iface and has a noarg constructor, and
◆
no instance of that class already exists in the list,
EARLY LOOK DRAFT 2
44
PROVIDER INTERFACES
then an instance of that class is created and added to the list. In this case, the order of instances generated from a given provider is unspecified, but instances from earlier providers precede instances from later providers in the resulting list. 5.1
TrustVerifier
TrustVerifier
is defined as:
package javax.rmi.security.spi; public interface TrustVerifier { boolean trustedConstraintClass(Class c); boolean trustedPrincipalClass(Class c); boolean trustedClientSocketFactoryClass(Class c); boolean trustedProxy(Object proxy, SecurityConstraints constraints) throws RemoteException; }
The trustedConstraintClass method returns true if the given class is known to be a trusted SecurityConstraint class, and false otherwise. This method does not need to return true for the standard constraint classes defined in Section 2. The trustedPrincipalClass method returns true if the given class is known to be a trusted Principal class for use in security constraints, and false otherwise. The trustedClientFactoryClass method returns true if the given class is known to be a trusted SecureClientSocketFactory class, and false otherwise. The trustedProxy method returns true if the given proxy is known to be trusted to correctly implement the RemoteSecurity interface, and false otherwise.
5.2
SecureServerSocketFactory
SecureServerSocketFactory
is defined as:
package javax.rmi.security.spi; public interface SecureServerSocketFactory { SecureClientSocketFactory getClientFactory(int port); boolean supportsConstraints( SecurityConstraints constraints,
EARLY LOOK DRAFT 2
45
JAVA™ RMI SECURITY EXTENSION
Subject serverSubject); ServerSocket createServerSocket(Subject serverSubject, int port) throws IOException; }
The getClientFactory method returns a SecureClientSocketFactory that is compatible with a ServerSocket created by this SecureServerSocketFactory for the specified port. The supportsConstraints method returns true if the factory can support the specified server constraints (any contained preferences are ignored) when coupled with the specified server subject, and returns false otherwise. The createServerSocket method returns a new ServerSocket that is listening on the specified port (or an anonymous port if the specified port is zero) and that will authenticate as appropriate using the specified serverSubject. Sockets created by the accept method of this ServerSocket must implement the SecureSocket interface. The equals method returns true if the parameter is a functionally equivalent SecureServerSocketFactory instance.
5.3
SecureSocket
SecureSocket
is defined as:
package javax.rmi.security.spi; public interface SecureSocket { SecurityConstraints getSecurityConstraints(); Subject getClientSubject(); }
The getSecurityConstraints method returns the constraints actually in use on the socket. A non-null read-only instance is returned that has only requirements, no preferences, and no relative-time constraints. The getClientSubject method returns the authenticated identity of the client, or null if the client has not been authenticated.
EARLY LOOK DRAFT 2
46
PROVIDER INTERFACES
5.4
SecureClientSocketFactory
SecureClientSocketFactory
is defined as:
package javax.rmi.security.spi; public interface SecureClientSocketFactory { SecurityConstraints choosePreferences( SecurityConstraints constraints, Subject clientSubject) throws IOException; Socket createSocket(SecurityConstraints constraints, Subject clientSubject) throws IOException; void checkConnect(); }
Classes that implement this interface should be serializable. The equals method returns true if the parameter is a functionally equivalent SecureClientSocketFactory instance. All instances returned by repeated calls to the getClientFactory method of a SecureServerSocketFactory with the same port, and all serialized copies of them, must be equal. The choosePreferences method chooses the “best” preferences (if any) that can be supported by the factory while simultaneously supporting all of the specified requirements, given the specified client subject. It also chooses the “best” constraints among any alternatives contained in instances of AlternativeSecurityConstraints. The meaning of “best” is implementation dependent. This method can assume that none of the constraints are based on relative time. The returned constraints must: imply all of the requirements from the parameter passed in; include whatever additional requirements are selected to satisfy the chosen preferences; and have no preferences and no instances of AlternativeSecurityConstraints. If not all of the requirements can be supported in conjunction with the specified client subject, then null is returned. The createSocket method returns a socket that will satisfy the specified security requirements (any preferences can be ignored), authenticating as necessary as the appropriate subset of the specified client subject. This method can assume that none of the constraints are based on relative time. If not all of the requirements can be supported, UnsupportedSecurityException is thrown. If the socket returned by createSocket supports reuse with different constraints it should implement the SecureSessionSocket interface. The checkConnect method throws SecurityException if the calling thread does not have permission to call createSocket. In the usual case, this method
EARLY LOOK DRAFT 2
47
JAVA™ RMI SECURITY EXTENSION
calls the checkConnect method of the installed security manager, passing the host and port that would be used to create the socket.
5.5
SecureSessionSocket
SecureSessionSocket
is defined as:
package javax.rmi.security.spi; public interface SecureSessionSocket { boolean supportsConstraints( SecurityConstraints constraints, Subject clientSubject); void switchConstraints(SecurityConstraints constraints, Subject clientSubject) throws IOException; }
The supportsConstraints method returns true if the socket supports switching to the specified constraints (any contained preferences are ignored) coupled with the specified client subject, and returns false otherwise. The switchConstraints method causes the socket to actually switch to the specified constraints (any contained preferences are ignored) and client subject. If not all of the requirements can be supported, UnsupportedSecurityException is thrown.
EARLY LOOK DRAFT 2
48
PROVIDER INTERFACES
EARLY LOOK DRAFT 2
49
JAVA™ RMI SECURITY EXTENSION
6
Serialization
The InvocationHandler classes used in secure RMI stubs are implementation specific. The class used for a server exported as a SecureUnicastRemoteObject must have a writeReplace method that returns an instance of SecureUnicastData: package javax.rmi.security.ref; public class SecureUnicastData implements Serializable { public SecurityConstraints clientConstraints; public SecurityConstraints defaultConstraints; public long[] methodHashes; public SecurityConstraints[] methodConstraints; public SecureClientSocketFactory[] factories; public ObjID id; }
The implementation of SecureUnicastData must have a readResolve method that returns an equivalent instance of the same InvocationHandler class. Similarly, the class used for a server exported as a SecureActivatable must have a writeReplace method that returns an instance of SecureActivatableData: package javax.rmi.security.ref; public class SecureActivatableData extends SecureUnicastData { public ActivationID aid; }
The implementation of SecureActivatableData must have a readResolve method that returns an equivalent instance of the same InvocationHandler class. The fields of SecureUnicastData and SecureActivatableData hold the following data: ◆ clientConstraints
- the client constraints as would be returned by the method on the proxy
RemoteSecurity getClientConstraints
EARLY LOOK DRAFT 2
50
SERIALIZATION ◆ defaultConstraints
SecureExportDesc
- the default server constraints specified in the when the object was exported
◆ methodHashes
- RMI method hashes for all of the methods specified in MethodConstraints in the SecureExportDesc when the object was exported, in arbitrary order
◆ methodConstraints - the constraints specified in the MethodConstraints
for the corresponding index of methodHashes ◆ factories
- the ordered list of client socket factories obtained from the server socket factories specified in the SecureExportDesc when the object was exported
◆ id
- the identifier for the server
◆ aid
- the activation identifier for the server
For SecureUnicastData instances, the factories and id fields must be nonnull. For SecureActivatableData instances, the aid field must be non-null.
EARLY LOOK DRAFT 2