Blob Blame History Raw
From 448087c001fd302df4a8b73f1dd87631c2ae0e61 Mon Sep 17 00:00:00 2001
From: Marian Koncek <mkoncek@redhat.com>
Date: Wed, 14 Oct 2020 15:09:29 +0200
Subject: [PATCH] CVE-2015-7501

---
 .../functors/CloneTransformer.java            | 32 ++++++++++++-
 .../collections4/functors/ForClosure.java     | 33 ++++++++++++-
 .../collections4/functors/FunctorUtils.java   | 35 ++++++++++++++
 .../functors/InstantiateFactory.java          | 32 ++++++++++++-
 .../functors/InstantiateTransformer.java      | 31 +++++++++++++
 .../functors/InvokerTransformer.java          | 32 ++++++++++++-
 .../functors/PrototypeFactory.java            | 46 +++++++++++++++++++
 .../collections4/functors/WhileClosure.java   | 33 ++++++++++++-
 .../collections4/FactoryUtilsTest.java        | 35 --------------
 .../collections4/TransformerUtilsTest.java    |  1 -
 10 files changed, 269 insertions(+), 41 deletions(-)

diff --git a/src/main/java/org/apache/commons/collections4/functors/CloneTransformer.java b/src/main/java/org/apache/commons/collections4/functors/CloneTransformer.java
index 87b080b..90fb3ef 100644
--- a/src/main/java/org/apache/commons/collections4/functors/CloneTransformer.java
+++ b/src/main/java/org/apache/commons/collections4/functors/CloneTransformer.java
@@ -16,6 +16,9 @@
  */
 package org.apache.commons.collections4.functors;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
 
 import org.apache.commons.collections4.Transformer;
@@ -24,7 +27,17 @@ import org.apache.commons.collections4.Transformer;
  * Transformer implementation that returns a clone of the input object.
  * <p>
  * Clone is performed using <code>PrototypeFactory.prototypeFactory(input).create()</code>.
- *
+ * <p>
+ * <b>WARNING:</b> This class will throw an
+ * {@link UnsupportedOperationException} when trying to serialize or
+ * de-serialize an instance to prevent potential remote code execution exploits.
+ * <p>
+ * In order to re-enable serialization support for {@code CloneTransformer}
+ * the following system property can be used (via -Dproperty=true):
+ * <pre>
+ * org.apache.commons.collections.enableUnsafeSerialization
+ * </pre>
+ * 
  * @since 3.0
  * @version $Id: CloneTransformer.java 1543950 2013-11-20 21:13:35Z tn $
  */
@@ -73,4 +86,21 @@ public class CloneTransformer<T> implements Transformer<T, T>, Serializable {
         return INSTANCE;
     }
 
+    /**
+     * 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/main/java/org/apache/commons/collections4/functors/ForClosure.java b/src/main/java/org/apache/commons/collections4/functors/ForClosure.java
index ef5a4d1..fdf1d80 100644
--- a/src/main/java/org/apache/commons/collections4/functors/ForClosure.java
+++ b/src/main/java/org/apache/commons/collections4/functors/ForClosure.java
@@ -16,13 +16,26 @@
  */
 package org.apache.commons.collections4.functors;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
 
 import org.apache.commons.collections4.Closure;
 
 /**
  * Closure implementation that calls another closure n times, like a for loop.
- *
+ * <p>
+ * <b>WARNING:</b> This class will throw an
+ * {@link UnsupportedOperationException} when trying to serialize or
+ * de-serialize an instance to prevent potential remote code execution exploits.
+ * <p>
+ * In order to re-enable serialization support for {@code ForClosure}
+ * the following system property can be used (via -Dproperty=true):
+ * <pre>
+ * org.apache.commons.collections.enableUnsafeSerialization
+ * </pre>
+ * 
  * @since 3.0
  * @version $Id: ForClosure.java 1477798 2013-04-30 19:49:02Z tn $
  */
@@ -102,4 +115,22 @@ public class ForClosure<E> implements Closure<E>, 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/main/java/org/apache/commons/collections4/functors/FunctorUtils.java b/src/main/java/org/apache/commons/collections4/functors/FunctorUtils.java
index de3ccbd..57da174 100644
--- a/src/main/java/org/apache/commons/collections4/functors/FunctorUtils.java
+++ b/src/main/java/org/apache/commons/collections4/functors/FunctorUtils.java
@@ -16,6 +16,8 @@
  */
 package org.apache.commons.collections4.functors;
 
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Collection;
 
 import org.apache.commons.collections4.Closure;
@@ -30,6 +32,10 @@ import org.apache.commons.collections4.Transformer;
  */
 class FunctorUtils {
 
+    /** System property key to enable unsafe serialization */
+    final static String UNSAFE_SERIALIZABLE_PROPERTY
+        = "org.apache.commons.collections.enableUnsafeSerialization";
+
     /**
      * Restricted constructor.
      */
@@ -204,4 +210,33 @@ class FunctorUtils {
         return (Transformer<I, O>) transformer;
     }
 
+    /**
+     * 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/main/java/org/apache/commons/collections4/functors/InstantiateFactory.java b/src/main/java/org/apache/commons/collections4/functors/InstantiateFactory.java
index dd6c548..a9fb91f 100644
--- a/src/main/java/org/apache/commons/collections4/functors/InstantiateFactory.java
+++ b/src/main/java/org/apache/commons/collections4/functors/InstantiateFactory.java
@@ -16,6 +16,9 @@
  */
 package org.apache.commons.collections4.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,7 +28,17 @@ import org.apache.commons.collections4.FunctorException;
 
 /**
  * Factory implementation that creates a new object instance by reflection.
- *
+ * <p>
+ * <b>WARNING:</b> This class will throw an
+ * {@link UnsupportedOperationException} when trying to serialize or
+ * de-serialize an instance to prevent potential remote code execution exploits.
+ * <p>
+ * In order to re-enable serialization support for {@code InstantiateTransformer}
+ * the following system property can be used (via -Dproperty=true):
+ * <pre>
+ * org.apache.commons.collections.enableUnsafeSerialization
+ * </pre>
+ * 
  * @since 3.0
  * @version $Id: InstantiateFactory.java 1477798 2013-04-30 19:49:02Z tn $
  */
@@ -133,4 +146,21 @@ public class InstantiateFactory<T> implements Factory<T>, Serializable {
         }
     }
 
+    /**
+     * 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/main/java/org/apache/commons/collections4/functors/InstantiateTransformer.java b/src/main/java/org/apache/commons/collections4/functors/InstantiateTransformer.java
index ce3d662..0595316 100644
--- a/src/main/java/org/apache/commons/collections4/functors/InstantiateTransformer.java
+++ b/src/main/java/org/apache/commons/collections4/functors/InstantiateTransformer.java
@@ -16,6 +16,9 @@
  */
 package org.apache.commons.collections4.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.collections4.Transformer;
 
 /**
  * Transformer implementation that creates a new object instance by reflection.
+ * <p>
+ * <b>WARNING:</b> This class will throw an
+ * {@link UnsupportedOperationException} when trying to serialize or
+ * de-serialize an instance to prevent potential remote code execution exploits.
+ * <p>
+ * In order to re-enable serialization support for {@code InstantiateTransformer}
+ * the following system property can be used (via -Dproperty=true):
+ * <pre>
+ * org.apache.commons.collections.enableUnsafeSerialization
+ * </pre>
  *
  * @since 3.0
  * @version $Id: InstantiateTransformer.java 1543950 2013-11-20 21:13:35Z tn $
@@ -125,4 +138,22 @@ public class InstantiateTransformer<T> implements Transformer<Class<? extends T>
         }
     }
 
+    /**
+     * 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/main/java/org/apache/commons/collections4/functors/InvokerTransformer.java b/src/main/java/org/apache/commons/collections4/functors/InvokerTransformer.java
index bd2d67d..f77f164 100644
--- a/src/main/java/org/apache/commons/collections4/functors/InvokerTransformer.java
+++ b/src/main/java/org/apache/commons/collections4/functors/InvokerTransformer.java
@@ -16,6 +16,9 @@
  */
 package org.apache.commons.collections4.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,7 +28,17 @@ import org.apache.commons.collections4.Transformer;
 
 /**
  * Transformer implementation that creates a new object instance by reflection.
- *
+ * <p>
+ * <b>WARNING:</b> This class will throw an
+ * {@link UnsupportedOperationException} when trying to serialize or
+ * de-serialize an instance to prevent potential remote code execution exploits.
+ * <p>
+ * In order to re-enable serialization support for {@code InvokerTransformer}
+ * the following system property can be used (via -Dproperty=true):
+ * <pre>
+ * org.apache.commons.collections.enableUnsafeSerialization
+ * </pre>
+ * 
  * @since 3.0
  * @version $Id: InvokerTransformer.java 1543247 2013-11-19 00:36:51Z ggregory $
  */
@@ -139,4 +152,21 @@ public class InvokerTransformer<I, O> implements Transformer<I, O>, 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/main/java/org/apache/commons/collections4/functors/PrototypeFactory.java b/src/main/java/org/apache/commons/collections4/functors/PrototypeFactory.java
index 8922d15..696fa35 100644
--- a/src/main/java/org/apache/commons/collections4/functors/PrototypeFactory.java
+++ b/src/main/java/org/apache/commons/collections4/functors/PrototypeFactory.java
@@ -47,6 +47,16 @@ public class PrototypeFactory {
      * <li>public copy constructor
      * <li>serialization clone
      * <ul>
+     * <p>
+     * <b>WARNING:</b> 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.
+     * <p>
+     * In order to re-enable serialization support the following system property
+     * can be used (via -Dproperty=true):
+     * <pre>
+     * org.apache.commons.collections.enableUnsafeSerialization
+     * </pre>
      *
      * @param <T>  the type the factory creates
      * @param prototype  the object to clone each time in the factory
@@ -141,6 +151,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
@@ -200,6 +228,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/main/java/org/apache/commons/collections4/functors/WhileClosure.java b/src/main/java/org/apache/commons/collections4/functors/WhileClosure.java
index b169d44..8c8eb91 100644
--- a/src/main/java/org/apache/commons/collections4/functors/WhileClosure.java
+++ b/src/main/java/org/apache/commons/collections4/functors/WhileClosure.java
@@ -16,6 +16,9 @@
  */
 package org.apache.commons.collections4.functors;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
 
 import org.apache.commons.collections4.Closure;
@@ -24,7 +27,17 @@ import org.apache.commons.collections4.Predicate;
 /**
  * Closure implementation that executes a closure repeatedly until a condition is met,
  * like a do-while or while loop.
- *
+ * <p>
+ * <b>WARNING:</b> This class will throw an
+ * {@link UnsupportedOperationException} when trying to serialize or
+ * de-serialize an instance to prevent potential remote code execution exploits.
+ * <p>
+ * In order to re-enable serialization support for {@code WhileClosure}
+ * the following system property can be used (via -Dproperty=true):
+ * <pre>
+ * org.apache.commons.collections.enableUnsafeSerialization
+ * </pre>
+ * 
  * @since 3.0
  * @version $Id: WhileClosure.java 1477798 2013-04-30 19:49:02Z tn $
  */
@@ -120,4 +133,22 @@ public class WhileClosure<E> implements Closure<E>, 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/java/org/apache/commons/collections4/FactoryUtilsTest.java b/src/test/java/org/apache/commons/collections4/FactoryUtilsTest.java
index 941cd40..7394ede 100644
--- a/src/test/java/org/apache/commons/collections4/FactoryUtilsTest.java
+++ b/src/test/java/org/apache/commons/collections4/FactoryUtilsTest.java
@@ -112,15 +112,6 @@ public class FactoryUtilsTest extends junit.framework.TestCase {
         final Date created = factory.create();
         assertTrue(proto != created);
         assertEquals(proto, created);
-
-        // check serialisation works
-        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-        final ObjectOutputStream out = new ObjectOutputStream(buffer);
-        out.writeObject(factory);
-        out.close();
-        final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()));
-        in.readObject();
-        in.close();
     }
 
     public void testPrototypeFactoryPublicCopyConstructor() throws Exception {
@@ -130,23 +121,6 @@ public class FactoryUtilsTest extends junit.framework.TestCase {
         final 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 (final NotSerializableException ex) {
-            out.close();
-        }
-        factory = FactoryUtils.<Object>prototypeFactory(new Mock2("S"));
-        buffer = new ByteArrayOutputStream();
-        out = new ObjectOutputStream(buffer);
-        out.writeObject(factory);
-        out.close();
-        final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()));
-        in.readObject();
-        in.close();
     }
 
     public void testPrototypeFactoryPublicSerialization() throws Exception {
@@ -156,15 +130,6 @@ public class FactoryUtilsTest extends junit.framework.TestCase {
         final Integer created = factory.create();
         assertTrue(proto != created);
         assertEquals(proto, created);
-
-        // check serialisation works
-        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-        final ObjectOutputStream out = new ObjectOutputStream(buffer);
-        out.writeObject(factory);
-        out.close();
-        final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()));
-        in.readObject();
-        in.close();
     }
 
     public void testPrototypeFactoryPublicSerializationError() {
diff --git a/src/test/java/org/apache/commons/collections4/TransformerUtilsTest.java b/src/test/java/org/apache/commons/collections4/TransformerUtilsTest.java
index 9c0c530..a924e05 100644
--- a/src/test/java/org/apache/commons/collections4/TransformerUtilsTest.java
+++ b/src/test/java/org/apache/commons/collections4/TransformerUtilsTest.java
@@ -431,7 +431,6 @@ public class TransformerUtilsTest extends junit.framework.TestCase {
      */
     public void testSingletonPatternInSerialization() {
         final Object[] singletones = new Object[] {
-                CloneTransformer.INSTANCE,
                 ExceptionTransformer.INSTANCE,
                 NOPTransformer.INSTANCE,
                 StringValueTransformer.stringValueTransformer(),
-- 
2.26.2