This is a draft for MarshalledInstance, to prevent codebase download DOS, it includes a reflective proxy verifier with MarshalledInstance to verify the codebase annotation.

Please suggest improvements.


/*
* 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.io;

import com.sun.jini.proxy.BasicProxyTrustVerifier;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamException;
import java.io.OutputStream;
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.ExportException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.jini.constraint.BasicMethodConstraints;
import net.jini.core.constraint.Confidentiality;
import net.jini.core.constraint.InvocationConstraint;
import net.jini.core.constraint.InvocationConstraints;
import net.jini.export.Exporter;
import net.jini.io.context.IntegrityEnforcement;
import net.jini.jeri.BasicJeriExporter;
import net.jini.jeri.ProxyTrustILFactory;
import net.jini.jeri.ssl.ConfidentialityStrength;
import net.jini.jeri.ssl.SslServerEndpoint;
import net.jini.security.BasicProxyPreparer;
import net.jini.security.ProxyPreparer;
import net.jini.security.ProxyPreparer;
import net.jini.security.TrustVerifier;
import net.jini.security.proxytrust.ServerProxyTrust;
import org.apache.river.impl.util.ConcurrentWeakMap;

/*
* Implementation note: This class uses the helper class
* MarshalledObject that is in this package. To avoid confusion
* with java.rmi.MarshalledObject the fully qualified class names
* are used for both classes.
*/
/**
* A <code>MarshalledInstance</code> contains an object in serialized
* form. The contained object can be deserialized on demand when
* explicitly requested. This allows an object to be sent from one VM
* to another in a way that allows the receiver to control when and if
* the object is deserialized.
* <p>
* The contained object is specified at construction time and can
* either be provided in unserialized or serialized form. If provided
* in unserialized form it will be serialized during construction
* with the serialization semantics defined by
* <code>MarshalOutputStream</code>. In particular, classes are annotated
* with a codebase URL from which the class can be loaded (if available).
* <p>
* If the <code>MarshalledInstance</code> needs to deserialize the
* contained object then the contained object will be deserialized with the
* deserialization semantics defined by <code>MarshalInputStream</code>.
* In particular, the codebase annotations associated with the contained
* object may be used to load classes referenced by the contained object.
* <p>
* <code>MarshalledInstance</code> provides functionality similar to
* <code>java.rmi.MarshalledObject</code>, but additionally provides
* for the verification of codebase integrity. Unlike
* <code>java.rmi.MarshalledObject</code>, it does not perform remote
* object-to-stub replacement.
*
* @author Sun Microsystems, Inc.
* @since 2.0
*/
public class MarshalledInstance implements Serializable {

   /* Change this later to be read from a configuration.
    */
   private static volatile boolean verifyState = true;
   /* Hold references to a constructed MarshalledInstance until the object
    * key is garbage collected.  It may need to hold duplicates if
* more than one marshalled instance is created with different annotations.
    * MarshalledInstance references are typically thrown away soon after
    * serialization, so this pool is here to retain a reference to
    * the deserialization verifier, while the object it holds hasn't been
    * garbage collected.
    * DGC cannot be used since it would pose a Denial Of Service Risk.
    */
private static final ConcurrentMap<Object, Cup> pool = new ConcurrentWeakMap<Object, Cup>();

   /* Add an Object key, MarshalledInstance value to the pool if it
    * doesn't already exist, if it does, return the value in the pool
    * this will be used to avoid duplicating the exported MD.
    * Duplicates may exist where the MarshalledInstance is equal but not
    * fullyEqual.  This prevents an explosion of MarshalledInstance
    * objects when they are created often.
    */
private static MarshalledInstance addToPool(Object o, MarshalledInstance i) {
       Cup c = pool.get(o);
       if (c == null) {
           c = new Cup();
           Cup existed = pool.putIfAbsent(o, c);
           if (existed != null) {
               c = existed;
           }
       }
       MarshalledInstance existed = c.putIfAbsent(i);
       return existed;
   }
   /**
* @serial Bytes of serialized representation. If <code>objBytes</code> is
    * <code>null</code> then the object marshalled was a <code>null</code>
    * reference.
    */
   private byte[] objBytes = null;
   /**
    * @serial Bytes of location annotations, which are ignored by
    * <code>equals</code>.  If <code>locBytes</code> is null, there were no
    * non-<code>null</code> annotations during marshalling.
    */
   private byte[] locBytes = null;
   /**
    * @serial Stored hash code of contained object.
* * @see #hashCode
    */
   private int hash;
   /**
    * @serial  MarshalledInstance deserialization authentication and
    * MessageDigest based verifier, may be null.  The serialized form
    * is a remote proxy.  The creating JVM has the original remote
    * object used for authentication and verification.
    */
   /* I think the implementation needs a static weak map referencing the
    * original object, that holds a reference to a MarshalledInstance to
    * prevent unwanted garbage collection.  When the original object goes
    * away, so does the MarshalledInstance.
    */
   private AuthCheckMDIntegrity authDeserializationMDIntegrityCheck = null;
   static final long serialVersionUID = -5187033771082433496L;

   /**
    * Creates a new <code>MarshalledInstance</code> that contains the
    * marshalled representation of the current state of the supplied
    * object. The object is serialized with the semantics defined by
* <code>MarshalOutputStream</code>. The output stream used to marshal the
    * object implements {@link ObjectStreamContext} and returns an empty
    * collection from its {@link ObjectStreamContext#getObjectStreamContext
    * getObjectStreamContext} method.
    *
    * @param obj The Object to be contained in the new
    *          <code>MarshalledInstance</code>
    * @throws IOException if the object cannot be serialized
    */
   public MarshalledInstance(Object obj) throws IOException {
       this(obj, Collections.EMPTY_SET);
   }

   /**
    * Creates a new <code>MarshalledInstance</code> that contains the
    * marshalled representation of the current state of the supplied
    * object. The object is serialized with the semantics defined by
* <code>MarshalOutputStream</code>. The output stream used to marshal the
    * object implements {@link ObjectStreamContext} and returns the given
    * collection from its {@link ObjectStreamContext#getObjectStreamContext
    * getObjectStreamContext} method.
    *
    * @param obj The Object to be contained in the new
    *          <code>MarshalledInstance</code>
    * @param context the collection of context information objects
    * @throws IOException if the object cannot be serialized
* @throws NullPointerException if <code>context</code> is <code>null</code>
    */
   public MarshalledInstance(Object obj, Collection context)
           throws IOException {
       if (context == null) {
           throw new NullPointerException();
       }

       if (obj == null) {
           hash = 13;        // null hash for java.rmi.MarshalledObject
           return;
       }
       ByteArrayOutputStream bout = new ByteArrayOutputStream();
       ByteArrayOutputStream lout = new ByteArrayOutputStream();
       MarshalledInstanceOutputStream out =
               new MarshalledInstanceOutputStream(bout, lout, context);
       out.writeObject(obj);
       out.flush();
       objBytes = bout.toByteArray();
       // locBytes is null if no annotations
       locBytes = (out.hadAnnotations() ? lout.toByteArray() : null);

       // Calculate hash from the marshalled representation of object
       // so the hashcode will be comparable when sent between VMs.
       //
       // Note: This calculation must match the calculation in
       //     java.rmi.MarshalledObject since we use this hash
       //     in the converted MarshalledObject. The reverse is
       //     also true in that we use the MarshalledObject's
       //     hash for our hash. (see the MarshalledInstance(
       //     MarshalledObject) constructor)
       //
       int h = 0;
       for (int i = 0; i < objBytes.length; i++) {
           h = 31 * h + objBytes[i];
       }
       hash = h;
       createDeserializationIntegrityCheck();
       MarshalledInstance exists = addToPool(obj, this);
       if (exists != null) {
           // we just created a duplicate marshalled instance best we share
           // the deserialization check so we don't have to retain too many
           // unnecessary strong references.
authDeserializationMDIntegrityCheck = exists.authDeserializationMDIntegrityCheck;
       }
   }

   private void createDeserializationIntegrityCheck() {
       // Should we bomb out, or just leave it without a verifier?
       try {
authDeserializationMDIntegrityCheck = new IntegrityCheck(objBytes, locBytes, hash, "SHA");
       } catch (NoSuchAlgorithmException ex) {
Logger.getLogger(MarshalledInstance.class.getName()).log(Level.SEVERE, null, ex);
       } catch (ExportException ex) {
Logger.getLogger(MarshalledInstance.class.getName()).log(Level.SEVERE, null, ex);
       }
   }

   /**
    * Creates a new <code>MarshalledInstance</code> from an
    * existing <code>MarshalledObject</code>. An object equivalent
    * to the object contained in the passed <code>MarshalledObject</code>
    * will be contained in the new <code>MarshalledInstance</code>.
    * <p>
    * The object contained in the passed <code>MarshalledObject</code>
    * will not be unmarshalled as part of this call.
    *
    * @param mo The <code>MarshalledObject</code> that contains
    *        the object the new <code>MarshalledInstance</code> should
    *        contain
    * @throws NullPointerException if <code>mo</code> is <code>null</code>
    */
   public MarshalledInstance(java.rmi.MarshalledObject mo) {

       if (mo == null) {
           throw new NullPointerException();
       }

       // To extract the java.rmi.MarshalledObject's fields we
       // convert the mo into a net.jini.io.MarshalledObject.
       // (See resolveClass() in FromMOInputStream) The private
       // version of MarshalledObject allows access to the needed
       // fields.
       //
       net.jini.io.MarshalledObject privateMO = null;
       try {
           ByteArrayOutputStream baos = new ByteArrayOutputStream();
           ObjectOutputStream oos = new ObjectOutputStream(baos);
           oos.writeObject(mo);
           oos.flush();
           byte[] bytes = baos.toByteArray();
           ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
           ObjectInputStream ois = new FromMOInputStream(bais);
           privateMO =
                   (net.jini.io.MarshalledObject) ois.readObject();
       } catch (IOException ioe) {
           throw new AssertionError(ioe);
       } catch (ClassNotFoundException cnfe) {
           throw new AssertionError(cnfe);
       }
       objBytes = privateMO.objBytes;
       locBytes = privateMO.locBytes;
       hash = privateMO.hash;
   // We shouldn't create a verifier proxy for MarshalledInstance
   // created from a MarshalledObject, since the actual object may
   // not be local, how can we vouch for it if we don't have the
   // original object?
//    createDeserializationIntegrityCheck();
   }

   /**
    * Creates a new <code>MarshalledObject</code> that will
    * contain an object equivalent to the object contained
    * in this <code>MarshalledInstance</code> object.
    * <p>
    * The object contained in this <code>MarshalledInstance</code>
    * object will not be unmarshalled as part of this call.
    * @return A new <code>MarshalledObject</code> which
    *        contains an object equivalent to the object
    *        contained in this <code>MarshalledInstance</code>
    */
   public java.rmi.MarshalledObject convertToMarshalledObject() {

       // To create a java.rmi.MarshalledObject with previously
       // serialized data we first create a private
       // net.jini.io.MarshalledObject with the
       // data and then convert it to the final object by changing
       // the class during readObject(). (See resolveClass() in
       // ToMOInputStream)
       //
       net.jini.io.MarshalledObject privateMO =
               new net.jini.io.MarshalledObject();

       privateMO.objBytes = objBytes;
       privateMO.locBytes = locBytes;
       privateMO.hash = hash;

       java.rmi.MarshalledObject mo = null;
       try {
           ByteArrayOutputStream baos = new ByteArrayOutputStream();
           ObjectOutputStream oos = new ObjectOutputStream(baos);
           oos.writeObject(privateMO);
           oos.flush();
           byte[] bytes = baos.toByteArray();
           ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
           ObjectInputStream ois = new ToMOInputStream(bais);
           mo = (java.rmi.MarshalledObject) ois.readObject();
       } catch (IOException ioe) {
           throw new AssertionError(ioe);
       } catch (ClassNotFoundException cnfe) {
           throw new AssertionError(cnfe);
       }
       return mo;
   }

   /**
    * Returns a new copy of the contained object. Deserialization is
* performed with the semantics defined by <code>MarshalInputStream</code>.
    * The input stream used to unmarshal the object implements {@link
    * ObjectStreamContext} and returns a collection from its {@link
    * ObjectStreamContext#getObjectStreamContext getObjectStreamContext}
    * method which contains a single element of type {@link
* IntegrityEnforcement}; the {@link IntegrityEnforcement#integrityEnforced
    * integrityEnforced} method of this element returns the specified
    * <code>verifyCodebaseIntegrity</code> value.
    * <p>MarshalledInstance</code> implements this method by calling
    * <code>{@link #get(ClassLoader, boolean, ClassLoader, Collection)
    * get}(null, verifyCodebaseIntegrity, null, null)</code>.
    *
    * @param verifyCodebaseIntegrity if <code>true</code> then
    *        codebase integrity is verified, otherwise code base
    *        integrity is not verified
    * @return a new copy of the contained object
    * @throws IOException if an
    *         <code>IOException</code> occurs while deserializing the
    *         object from its internal representation
    * @throws ClassNotFoundException if any classes necessary
    *         for reconstructing the contained object can not
    *         be found or if <code>verifyCodebaseIntegrity</code>
    *         is <code>true</code> and the integrity of the
    *         contained object's codebase cannot be confirmed
    */
   public Object get(final boolean verifyCodebaseIntegrity)
           throws IOException, ClassNotFoundException {
       return get(null, verifyCodebaseIntegrity, null, null);
   }

   /**
    * Returns a new copy of the contained object. Deserialization is
* performed with the semantics defined by <code>MarshalInputStream</code>.
    * If <code>context</code> is not <code>null</code>
    * the input stream used to unmarshal the object implements {@link
    * ObjectStreamContext} and returns the given collection from its {@link
    * ObjectStreamContext#getObjectStreamContext getObjectStreamContext}
    * method.
    * <p>If <code>context</code> is <code>null</code>
    * the input stream used to unmarshal the object implements {@link
    * ObjectStreamContext} and returns a collection from its {@link
    * ObjectStreamContext#getObjectStreamContext getObjectStreamContext}
    * method which contains a single element of type {@link
* IntegrityEnforcement}; the {@link IntegrityEnforcement#integrityEnforced
    * integrityEnforced} method of this element returns the specified
    * <code>verifyCodebaseIntegrity</code> value.
    *
    * @param defaultLoader the class loader value (possibly
    *          <code>null</code>) to pass as the <code>defaultLoader</code>
    *        argument to <code>RMIClassLoader</code> methods
    * @param verifyCodebaseIntegrity if <code>true</code> then
    *        codebase integrity is verified, otherwise code base
    *        integrity is not verified
    * @param verifierLoader the class loader value (possibly
    *        <code>null</code>) to pass to {@link
    *        net.jini.security.Security#verifyCodebaseIntegrity
    *        Security.verifyCodebaseIntegrity}, if
    *        <code>verifyCodebaseIntegrity</code> is <code>true</code>
    * @param context the collection of context information objects or
    *        <code>null</code>
    * @return a new copy of the contained object
    * @throws IOException if an
    *         <code>IOException</code> occurs while deserializing the
    *         object from its internal representation
    * @throws ClassNotFoundException if any classes necessary
    *         for reconstructing the contained object can not
    *         be found or if <code>verifyCodebaseIntegrity</code>
    *         is <code>true</code> and the integrity of the
    *         contained object's codebase cannot be confirmed
    */
   public Object get(ClassLoader defaultLoader,
           final boolean verifyCodebaseIntegrity,
           ClassLoader verifierLoader,
           Collection context)
           throws IOException, ClassNotFoundException {
       if (objBytes == null) // must have been a null object
       {
           return null;
       }

       if (context == null) {
           context = Collections.singleton(
                   new IntegrityEnforcement() {

                       public boolean integrityEnforced() {
                           return verifyCodebaseIntegrity;
                       }
                   });
       }
       ByteArrayInputStream bin = new ByteArrayInputStream(objBytes);
       // locBytes is null if no annotations
       ByteArrayInputStream lin =
(locBytes == null ? null : new ByteArrayInputStream(locBytes));
       MarshalledInstanceInputStream in =
               new MarshalledInstanceInputStream(bin, lin,
               defaultLoader,
               verifyCodebaseIntegrity,
               verifierLoader,
               context);
       in.useCodebaseAnnotations();
       Object obj = in.readObject();
       in.close();
       return obj;
   }

   /**
    * Compares this <code>MarshalledInstance</code> to another
* object. Returns true if and only if the argument refers to an instance
    * of <code>MarshalledInstance</code> that contains exactly the same
    * serialized form for its contained object as this object does and
    * has the same class codebase annotations.
    *
    * @param obj the object to compare with this
    *            <code>MarshalledInstance</code>
    * @return <code>true</code> if the argument contains an object
    *         with an equivalent serialized form and codebase;
    *           otherwise returns <code>false</code>
    */
   public boolean fullyEquals(Object obj) {
       if (equals(obj)) {
           MarshalledInstance other = (MarshalledInstance) obj;
           return Arrays.equals(locBytes, other.locBytes);
       }
       return false;
   }

   /**
    * Compares this <code>MarshalledInstance</code> to another
* object. Returns true if and only if the argument refers to an instance
    * of <code>MarshalledInstance</code> that contains exactly the same
    * serialized form for its contained object as this object does. The
    * comparison ignores any class codebase annotations, so that
    * two objects can be equivalent if they have the same serialized
    * representation, except for the codebase of each class in the
    * serialized representation.
    * @param obj the object to compare with this
    *            <code>MarshalledInstance</code>
    * @return <code>true</code> if the argument contains an object
    *         with an equivalent serialized form; otherwise returns
    *         <code>false</code>
    */
   public boolean equals(Object obj) {
       if (obj == this) {
           return true;
       }

       if (obj instanceof MarshalledInstance) {
           MarshalledInstance other = (MarshalledInstance) obj;
           if (hash != other.hash) {
               return false;
           }
           return Arrays.equals(objBytes, other.objBytes);
       }
       return false;
   }

   /**
    * Returns the hash code for this <code>MarshalledInstance</code>.
    * The hash code is calculated only from the serialized form
    * of the contained object.
    * @return The hash code for this object
    */
   public int hashCode() {
       return hash;
   }

   /**
    * Verify the case of null contained object.
    */
   private void readObject(ObjectInputStream in)
           throws IOException, ClassNotFoundException {
       // This allows backward compatible evolution of serialized form.
       in.defaultReadObject();

       // If contained object is null, then hash and locBytes must be
       // proper
       //
       if ((objBytes == null) && ((hash != 13) || (locBytes != null))) {
           throw new InvalidObjectException("Bad hash or annotation");
       }
       // Defensive copy mutable arrays.
       objBytes = objBytes.clone();
       locBytes = locBytes.clone();
       // During deserialization we verify the MarshalledInstance using
       // the proxy, for now until we get some proper tests up and
       // running, I'm not going to require authentication.
       // Later this will require authentication and a server
       // minimum principal.
if (authDeserializationMDIntegrityCheck == null && verifyState == true) { throw new IOException("MarshalledInstance deserialization aborted" +
                   "unable to verify state, uable to " +
                   "authenticate or prevent codebase download denial of " +
"service, missing deserialization integrity check proxy");
       }
       if (authDeserializationMDIntegrityCheck != null) {
           ProxyPreparer preparer = new BasicProxyPreparer();
// Don't overwrite proxy reference we might want to serialize it again. AuthCheckMDIntegrity proxy = (AuthCheckMDIntegrity) preparer.prepareProxy(authDeserializationMDIntegrityCheck);
           // Verify state has been transferred successfully.
           String algorithm = proxy.getAlgorithm();
           MessageDigest md = null;
           try {
               md = MessageDigest.getInstance(algorithm);
           } catch (NoSuchAlgorithmException ex) {
               if (verifyState == true) {
                   throw new IOException("Unable to verify state", ex);
               } else {
Logger.getLogger(MarshalledInstance.class.getName()).log(
                           Level.SEVERE, null, ex);
               }
           }
           if (md != null) {
               md.update(objBytes);
               byte[] objMD = md.digest();
               md.reset();
               md.update(locBytes);
               byte[] locMD = md.digest();
if (Arrays.equals(proxy.getLocMD(), locMD) && Arrays.equals(proxy.getObjMD(), objMD) && hash == proxy.getHashCode()) { // It would now be safe, if we have authenticated the proxy
                   // with the minimum server principal and confirmed the
// Codebase annotation is correct, to grant DownloadPermission. // WE COULD ASSUME THAT DownloadPermission MUST BE GRANTED
                   // IF DESERIALIZATION HAS BEEN SUCCESSFUL.
// Granting DownloadPermission prevents a trusted service
                   // from returning another proxy that we don't trust, and
                   // having it's codebase downloaded automatically.
                   return;
               } else {
throw new IOException("MarshalledInstance serial form is corrupt");
               }
           }
       }
   }

   private void writeObject(ObjectOutputStream out) throws IOException {
       out.defaultWriteObject();
   }

   /**
    * Protect against missing superclass.
    */
   private void readObjectNoData() throws ObjectStreamException {
       throw new InvalidObjectException("Bad class hierarchy");
   }

   /**
    * This class is used to marshal objects for
    * <code>MarshalledInstance</code>.  It places the location annotations
    * to one side so that two <code>MarshalledInstance</code>s can be
    * compared for equality if they differ only in location
    * annotations.  Objects written using this stream should be read back
    * from a <code>MarshalledInstanceInputStream</code>.
* * @see MarshalledInstanceInputStream
    */
   private static class MarshalledInstanceOutputStream
           extends MarshalOutputStream {

       /** The stream on which location objects are written. */
       private ObjectOutputStream locOut;
       /** <code>true</code> if non-<code>null</code> annotations are
        *  written.
        */
       private boolean hadAnnotations;

       /**
        * Creates a new <code>MarshalledObjectOutputStream</code> whose
* non-location bytes will be written to <code>objOut</code> and whose
        * location annotations (if any) will be written to
        * <code>locOut</code>.
        */
       public MarshalledInstanceOutputStream(OutputStream objOut,
               OutputStream locOut,
               Collection context)
               throws IOException {
           super(objOut, context);
           this.locOut = new ObjectOutputStream(locOut);
           hadAnnotations = false;
       }

       /**
        * Returns <code>true</code> if any non-<code>null</code> location
        * annotations have been written to this stream.
        */
       public boolean hadAnnotations() {
           return hadAnnotations;
       }

       /**
        * Overrides <code>MarshalOutputStream.writeAnnotation</code>
        * implementation to write annotations to the location stream.
        */
       protected void writeAnnotation(String loc) throws IOException {
           hadAnnotations |= (loc != null);
           locOut.writeObject(loc);
       }

       public void flush() throws IOException {
           super.flush();
           locOut.flush();
       }
   }

   /**
    * The counterpart to <code>MarshalledInstanceOutputStream</code>.
* * @see MarshalledInstanceOutputStream
    */
   private static class MarshalledInstanceInputStream
           extends MarshalInputStream {

       /**
        * The stream from which annotations will be read.  If this is
        * <code>null</code>, then all annotations were <code>null</code>.
        */
       private ObjectInputStream locIn;

       /**
        * Creates a new <code>MarshalledObjectInputStream</code> that
        * reads its objects from <code>objIn</code> and annotations
        * from <code>locIn</code>.  If <code>locIn</code> is
        * <code>null</code>, then all annotations will be
        * <code>null</code>.
        */
       MarshalledInstanceInputStream(InputStream objIn,
               InputStream locIn,
               ClassLoader defaultLoader,
               boolean verifyCodebaseIntegrity,
               ClassLoader verifierLoader,
               Collection context)
               throws IOException {
           super(objIn,
                   defaultLoader,
                   verifyCodebaseIntegrity,
                   verifierLoader,
                   context);
this.locIn = (locIn == null ? null : new ObjectInputStream(locIn));
       }

       /**
        * Overrides <code>MarshalInputStream.readAnnotation</code> to
* return locations from the stream we were given, or <code>null</code>
        * if we were given a <code>null</code> location stream.
        */
       protected String readAnnotation()
               throws IOException, ClassNotFoundException {
           return (locIn == null ? null : (String) locIn.readObject());
       }
   }

   /**
    * Input stream to convert <code>java.rmi.MarshalledObject</code>
    * into <code>net.jini.io.MarshalledObject</code>.
    */
   private static class FromMOInputStream extends ObjectInputStream {

       public FromMOInputStream(InputStream in) throws IOException {
           super(in);
       }

       /**
        * Overrides <code>ObjectInputStream.resolveClass</code> to change
        * an occurence of class <code>java.rmi.MarshalledObject</code> to
        * class <code>net.jini.io.MarshalledObject</code>.
        */
       protected Class resolveClass(ObjectStreamClass desc)
               throws IOException, ClassNotFoundException {
           if (desc.getName().equals("java.rmi.MarshalledObject")) {
               return net.jini.io.MarshalledObject.class;
           }
           return super.resolveClass(desc);
       }
   }

   /**
    * Input stream to convert
    * <code>net.jini.io.MarshalledObject</code> into
    * <code>java.rmi.MarshalledObject</code>.
    */
   private static class ToMOInputStream extends ObjectInputStream {

       public ToMOInputStream(InputStream in) throws IOException {
           super(in);
       }

       /**
        * Overrides <code>ObjectInputStream.resolveClass</code>
        * to change an occurence of class
        * <code>net.jini.io.MarshalledObject</code>
        * to class <code>java.rmi.MarshalledObject</code>.
        */
       protected Class resolveClass(ObjectStreamClass desc)
               throws IOException, ClassNotFoundException {
           if (desc.getName().equals("net.jini.io.MarshalledObject")) {
               return java.rmi.MarshalledObject.class;
           }
           return super.resolveClass(desc);
       }
   }

   interface AuthCheckMDIntegrity {

       byte[] getLocMD() throws IOException;

       byte[] getObjMD() throws IOException;

       String getAlgorithm() throws IOException;

       int getHashCode() throws IOException;
   }

   private static class IntegrityCheck implements AuthCheckMDIntegrity,
           ServerProxyTrust, Remote, Serializable {

       private static final long serialVersionUID = 1L;
       private final byte[] objMD;
       private final byte[] locMD;
       private final int hash;
       private final String algorithm;
       private final Object proxy;

       IntegrityCheck(byte[] object, byte[] annotation, int hashCode,
               String algorithm)
               throws NoSuchAlgorithmException, ExportException {
           hash = hashCode;
           MessageDigest md = MessageDigest.getInstance(algorithm);
           md.update(object);
           objMD = md.digest();
           md.reset();
           md.update(annotation);
           locMD = md.digest();
           this.algorithm = algorithm;
/* We could get the exporter from a configuration, but that would
            * risk inadequate Confidentiality, worse, it may keep the vm
            * alive while the exporter remains exported, or worse still the
            * server may be subject to DOS attacks if DGC is enabled,
            * which is what we're trying to avoid in the first place,
            * since the proxy is used to allow the client to Authenticate
* the server to prevent DOS attacks in the client, the fix would be
            * worse than the original bug.
            *
            * I'll add a configuration item later to allow for different
            * secure ServerEndpoint's
            *
            * This exported object only remains exported while a strong
            * reference is kept to the enclosing MarshalledInstance.  Since
            * it is the registrar proxy (reggie) that creates a
* MarshalledInstance, a reference would need to be maintained in the
            * client.
            */
           Exporter exporter =
                   new BasicJeriExporter(
                   SslServerEndpoint.getInstance(0),
                   new ProxyTrustILFactory(
                   new BasicMethodConstraints(
                   new InvocationConstraints(
                   new InvocationConstraint[]{
               Confidentiality.YES,
               ConfidentialityStrength.STRONG
           }, null) //End InvocationConstraints constructor
                   )//End BasicMethodConstraints constructor
                   , null)//End ProxyTrustILFactory constructor
                   , false, false);//End BasiceJeriExporter constructor
           proxy = exporter.export(this);
       }

       public TrustVerifier getProxyVerifier() throws RemoteException {
           return new BasicProxyTrustVerifier(proxy);
       //return new ProxyTrustVerifier();
       }

       public byte[] getLocMD() throws RemoteException {
           return locMD;
       }

       public byte[] getObjMD() throws RemoteException {
           return objMD;
       }

       public String getAlgorithm() throws RemoteException {
           return algorithm;
       }

       public int getHashCode() throws IOException {
           return hash;
       }

       private Object writeReplace() {
           return proxy;
       }

       // Prevent an attacker fabricating an instance.
       private void readObject(ObjectInputStream in)
               throws InvalidObjectException {
           throw new InvalidObjectException("Proxy Required");
       }
   }

/* Stores unique marshalled Instance references, as defined by fullyEquals()
    * duplicates are only added if they are not fullyEqual.
    */
   private static class Cup {

List<MarshalledInstance> items = new ArrayList<MarshalledInstance>(2);

       MarshalledInstance putIfAbsent(MarshalledInstance t) {
           synchronized (items) {
               Iterator<MarshalledInstance> it = items.iterator();
               while (it.hasNext()) {
                   MarshalledInstance i = it.next();
                   if (t.fullyEquals(i)) {
                       return i;
                   }
               }
               items.add(t);
               return null;
           }
       }
   }
}

Reply via email to