/* * 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.security.proxytrust; import com.sun.jini.jeri.internal.runtime.Util; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; import java.rmi.Remote; import net.jini.core.constraint.RemoteMethodControl; /** * Invocation handler for remote objects, supporting proxy trust verification * by clients using {@link ProxyTrustVerifier}. This invocation handler * contains both an underlying main proxy and a bootstrap proxy; the main * proxy is not expected to be considered trusted directly by clients, but * the bootstrap proxy is. The main proxy must be an instance of both * {@link RemoteMethodControl} and {@link TrustEquivalence}, and the bootstrap * proxy must be an instance of {@link ProxyTrust}, * <code>RemoteMethodControl</code>, and <code>TrustEquivalence</code>. This * invocation handler handles most method invocations by delegating to the * main proxy. The bootstrap proxy is produced by the iterator returned by * the {@link #getProxyTrustIterator getProxyTrustIterator} method, as * required by <code>ProxyTrustVerifier</code>. * * @author Sun Microsystems, Inc. * @since 2.0 */ public final class ProxyTrustInvocationHandler implements InvocationHandler, TrustEquivalence, Serializable { private static final long serialVersionUID = -3270029468290295063L; private static final Class[] consArgs = new Class[]{InvocationHandler.class}; /** * The main proxy. * * @serial */ private final RemoteMethodControl main; /** * The bootstrap proxy. * * @serial */ private final ProxyTrust boot; /** * Creates an instance with the specified main proxy and bootstrap proxy. * * @param main the main proxy * @param boot the bootstrap proxy * @throws NullPointerException if any argument is <code>null</code> * @throws IllegalArgumentException if the main proxy is not an instance * of {@link TrustEquivalence}, or the bootstrap proxy is not an instance * of <code>RemoteMethodControl</code> or <code>TrustEquivalence</code> */ public ProxyTrustInvocationHandler(RemoteMethodControl main, ProxyTrust boot) { if (main == null || boot == null) { throw new NullPointerException("arguments cannot be null"); } else if (!(main instanceof TrustEquivalence)) { throw new IllegalArgumentException( "main proxy must implement TrustEquivalence"); } else if (!(boot instanceof RemoteMethodControl)) { throw new IllegalArgumentException( "bootstrap proxy must implement RemoteMethodControl"); } else if (!(boot instanceof TrustEquivalence)) { throw new IllegalArgumentException( "bootstrap proxy must implement TrustEquivalence"); } this.main = main; this.boot = boot; } /** * Executes the specified method with the specified arguments on the * specified proxy, and returns the return value, if any. * <p> * If the specified method is <code>Object.equals</code>, returns * <code>true</code> if the argument (<code>args[0]</code>) is an * instance of a dynamic proxy class (that is, a class generated by * {@link Proxy}) that implements the same interfaces as the specified * proxy and this invocation handler is equal to the invocation handler * of that argument, and returns <code>false</code> otherwise. * <p> * If the specified method is <code>Object.toString</code>, returns * a string representation of the specified proxy object. * <p> * If the specified method is <code>Object.hashCode</code>, returns * a hash code for the specified proxy object. * <p> * If the specified method is {@link RemoteMethodControl#setConstraints * RemoteMethodControl.setConstraints}, returns a new proxy (an instance * of the same class as the specified proxy) containing an instance of * this class with a new main proxy and the same bootstrap proxy from * this handler. The new main proxy is obtained by delegating to the * existing main proxy of this handler (as described below). An exception * is thrown if the specified proxy is not an instance of a dynamic * proxy class containing this invocation handler. * <p> * If the specified method is * {@link TrustEquivalence#checkTrustEquivalence * TrustEquivalence.checkTrustEquivalence}, returns <code>true</code> if * the argument (<code>args[0]</code>) is an instance of a dynamic proxy * class that implements the same interfaces as the specified proxy and * calling the {@link #checkTrustEquivalence checkTrustEquivalence} method * of this invocation handler with the invocation handler of that argument * returns <code>true</code>, and returns <code>false</code> otherwise. * <p> * For all other methods, returns the object obtained by delegating to * the main proxy: the specified method is reflectively invoked on the * main proxy with the specified arguments, unless the method's declaring * class is not public but the main proxy is an instance of that * declaring class and the main proxy's class is public, in which case * the corresponding method of the main proxy's class is reflectively * invoked instead. * <p> * The semantics of this method are unspecified if the arguments could * not have been produced by an instance of some valid dynamic proxy * class containing this invocation handler. * * @param proxy the proxy object * @param method the method being invoked * @param args the arguments to the specified method * @return the value returned by executing the specified method on * the specified proxy with the specified arguments, or <code>null</code> * if the method has <code>void</code> return type * @throws Throwable the exception thrown by executing the specified * method * @throws IllegalArgumentException if the declaring class of the * specified method is not public and either the main proxy is not an * instance of that declaring class or the main proxy's class is not * public */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class decl = method.getDeclaringClass(); if (decl == Object.class) { String name = method.getName(); if (name.equals("equals")) { Object obj = args[0]; if (proxy == obj || (obj != null && Util.sameProxyClass(proxy, obj) && equals(Proxy.getInvocationHandler(obj)))) { return Boolean.TRUE; } return Boolean.FALSE; } else if (name.equals("toString")) { return proxyToString(proxy); } else if (name.equals("hashCode")) { return new Integer(hashCode()); } throw new IllegalArgumentException("unexpected Object method"); } else if (decl == RemoteMethodControl.class && method.getName().equals("setConstraints")) { if (Proxy.getInvocationHandler(proxy) != this) { throw new IllegalArgumentException("wrong invocation handler"); } InvocationHandler handler = new ProxyTrustInvocationHandler( (RemoteMethodControl) invoke0(method, args), boot); return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), proxy.getClass().getInterfaces(), handler); } else if (decl == TrustEquivalence.class) { if (!method.getName().equals("checkTrustEquivalence")) { throw new AssertionError("unknown TrustEquivalence method"); } Object obj = args[0]; if (proxy == obj || (obj != null && Util.sameProxyClass(proxy, obj) && checkTrustEquivalence(Proxy.getInvocationHandler(obj)))) { return Boolean.TRUE; } return Boolean.FALSE; } return invoke0(method, args); } /** * Reflectively invoke the method on the main proxy, unless the method's * declaring class is not public but the main proxy's class is public, in * which case invoke the corresponding method from the main proxy's class * instead. */ private Object invoke0(Method m, Object[] args) throws Throwable { Class iface = m.getDeclaringClass(); if (!Modifier.isPublic(iface.getModifiers())) { Class impl = main.getClass(); if (Modifier.isPublic(impl.getModifiers()) && iface.isInstance(main)) { try { m = impl.getMethod(m.getName(), m.getParameterTypes()); } catch (Exception e) { } } } try { return m.invoke(main, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (IllegalAccessException e) { throw new IllegalArgumentException().initCause(e); } } /** * Returns <code>true</code> if the argument is an instance of this * class, and calling the <code>checkTrustEquivalence</code> method on * the main proxy of this invocation handler, passing the main proxy of * the argument, returns <code>true</code>, and calling the * <code>checkTrustEquivalence</code> method on the bootstrap proxy of * this invocation handler, passing the bootstrap proxy of the argument, * returns <code>true</code>, and returns <code>false</code> otherwise. */ public boolean checkTrustEquivalence(Object obj) { if (this == obj) { return true; } else if (!(obj instanceof ProxyTrustInvocationHandler)) { return false; } ProxyTrustInvocationHandler oh = (ProxyTrustInvocationHandler) obj; return (((TrustEquivalence) main).checkTrustEquivalence(oh.main) && ((TrustEquivalence) boot).checkTrustEquivalence(oh.boot)); } /** * Returns <code>true</code> if the argument is an instance of this * class with the same main proxy and the same bootstrap proxy, and * <code>false</code> otherwise. */ public boolean equals(Object obj) { if (this == obj) { return true; } else if (!(obj instanceof ProxyTrustInvocationHandler)) { return false; } ProxyTrustInvocationHandler oh = (ProxyTrustInvocationHandler) obj; return main.equals(oh.main) && boot.equals(oh.boot); } /** * Returns a hash code value for this object. */ public int hashCode() { return main.hashCode() + boot.hashCode(); } /** * Returns a string representation of this object. */ public String toString() { return ("ProxyTrustInvocationHandler[main: " + main + ", boot: " + boot + "]"); } /** * Returns a string representation for a proxy that uses this invocation * handler. */ private String proxyToString(Object proxy) { Class[] interfaces = proxy.getClass().getInterfaces(); Class iface = null; for (int i = interfaces.length; --i >= 0; iface = interfaces[i]) { if (interfaces[i] == RemoteMethodControl.class) { break; } } if (iface == null) { return "Proxy[" + this + "]"; } String n = iface.getName(); int dot = n.lastIndexOf('.'); if (dot >= 0) { n = n.substring(dot + 1); } return "Proxy[" + n + "," + this + "]"; } /** * Returns an iterator that produces the bootstrap proxy as the only * element of the iteration. * * @return an iterator that produces the bootstrap proxy as the only * element of the iteration */ protected ProxyTrustIterator getProxyTrustIterator() { return new SingletonProxyTrustIterator(boot); } /** * Verifies that the main proxy is an instance of * {@link TrustEquivalence}, and that the bootstrap proxy is an instance * of both {@link RemoteMethodControl} and <code>TrustEquivalence</code>. * * @throws InvalidObjectException if the main proxy is not an instance of * <code>TrustEquivalence</code>, or the bootstrap proxy is not an * instance of both <code>RemoteMethodControl</code> and * <code>TrustEquivalence</code> */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); if (!(main instanceof TrustEquivalence)) { throw new InvalidObjectException( "main proxy must implement TrustEquivalence"); } else if (!(boot instanceof RemoteMethodControl)) { throw new InvalidObjectException( "bootstrap proxy must implement RemoteMethodControl"); } else if (!(boot instanceof TrustEquivalence)) { throw new InvalidObjectException( "bootstrap proxy must implement TrustEquivalence"); } } }