From 39f12f04d0b138377194b7aa43aa16a2ff401f98 Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Fri, 2 Jun 2017 14:37:35 +0200 Subject: [PATCH] Backport fix for CVE-2017-5645 --- .../log4j/FilteredObjectInputStream.java | 76 +++++++++++++++++++ .../java/org/apache/log4j/net/SocketNode.java | 9 ++- 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/apache/log4j/FilteredObjectInputStream.java diff --git a/src/main/java/org/apache/log4j/FilteredObjectInputStream.java b/src/main/java/org/apache/log4j/FilteredObjectInputStream.java new file mode 100644 index 00000000..ea597308 --- /dev/null +++ b/src/main/java/org/apache/log4j/FilteredObjectInputStream.java @@ -0,0 +1,76 @@ +/* + * 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.log4j; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Extended ObjectInputStream that only allows certain classes to be deserialized. + * + * Backported from 2.8.2 + */ +public class FilteredObjectInputStream extends ObjectInputStream { + + private static final Set REQUIRED_JAVA_CLASSES = new HashSet(Arrays.asList(new String[] { + // Types of non-trainsient fields of LoggingEvent + "java.lang.String", + "java.util.Hashtable", + // ThrowableInformation + "[Ljava.lang.String;" + })); + + public static final Collection SYSTEM_ALLOWED_CLASSES = getAllowedClasses(); + + private final Collection allowedClasses; + + public FilteredObjectInputStream(final InputStream in, final Collection allowedClasses) throws IOException { + super(in); + this.allowedClasses = allowedClasses; + } + + protected Class resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException { + String name = desc.getName(); + if (!(isAllowedByDefault(name) || allowedClasses.contains(name))) { + throw new InvalidObjectException("Class is not allowed for deserialization: " + name); + } + return super.resolveClass(desc); + } + + private static Collection getAllowedClasses() { + Collection allowedClasses = new HashSet(); + String property = System.getProperty("org.apache.log4j.net.allowedClasses"); + if (property != null) + allowedClasses.addAll(Arrays.asList(property.split(","))); + return allowedClasses; + } + + private static boolean isAllowedByDefault(final String name) { + return name.startsWith("org.apache.log4j.") || + name.startsWith("[Lorg.apache.log4j.") || + REQUIRED_JAVA_CLASSES.contains(name); + } + +} diff --git a/src/main/java/org/apache/log4j/net/SocketNode.java b/src/main/java/org/apache/log4j/net/SocketNode.java index e977f133..692da289 100644 --- a/src/main/java/org/apache/log4j/net/SocketNode.java +++ b/src/main/java/org/apache/log4j/net/SocketNode.java @@ -22,6 +22,10 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.io.ObjectInputStream; import java.net.Socket; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import org.apache.log4j.FilteredObjectInputStream; import org.apache.log4j.Logger; import org.apache.log4j.spi.LoggerRepository; @@ -53,8 +57,9 @@ public class SocketNode implements Runnable { this.socket = socket; this.hierarchy = hierarchy; try { - ois = new ObjectInputStream( - new BufferedInputStream(socket.getInputStream())); + ois = new FilteredObjectInputStream( + new BufferedInputStream(socket.getInputStream()), + FilteredObjectInputStream.SYSTEM_ALLOWED_CLASSES); } catch(InterruptedIOException e) { Thread.currentThread().interrupt(); logger.error("Could not open ObjectInputStream to "+socket, e); -- 2.33.1