Repository: logging-log4j2 Updated Branches: refs/heads/master 5aff929bb -> 5dcc19215
[LOG4J2-1863] Add class filtering to AbstractSocketServer This allows a whitelist of class names to be specified to configure which classes are allowed to be deserialized in both TcpSocketServer and UdpSocketServer. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/5dcc1921 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/5dcc1921 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/5dcc1921 Branch: refs/heads/master Commit: 5dcc19215827db29c993d0305ee2b0d8dd05939d Parents: 5aff929 Author: Matt Sicker <[email protected]> Authored: Sat Mar 11 16:01:46 2017 -0600 Committer: Matt Sicker <[email protected]> Committed: Sun Apr 2 12:41:20 2017 -0500 ---------------------------------------------------------------------- .../core/net/server/AbstractSocketServer.java | 13 ++++ .../server/ObjectInputStreamLogEventBridge.java | 23 ++++++- .../log4j/core/net/server/TcpSocketServer.java | 25 +++++++- .../log4j/core/net/server/UdpSocketServer.java | 18 +++++- .../core/util/FilteredObjectInputStream.java | 67 ++++++++++++++++++++ 5 files changed, 140 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5dcc1921/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java index 9836694..923d3f4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/AbstractSocketServer.java @@ -26,6 +26,8 @@ import java.io.InputStreamReader; import java.net.InetAddress; import java.net.URI; import java.net.URL; +import java.util.Collections; +import java.util.List; import java.util.Objects; import com.beust.jcommander.Parameter; @@ -70,6 +72,9 @@ public abstract class AbstractSocketServer<T extends InputStream> extends LogEve "-a" }, converter = InetAddressConverter.class, description = "Server socket local bind address.") private InetAddress localBindAddress; + @Parameter(names = {"--classes", "-C"}, description = "Additional classes to allow deserialization") + private List<String> allowedClasses; + String getConfigLocation() { return configLocation; } @@ -101,6 +106,14 @@ public abstract class AbstractSocketServer<T extends InputStream> extends LogEve void setLocalBindAddress(final InetAddress localBindAddress) { this.localBindAddress = localBindAddress; } + + List<String> getAllowedClasses() { + return allowedClasses == null ? Collections.<String>emptyList() : allowedClasses; + } + + void setAllowedClasses(final List<String> allowedClasses) { + this.allowedClasses = allowedClasses; + } } /** http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5dcc1921/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/ObjectInputStreamLogEventBridge.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/ObjectInputStreamLogEventBridge.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/ObjectInputStreamLogEventBridge.java index 059f069..a27b1cb 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/ObjectInputStreamLogEventBridge.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/ObjectInputStreamLogEventBridge.java @@ -19,18 +19,37 @@ package org.apache.logging.log4j.core.net.server; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LogEventListener; +import org.apache.logging.log4j.core.util.FilteredObjectInputStream; /** * Reads and logs serialized {@link LogEvent} objects from an {@link ObjectInputStream}. */ public class ObjectInputStreamLogEventBridge extends AbstractLogEventBridge<ObjectInputStream> { + private final List<String> allowedClasses; + + public ObjectInputStreamLogEventBridge() { + this(Collections.<String>emptyList()); + } + + /** + * Constructs an ObjectInputStreamLogEventBridge with additional allowed classes to deserialize. + * + * @param allowedClasses class names to also allow for deserialization + * @since 2.8.2 + */ + public ObjectInputStreamLogEventBridge(final List<String> allowedClasses) { + this.allowedClasses = allowedClasses; + } + @Override public void logEvents(final ObjectInputStream inputStream, final LogEventListener logEventListener) - throws IOException { + throws IOException { try { logEventListener.log((LogEvent) inputStream.readObject()); } catch (final ClassNotFoundException e) { @@ -40,6 +59,6 @@ public class ObjectInputStreamLogEventBridge extends AbstractLogEventBridge<Obje @Override public ObjectInputStream wrapStream(final InputStream inputStream) throws IOException { - return new ObjectInputStream(inputStream); + return new FilteredObjectInputStream(inputStream, allowedClasses); } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5dcc1921/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java index 68ac1ba..b5ce211 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/TcpSocketServer.java @@ -24,6 +24,8 @@ import java.io.OptionalDataException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -148,9 +150,26 @@ public class TcpSocketServer<T extends InputStream> extends AbstractSocketServer */ public static TcpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port, final int backlog, final InetAddress localBindAddress) throws IOException { + return createSerializedSocketServer(port, backlog, localBindAddress, Collections.<String>emptyList()); + } + + /** + * Creates a socket server that reads serialized log events. + * + * @param port the port to listen + * @param localBindAddress The server socket's local bin address + * @param allowedClasses additional class names to allow for deserialization + * @return a new a socket server + * @throws IOException + * if an I/O error occurs when opening the socket. + * @since 2.8.2 + */ + public static TcpSocketServer<ObjectInputStream> createSerializedSocketServer( + final int port, final int backlog, final InetAddress localBindAddress, final List<String> allowedClasses + ) throws IOException { LOGGER.entry(port); final TcpSocketServer<ObjectInputStream> socketServer = new TcpSocketServer<>(port, backlog, localBindAddress, - new ObjectInputStreamLogEventBridge()); + new ObjectInputStreamLogEventBridge(allowedClasses)); return LOGGER.exit(socketServer); } @@ -185,8 +204,8 @@ public class TcpSocketServer<T extends InputStream> extends AbstractSocketServer if (cla.getConfigLocation() != null) { ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(cla.getConfigLocation())); } - final TcpSocketServer<ObjectInputStream> socketServer = TcpSocketServer - .createSerializedSocketServer(cla.getPort(), cla.getBacklog(), cla.getLocalBindAddress()); + final TcpSocketServer<ObjectInputStream> socketServer = TcpSocketServer.createSerializedSocketServer( + cla.getPort(), cla.getBacklog(), cla.getLocalBindAddress(), cla.getAllowedClasses()); final Thread serverThread = socketServer.startNewThread(); if (cla.isInteractive()) { socketServer.awaitTermination(serverThread); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5dcc1921/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java index ed04f69..e761c3b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/server/UdpSocketServer.java @@ -24,6 +24,7 @@ import java.io.ObjectInputStream; import java.io.OptionalDataException; import java.net.DatagramPacket; import java.net.DatagramSocket; +import java.util.List; import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.core.util.BasicCommandLineArguments; @@ -64,6 +65,21 @@ public class UdpSocketServer<T extends InputStream> extends AbstractSocketServer } /** + * Creates a socket server that reads serialized log events. + * + * @param port the port to listen + * @param allowedClasses additional classes to allow for deserialization + * @return a new a socket server + * @throws IOException if an I/O error occurs when opening the socket. + * @since 2.8.2 + */ + public static UdpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port, + final List<String> allowedClasses) + throws IOException { + return new UdpSocketServer<>(port, new ObjectInputStreamLogEventBridge(allowedClasses)); + } + + /** * Creates a socket server that reads XML log events. * * @param port @@ -93,7 +109,7 @@ public class UdpSocketServer<T extends InputStream> extends AbstractSocketServer ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(cla.getConfigLocation())); } final UdpSocketServer<ObjectInputStream> socketServer = UdpSocketServer - .createSerializedSocketServer(cla.getPort()); + .createSerializedSocketServer(cla.getPort(), cla.getAllowedClasses()); final Thread serverThread = socketServer.startNewThread(); if (cla.isInteractive()) { socketServer.awaitTermination(serverThread); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5dcc1921/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FilteredObjectInputStream.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FilteredObjectInputStream.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FilteredObjectInputStream.java new file mode 100644 index 0000000..57cc31c --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FilteredObjectInputStream.java @@ -0,0 +1,67 @@ +/* + * 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.logging.log4j.core.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * Extended ObjectInputStream that only allows certain classes to be deserialized. + * + * @since 2.8.2 + */ +public class FilteredObjectInputStream extends ObjectInputStream { + + private static final List<String> REQUIRED_JAVA_CLASSES = Arrays.asList( + // for StandardLevel + "java.lang.Enum", + // for location information + "java.lang.StackTraceElement", + // for Message delegate + "java.rmi.MarshalledObject", + "[B" + ); + + private final Collection<String> allowedClasses; + + public FilteredObjectInputStream(final InputStream in, final Collection<String> allowedClasses) throws IOException { + super(in); + this.allowedClasses = allowedClasses; + } + + @Override + 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 boolean isAllowedByDefault(final String name) { + return name.startsWith("org.apache.logging.log4j.") || + name.startsWith("[Lorg.apache.logging.log4j.") || + REQUIRED_JAVA_CLASSES.contains(name); + } + +}
