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