You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@river.apache.org by Peter Firmstone <ji...@zeus.net.au> on 2011/02/05 11:35:42 UTC

The internet - Unicast Discovery Denial of Service Attack

The following class reads in the Unicast V2 discovery response from a 
lookup service, the intent of this class is to isolate this part of 
discovery on its own thread, to guard against an attackers carefully 
crafted input stream, when attempting to discover a lookup service on 
the internet.

The process of unmarshalling is isolated to a single thread, an 
Exception handler captures any Errors and terminates the thread.

This protects the application thread from Errors, like StackOverflowError.


/*
 * 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 org.apache.river.impl.security.dos;

import com.sun.jini.discovery.DiscoveryProtocolException;
import com.sun.jini.discovery.UnicastResponse;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.Collection;
import java.util.concurrent.Callable;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.io.MarshalledInstance;

class DiscoveryV2ReadUncastResponseTask implements 
Callable<UnicastResponse> {

    private final InputStream in;
    private final ClassLoader defaultLoader;
    private final boolean verifyCodebaseIntegrity;
    private final ClassLoader verifierLoader;
    private final Collection context;

    DiscoveryV2ReadUncastResponseTask(InputStream in,
            ClassLoader defaultLoader,
            boolean verifyCodebaseIntegrity,
            ClassLoader verifierLoader,
            Collection context)
    {
        this.in = in;
        this.defaultLoader = defaultLoader;
        this.verifyCodebaseIntegrity = verifyCodebaseIntegrity;
        this.verifierLoader = verifierLoader;
        this.context = context;
    }

    public UnicastResponse call() throws Exception {
        try {
            DataInput din = new DataInputStream(in);
            String host = din.readUTF();

            // read LUS port
            int port = din.readUnsignedShort();
            String[] groups = new String[din.readInt()];
            for (int i = 0; i < groups.length; i++) {
                groups[i] = din.readUTF();
            }
            MarshalledInstance mi = (MarshalledInstance) new 
ObjectInputStream(in).readObject();
            ServiceRegistrar reg =
                    (ServiceRegistrar) mi.get(defaultLoader,
                    verifyCodebaseIntegrity, verifierLoader, context);
            return new UnicastResponse(host, port, groups, reg);
        } catch (RuntimeException e) {
            throw new DiscoveryProtocolException(null, e);
        }
    }
}


/*
 * 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 org.apache.river.impl.security.dos;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Performs Callable tasks in an isolated thread, which is terminated
 * if any Errors are experienced.  This may take some time to terminate 
the thread
 * as it is not forcibly stopped.  The thread priority is minimal and a 
daemon
 * thread so it would take a number of unterminated threads to cause 
problems.
 *
 * The caller can give up on the execution of the task by setting a timeout.
 *
 * @param T
 * @author peter
 */
public class Isolate<T> {
    private volatile ExecutorService isolateExecutor;
    private volatile Throwable thrown;
   
    public Isolate()
    {
        thrown = null;
    isolateExecutor = Executors.newSingleThreadExecutor(new Factory(this));
    }
   
    /**
     * Process Callable tasks in isolation.
     * If an ExecutionException has been thrown, the task should be 
abandoned.
     * If an IsolationException has been thrown, an Error has occurred with
     * the Isolate and a new object should be created.
     *
     * @param task
     * @param timeout
     * @param timeUnit
     * @return
     * @throws org.apache.river.impl.security.dos.Isolate.IsolationException
     * @throws java.util.concurrent.ExecutionException
     */
    public T process(Callable<T> task, long timeout, TimeUnit timeUnit) 
throws
            IsolationException, ExecutionException, InterruptedException,
            TimeoutException {
        if ( thrown != null){
            throw new IsolationException ("Isolate has" +
                  "experienced an error during processing and " +
                  "cannot perform further " +
                  "tasks", thrown);
        }
        return isolateExecutor.submit(task).get(timeout, timeUnit);
    }
   
    private void terminate(Throwable e){
    thrown = e;
    isolateExecutor.shutdownNow();   
    }
   
    private static class Factory implements ThreadFactory{
    private static final ThreadGroup tg = new ThreadGroup("Isolated");
    {
        tg.setDaemon(true);
        tg.setMaxPriority(4);
    }
    private final Isolate isolate;
    Factory(Isolate uri){
        isolate = uri;
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(tg, r);
        t.setUncaughtExceptionHandler(new ExceptionHandler(isolate));
        return t;
    }   
    }
   
    private static class ExceptionHandler implements 
Thread.UncaughtExceptionHandler{
    private final Isolate isolate;
   
    ExceptionHandler(Isolate uri){
        isolate = uri;
    }

    public void uncaughtException(Thread t, Throwable e) {
        // For all other Exceptions we let the ExecutorService handle it.
        if ( e instanceof Error){
        // Do we want to take different actions based on the error?
        isolate.terminate(e);
        }
    }
   
    }
   
    public static class IsolationException extends Exception {
        private static final long serialVersionUID = 1L;

        public IsolationException() {
            super();
        }

        public IsolationException(String message) {
            super(message);
        }

        public IsolationException(String message, Throwable cause) {
            super(message, cause);
        }

        public IsolationException(Throwable cause) {
            super(cause);
        }
    }
}






The internet - MarshalledInstance Denial of Service Attack

Posted by Peter Firmstone <ji...@zeus.net.au>.
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;
            }
        }
    }
}