/* * 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; } }