/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.jini.jeri;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.security.PrivilegedAction;
import net.jini.core.constraint.MethodConstraints;
import net.jini.core.constraint.RemoteMethodControl;
import net.jini.security.Security;
import net.jini.security.TrustVerifier;
import net.jini.security.proxytrust.TrustEquivalence;
/**
* Trust verifier for dynamic proxies and object endpoints used in Jini
* extensible remote invocation (Jini ERI). This class is intended to be
* specified in a resource to configure the operation of {@link
* Security#verifyObjectTrust Security.verifyObjectTrust}.
*
* @author Sun Microsystems, Inc.
* @since 2.0
**/
public class BasicJeriTrustVerifier implements TrustVerifier {
/**
* Creates an instance.
*/
public BasicJeriTrustVerifier() {
}
/**
* Returns <code>true</code> if the specified object is a trusted
* Jini ERI dynamic proxy or a trusted Jini ERI object endpoint;
* returns <code>false</code> otherwise.
*
* <p>For the purposes of this verifier, the specified object is a trusted
* Jini ERI dynamic proxy if all of the following are true:
* <ul>
* <li>The object is an instance of {@link RemoteMethodControl}.
* <li>The object is an instance of a dynamic proxy class; that is, a
* class generated by {@link Proxy}.
* <li>Invoking the protected method {@link #hasTrustedProxyClass
* hasTrustedProxyClass} with the specified object and the specified
* trust verifier context returns <code>true</code>.
* <li>Invoking the protected method {@link #isTrustedInvocationHandler
* isTrustedInvocationHandler} with the {@link
* java.lang.reflect.InvocationHandler} contained in the dynamic proxy and
* the specified trust verifier context returns <code>true</code>.
* </ul>
*
* <p>For the purposes of this verifier, the specified object is a trusted
* Jini ERI object endpoint if the object is an instance of
* {@link BasicObjectEndpoint} and its transport endpoint is
* trusted; that is, calling the specified context's
* <code>isTrustedObject</code> method with the transport endpoint returns
* <code>true</code>.
*
* @throws SecurityException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
**/
public boolean isTrustedObject(Object obj, final TrustVerifier.Context ctx)
throws RemoteException
{
if (obj == null || ctx == null) {
throw new NullPointerException();
} else if (obj instanceof RemoteMethodControl) {
final Class c = obj.getClass();
if (Proxy.isProxyClass(c)) {
if (!hasTrustedProxyClass(obj, ctx)) {
return false;
}
InvocationHandler handler = Proxy.getInvocationHandler(obj);
return isTrustedInvocationHandler(handler, ctx);
}
} else if (obj.getClass() == BasicObjectEndpoint.class) {
BasicObjectEndpoint oe = (BasicObjectEndpoint) obj;
return ctx.isTrustedObject(oe.getEndpoint());
}
return false;
}
/**
* Returns <code>true</code> if the specified dynamic proxy's class is
* trusted by this verifier; returns <code>false</code> otherwise. The
* <code>isTrustedObject</code> method calls this method to verify that
* the proxy's class is trusted.
*
* <p><code>BasicJeriTrustVerifier</code> implements this method to
* return <code>true</code> if all of the following are true:
* <ul>
* <li>The proxy is an instance of {@link Remote}.
* <li>Invoking the protected method {@link #hasTrustedClassLoader
* hasTrustedClassLoader} with the specified proxy and the specified
* trust verifier context returns <code>true</code>.
* <li>For each direct superinterface of the proxy's class, invoking
* the protected method {@link #isTrustedProxyInterface
* isTrustedProxyInterface} with the interface and the specified trust
* verifier context returns <code>true</code>.
* </ul>
*
* <p>A subclass can override this method to augment the set of trusted
* proxy classes for Jini ERI proxies.
*
* @param proxy the dynamic proxy
* @param ctx the trust verifier context, to aid in verification of
* the specified proxy's class
* @return <code>true</code> if the specified dynamic proxy's class is
* trusted by this verifier; returns <code>false</code> otherwise
* @throws RemoteException if a communication-related exception occurs
* @throws SecurityException if a security exception occurs
* @throws NullPointerException if any argument is <code>null</code>
* @since 2.1
**/
protected boolean hasTrustedProxyClass(Object proxy,
TrustVerifier.Context ctx)
throws RemoteException
{
Class c = proxy.getClass();
if (ctx == null) {
throw new NullPointerException();
} else if (!(proxy instanceof Remote &&
hasTrustedClassLoader(proxy, ctx)))
{
return false;
}
Class[] interfaces = c.getInterfaces();
for (int i = interfaces.length; --i >= 0; ) {
if (!isTrustedProxyInterface(interfaces[i], ctx)) {
return false;
}
}
return true;
}
/**
* Returns <code>true</code> if the class loader of the specified dynamic
* proxy's class is trusted by this verifier; returns <code>false</code>
* otherwise. The default implementation of
* <code>hasTrustedProxyClass</code> calls this method to verify
* that the proxy's class loader is trusted.
*
* <p><code>BasicJeriTrustVerifier</code> implements this method to
* return <code>true</code> if the specified proxy is an instance of a
* dynamic proxy class and the class loader of the specified proxy's
* class is either equal to or an ancestor of the class loader returned
* by the trust verifier context's {@link
* net.jini.security.TrustVerifier.Context#getClassLoader
* getClassLoader} method (or the current context class loader, if that
* method returns <code>null</code>).
*
* <p>A subclass can override this method to augment the set of trusted
* loaders for Jini ERI proxies.
*
* @param proxy the dynamic proxy
* @param ctx the trust verifier context, to aid in verification of
* the class loader of the specified proxy's class
* @return <code>true</code> if the class loader of the specified dynamic
* proxy's class is trusted by this verifier; returns <code>false</code>
* otherwise
* @throws RemoteException if a communication-related exception occurs
* @throws SecurityException if a security exception occurs
* @throws IllegalArgumentException if the proxy is not an
* instance of a dynamic proxy class
* @throws NullPointerException if any argument is <code>null</code>
* @since 2.1
**/
protected boolean hasTrustedClassLoader(Object proxy,
final TrustVerifier.Context ctx)
throws RemoteException
{
final Class c = proxy.getClass();
if (!Proxy.isProxyClass(c)) {
throw new IllegalArgumentException(
"Proxy must be an instance of a dynamic proxy class");
}
Boolean b = (Boolean)
Security.doPrivileged(new PrivilegedAction() {
public Object run() {
ClassLoader cl = ctx.getClassLoader();
if (cl == null) {
cl = Thread.currentThread().getContextClassLoader();
}
return Boolean.valueOf(covers(cl, c.getClassLoader()));
}
});
return b.booleanValue();
}
/**
* Returns <code>true</code> if the specified dynamic proxy interface is
* trusted by this verifier; returns <code>false</code> otherwise. The
* default implementation of <code>hasTrustedProxyClass</code> calls
* this method to verify that the dynamic proxy's interfaces are trusted.
*
* <p><code>BasicJeriTrustVerifier</code> implements this method to
* return <code>true</code> if the specified class is
* {@link RemoteMethodControl}, {@link TrustEquivalence}, or a
* well-formed remote interface (one that extends {@link Remote} and for
* which all methods declare {@link RemoteException} or a superclass in
* their <code>throws</code> clause).
*
* <p>A subclass can override this method to augment the set of trusted
* proxy interfaces for Jini ERI proxies.
*
* @param intf the dynamic proxy interface
* @param ctx the trust verifier context, to aid in verification of
* the specified proxy interface
* @return <code>true</code> if the specified dynamic proxy interface is
* trusted by this verifier; returns <code>false</code> otherwise
* @throws RemoteException if a communication-related exception occurs
* @throws SecurityException if a security exception occurs
* @throws NullPointerException if any argument is <code>null</code>
* @since 2.1
**/
protected boolean isTrustedProxyInterface(Class intf,
TrustVerifier.Context ctx)
throws RemoteException
{
if (ctx == null) {
throw new NullPointerException();
} else if (intf == RemoteMethodControl.class ||
intf == TrustEquivalence.class)
{
return true;
} else if (!Remote.class.isAssignableFrom(intf) || !intf.isInterface())
{
return false;
}
Method[] methods = intf.getMethods();
methods:
for (int i = methods.length; --i >= 0; ) {
Class[] exceptions = methods[i].getExceptionTypes();
for (int j = exceptions.length; --j >= 0; ) {
if (exceptions[j].isAssignableFrom(RemoteException.class))
{
continue methods;
}
}
return false;
}
return true;
}
/**
* Returns <code>true</code> if the specified invocation
* handler is trusted by this trust verifier; returns
* <code>false</code> otherwise. The <code>isTrustedObject</code>
* method calls this method to verify trust in the invocation handler.
*
* <p><code>BasicJeriTrustVerifier</code> implements this method to
* return <code>true</code> if all of the following are true:
* <ul>
* <li>The specified invocation handler's class is equal to
* <code>BasicInvocationHandler</code>.
* <li>Invoking the {@link #checkInvocationHandlerContent
* checkInvocationHandlerContent} method with the specified handler
* and the specified context returns <code>true</code>.
* </ul>
*
* <p>A subclass can override this method to verify that the invocation
* handler's class is a trusted subclass of
* <code>BasicInvocationHandler</code> and to verify the contents of
* the invocation handler. A subclass implementation of this method
* can invoke the <code>checkInvocationHandlerContent</code> static
* utility method to verify that the invocation handler's constraints
* and object endpoint are trusted.
*
* @param handler the invocation handler
* @param ctx the trust verifier context, to aid in verification of
* the specified handler
* @return <code>true</code> if the specified invocation
* handler is trusted by this trust verifier; returns
* <code>false</code> otherwise
* @throws RemoteException if a communication-related exception occurs
* @throws SecurityException if a security exception occurs
* @throws NullPointerException if any argument is <code>null</code>
* @since 2.1
**/
protected boolean isTrustedInvocationHandler(
InvocationHandler handler,
TrustVerifier.Context ctx)
throws RemoteException
{
if (handler.getClass() != BasicInvocationHandler.class) {
return false;
}
return checkInvocationHandlerContent(
(BasicInvocationHandler) handler, ctx);
}
/**
* Returns <code>true</code> if the specified invocation handler's
* constraints and object endpoint are trusted; returns
* <code>false</code> otherwise. The
* <code>isTrustedInvocationHandler</code> method calls this utility
* method to check that the invocation handler's constraints and object
* endpoint are trusted.
*
* <p>The specified invocation handler's constraints and object
* endpoint are trusted if all of the following are true:
* <ul>
* <li>The object endpoint in the specified invocation handler is
* trusted; that is, calling the specified context's
* {@link net.jini.security.TrustVerifier.Context#isTrustedObject
* isTrustedObject} method with the object endpoint returns
* <code>true</code>.
* <li>The server constraints in the specified invocation handler are
* trusted; that is, calling the specified context's
* <code>isTrustedObject</code> method with the server constraints
* returns <code>true</code>.
* </ul>
*
* @param handler the invocation handler
* @param ctx the trust verifier context, to aid in verification of
* the specified handler
* @return <code>true</code> if the specified invocation
* handler's constraints and object endpoint are trusted by this method;
* returns <code>false</code> otherwise
* @throws RemoteException if a communication-related exception occurs
* @throws SecurityException if a security exception occurs
* @throws NullPointerException if any argument is <code>null</code>
* @since 2.1
**/
protected static boolean checkInvocationHandlerContent(
BasicInvocationHandler handler,
TrustVerifier.Context ctx)
throws RemoteException
{
if (handler == null) {
throw new NullPointerException("handler is null");
}
MethodConstraints serverConstraints = handler.getServerConstraints();
return (ctx.isTrustedObject(handler.getObjectEndpoint()) &&
ctx.isTrustedObject(serverConstraints));
}
/**
* Returns true if the first argument is either equal to, or is a
* descendant of, the second argument. Null is treated as the root of
* the tree.
*/
private static boolean covers(ClassLoader sub, ClassLoader sup) {
if (sup == null) {
return true;
} else if (sub == null) {
return false;
}
do {
if (sub == sup) {
return true;
}
sub = sub.getParent();
} while (sub != null);
return false;
}
}