diff --git a/src/java/org/apache/commons/collections/functors/CloneTransformer.java b/src/java/org/apache/commons/collections/functors/CloneTransformer.java index 7200402..3df18ff 100644 --- a/src/java/org/apache/commons/collections/functors/CloneTransformer.java +++ b/src/java/org/apache/commons/collections/functors/CloneTransformer.java @@ -16,6 +16,9 @@ */ package org.apache.commons.collections.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import org.apache.commons.collections.Transformer; @@ -24,6 +27,16 @@ import org.apache.commons.collections.Transformer; * Transformer implementation that returns a clone of the input object. *
* Clone is performed using PrototypeFactory.getInstance(input).create()
.
+ *
+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *
+ * In order to re-enable serialization support for {@code CloneTransformer} + * the following system property can be used (via -Dproperty=true): + *
+ * org.apache.commons.collections.enableUnsafeSerialization + ** * @since Commons Collections 3.0 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ @@ -68,4 +81,21 @@ public class CloneTransformer implements Transformer, Serializable { return PrototypeFactory.getInstance(input).create(); } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(CloneTransformer.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(CloneTransformer.class); + is.defaultReadObject(); + } } diff --git a/src/java/org/apache/commons/collections/functors/ForClosure.java b/src/java/org/apache/commons/collections/functors/ForClosure.java index f0355c4..e15475c 100644 --- a/src/java/org/apache/commons/collections/functors/ForClosure.java +++ b/src/java/org/apache/commons/collections/functors/ForClosure.java @@ -16,12 +16,25 @@ */ package org.apache.commons.collections.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import org.apache.commons.collections.Closure; /** * Closure implementation that calls another closure n times, like a for loop. + *
+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *
+ * In order to re-enable serialization support for {@code ForClosure} + * the following system property can be used (via -Dproperty=true): + *
+ * org.apache.commons.collections.enableUnsafeSerialization + ** * @since Commons Collections 3.0 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ @@ -102,4 +115,22 @@ public class ForClosure implements Closure, Serializable { return iCount; } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(ForClosure.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(ForClosure.class); + is.defaultReadObject(); + } + } diff --git a/src/java/org/apache/commons/collections/functors/FunctorUtils.java b/src/java/org/apache/commons/collections/functors/FunctorUtils.java index 75f8d9b..aa7bec3 100644 --- a/src/java/org/apache/commons/collections/functors/FunctorUtils.java +++ b/src/java/org/apache/commons/collections/functors/FunctorUtils.java @@ -16,6 +16,8 @@ */ package org.apache.commons.collections.functors; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Collection; import java.util.Iterator; @@ -33,7 +35,11 @@ import org.apache.commons.collections.Transformer; * @author Matt Benson */ class FunctorUtils { - + + /** System property key to enable unsafe serialization */ + final static String UNSAFE_SERIALIZABLE_PROPERTY + = "org.apache.commons.collections.enableUnsafeSerialization"; + /** * Restricted constructor. */ @@ -152,4 +158,33 @@ class FunctorUtils { } } + /** + * Package-private helper method to check if serialization support is + * enabled for unsafe classes. + * + * @param clazz the clazz to check for serialization support + * @throws UnsupportedOperationException if unsafe serialization is disabled + */ + static void checkUnsafeSerialization(Class clazz) { + String unsafeSerializableProperty; + + try { + unsafeSerializableProperty = + (String) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return System.getProperty(UNSAFE_SERIALIZABLE_PROPERTY); + } + }); + } catch (SecurityException ex) { + unsafeSerializableProperty = null; + } + + if (!"true".equalsIgnoreCase(unsafeSerializableProperty)) { + throw new UnsupportedOperationException( + "Serialization support for " + clazz.getName() + " is disabled for security reasons. " + + "To enable it set system property '" + UNSAFE_SERIALIZABLE_PROPERTY + "' to 'true', " + + "but you must ensure that your application does not de-serialize objects from untrusted sources."); + } + } + } diff --git a/src/java/org/apache/commons/collections/functors/InstantiateFactory.java b/src/java/org/apache/commons/collections/functors/InstantiateFactory.java index 5d375de..938d6dc 100644 --- a/src/java/org/apache/commons/collections/functors/InstantiateFactory.java +++ b/src/java/org/apache/commons/collections/functors/InstantiateFactory.java @@ -16,6 +16,9 @@ */ package org.apache.commons.collections.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -25,6 +28,16 @@ import org.apache.commons.collections.FunctorException; /** * Factory implementation that creates a new object instance by reflection. + *
+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *
+ * In order to re-enable serialization support for {@code InstantiateTransformer} + * the following system property can be used (via -Dproperty=true): + *
+ * org.apache.commons.collections.enableUnsafeSerialization + ** * @since Commons Collections 3.0 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ @@ -136,5 +149,22 @@ public class InstantiateFactory implements Factory, Serializable { throw new FunctorException("InstantiateFactory: Constructor threw an exception", ex); } } - + + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(InstantiateFactory.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(InstantiateFactory.class); + is.defaultReadObject(); + } } diff --git a/src/java/org/apache/commons/collections/functors/InstantiateTransformer.java b/src/java/org/apache/commons/collections/functors/InstantiateTransformer.java index 73d6b2f..4927f05 100644 --- a/src/java/org/apache/commons/collections/functors/InstantiateTransformer.java +++ b/src/java/org/apache/commons/collections/functors/InstantiateTransformer.java @@ -16,6 +16,9 @@ */ package org.apache.commons.collections.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -25,6 +28,16 @@ import org.apache.commons.collections.Transformer; /** * Transformer implementation that creates a new object instance by reflection. + *
+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *
+ * In order to re-enable serialization support for {@code InstantiateTransformer} + * the following system property can be used (via -Dproperty=true): + *
+ * org.apache.commons.collections.enableUnsafeSerialization + ** * @since Commons Collections 3.0 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ @@ -116,4 +129,22 @@ public class InstantiateTransformer implements Transformer, Serializable { } } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(InstantiateTransformer.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(InstantiateTransformer.class); + is.defaultReadObject(); + } + } diff --git a/src/java/org/apache/commons/collections/functors/InvokerTransformer.java b/src/java/org/apache/commons/collections/functors/InvokerTransformer.java index 6f60961..75f48af 100644 --- a/src/java/org/apache/commons/collections/functors/InvokerTransformer.java +++ b/src/java/org/apache/commons/collections/functors/InvokerTransformer.java @@ -16,6 +16,9 @@ */ package org.apache.commons.collections.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -25,6 +28,16 @@ import org.apache.commons.collections.Transformer; /** * Transformer implementation that creates a new object instance by reflection. + *
+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *
+ * In order to re-enable serialization support for {@code InvokerTransformer} + * the following system property can be used (via -Dproperty=true): + *
+ * org.apache.commons.collections.enableUnsafeSerialization + ** * @since Commons Collections 3.0 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ @@ -134,4 +147,21 @@ public class InvokerTransformer implements Transformer, Serializable { } } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class); + is.defaultReadObject(); + } } diff --git a/src/java/org/apache/commons/collections/functors/PrototypeFactory.java b/src/java/org/apache/commons/collections/functors/PrototypeFactory.java index 4fa4150..d9908fa 100644 --- a/src/java/org/apache/commons/collections/functors/PrototypeFactory.java +++ b/src/java/org/apache/commons/collections/functors/PrototypeFactory.java @@ -49,6 +49,16 @@ public class PrototypeFactory { *
+ * WARNING: This method will return a {@code Factory} + * that will throw an {@link UnsupportedOperationException} when trying to serialize + * or de-serialize it to prevent potential remote code execution exploits. + *
+ * In order to re-enable serialization support the following system property + * can be used (via -Dproperty=true): + *
+ * org.apache.commons.collections.enableUnsafeSerialization + ** * @param prototype the object to clone each time in the factory * @return the
prototype
factory
@@ -144,6 +154,24 @@ public class PrototypeFactory {
throw new FunctorException("PrototypeCloneFactory: Clone method threw an exception", ex);
}
}
+
+ /**
+ * Overrides the default writeObject implementation to prevent
+ * serialization (see COLLECTIONS-580).
+ */
+ private void writeObject(ObjectOutputStream os) throws IOException {
+ FunctorUtils.checkUnsafeSerialization(PrototypeCloneFactory.class);
+ os.defaultWriteObject();
+ }
+
+ /**
+ * Overrides the default readObject implementation to prevent
+ * de-serialization (see COLLECTIONS-580).
+ */
+ private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
+ FunctorUtils.checkUnsafeSerialization(PrototypeCloneFactory.class);
+ is.defaultReadObject();
+ }
}
// PrototypeSerializationFactory
@@ -204,6 +232,24 @@ public class PrototypeFactory {
}
}
}
+
+ /**
+ * Overrides the default writeObject implementation to prevent
+ * serialization (see COLLECTIONS-580).
+ */
+ private void writeObject(ObjectOutputStream os) throws IOException {
+ FunctorUtils.checkUnsafeSerialization(PrototypeSerializationFactory.class);
+ os.defaultWriteObject();
+ }
+
+ /**
+ * Overrides the default readObject implementation to prevent
+ * de-serialization (see COLLECTIONS-580).
+ */
+ private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
+ FunctorUtils.checkUnsafeSerialization(PrototypeSerializationFactory.class);
+ is.defaultReadObject();
+ }
}
}
diff --git a/src/java/org/apache/commons/collections/functors/WhileClosure.java b/src/java/org/apache/commons/collections/functors/WhileClosure.java
index 853e83a..596afc8 100644
--- a/src/java/org/apache/commons/collections/functors/WhileClosure.java
+++ b/src/java/org/apache/commons/collections/functors/WhileClosure.java
@@ -16,6 +16,9 @@
*/
package org.apache.commons.collections.functors;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.Serializable;
import org.apache.commons.collections.Closure;
@@ -24,6 +27,16 @@ import org.apache.commons.collections.Predicate;
/**
* Closure implementation that executes a closure repeatedly until a condition is met,
* like a do-while or while loop.
+ * + * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *
+ * In order to re-enable serialization support for {@code WhileClosure} + * the following system property can be used (via -Dproperty=true): + *
+ * org.apache.commons.collections.enableUnsafeSerialization + ** * @since Commons Collections 3.0 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ @@ -120,4 +133,22 @@ public class WhileClosure implements Closure, Serializable { return iDoLoop; } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(WhileClosure.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(WhileClosure.class); + is.defaultReadObject(); + } + } diff --git a/src/test/org/apache/commons/collections/TestFactoryUtils.java b/src/test/org/apache/commons/collections/TestFactoryUtils.java index 0895903..bc7d729 100644 --- a/src/test/org/apache/commons/collections/TestFactoryUtils.java +++ b/src/test/org/apache/commons/collections/TestFactoryUtils.java @@ -136,15 +136,6 @@ public class TestFactoryUtils extends junit.framework.TestCase { Object created = factory.create(); assertTrue(proto != created); assertEquals(proto, created); - - // check serialisation works - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(buffer); - out.writeObject(factory); - out.close(); - ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())); - Object dest = in.readObject(); - in.close(); } public void testPrototypeFactoryPublicCopyConstructor() throws Exception { @@ -154,23 +145,6 @@ public class TestFactoryUtils extends junit.framework.TestCase { Object created = factory.create(); assertTrue(proto != created); assertEquals(proto, created); - - // check serialisation works - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(buffer); - try { - out.writeObject(factory); - } catch (NotSerializableException ex) { - out.close(); - } - factory = FactoryUtils.prototypeFactory(new Mock2("S")); - buffer = new ByteArrayOutputStream(); - out = new ObjectOutputStream(buffer); - out.writeObject(factory); - out.close(); - ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())); - Object dest = in.readObject(); - in.close(); } public void testPrototypeFactoryPublicSerialization() throws Exception { @@ -180,15 +154,6 @@ public class TestFactoryUtils extends junit.framework.TestCase { Object created = factory.create(); assertTrue(proto != created); assertEquals(proto, created); - - // check serialisation works - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(buffer); - out.writeObject(factory); - out.close(); - ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())); - Object dest = in.readObject(); - in.close(); } public void testPrototypeFactoryPublicSerializationError() {