log4j/CVE-2021-44228-4.patch
starlet-dx dbce122982 fix CVE-2021-44228
(cherry picked from commit 3ec2ef9b5f83e82fdcbd0390038e8a92aa1cdabc)
2021-12-11 15:10:28 +08:00

238 lines
13 KiB
Diff

From 7fe72d62fcb9246be792b946e405e1d40d402780 Mon Sep 17 00:00:00 2001
From: Ralph Goers <rgoers@apache.org>
Date: Sat, 4 Dec 2021 20:59:39 -0700
Subject: [PATCH] LOG4J2-3201 - Limit the protocols JNDI can use by default.
Limit the servers and classes that can be accessed via LDAP.
---
.../logging/log4j/core/net/JndiManager.java | 62 ++++++++++---------
.../logging/log4j/core/util/NetUtils.java | 4 ++
src/changes/changes.xml | 3 +
src/site/xdoc/manual/appenders.xml | 27 ++++++++
src/site/xdoc/manual/configuration.xml.vm | 26 ++++++++
src/site/xdoc/manual/extending.xml | 5 +-
src/site/xdoc/manual/lookups.xml | 7 +++
7 files changed, 103 insertions(+), 31 deletions(-)
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
index b392b93..2d7604f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
@@ -209,43 +209,45 @@ public class JndiManager extends AbstractManager {
public synchronized <T> T lookup(final String name) throws NamingException {
try {
URI uri = new URI(name);
- if (!allowedProtocols.contains(uri.getScheme().toLowerCase(Locale.ROOT))) {
- LOGGER.warn("Log4j JNDI does not allow protocol {}", uri.getScheme());
- return null;
- }
- if (LDAP.equalsIgnoreCase(uri.getScheme()) || LDAPS.equalsIgnoreCase(uri.getScheme())) {
- if (!allowedHosts.contains(uri.getHost())) {
- LOGGER.warn("Attempt to access ldap server not in allowed list");
+ if (uri.getScheme() != null) {
+ if (!allowedProtocols.contains(uri.getScheme().toLowerCase(Locale.ROOT))) {
+ LOGGER.warn("Log4j JNDI does not allow protocol {}", uri.getScheme());
return null;
}
- Attributes attributes = this.context.getAttributes(name);
- if (attributes != null) {
- // In testing the "key" for attributes seems to be lowercase while the attribute id is
- // camelcase, but that may just be true for the test LDAP used here. This copies the Attributes
- // to a Map ignoring the "key" and using the Attribute's id as the key in the Map so it matches
- // the Java schema.
- Map<String, Attribute> attributeMap = new HashMap<>();
- NamingEnumeration<? extends Attribute> enumeration = attributes.getAll();
- while (enumeration.hasMore()) {
- Attribute attribute = enumeration.next();
- attributeMap.put(attribute.getID(), attribute);
+ if (LDAP.equalsIgnoreCase(uri.getScheme()) || LDAPS.equalsIgnoreCase(uri.getScheme())) {
+ if (!allowedHosts.contains(uri.getHost())) {
+ LOGGER.warn("Attempt to access ldap server not in allowed list");
+ return null;
}
- Attribute classNameAttr = attributeMap.get(CLASS_NAME);
- if (attributeMap.get(SERIALIZED_DATA) != null) {
- if (classNameAttr != null) {
- String className = classNameAttr.get().toString();
- if (!allowedClasses.contains(className)) {
- LOGGER.warn("Deserialization of {} is not allowed", className);
+ Attributes attributes = this.context.getAttributes(name);
+ if (attributes != null) {
+ // In testing the "key" for attributes seems to be lowercase while the attribute id is
+ // camelcase, but that may just be true for the test LDAP used here. This copies the Attributes
+ // to a Map ignoring the "key" and using the Attribute's id as the key in the Map so it matches
+ // the Java schema.
+ Map<String, Attribute> attributeMap = new HashMap<>();
+ NamingEnumeration<? extends Attribute> enumeration = attributes.getAll();
+ while (enumeration.hasMore()) {
+ Attribute attribute = enumeration.next();
+ attributeMap.put(attribute.getID(), attribute);
+ }
+ Attribute classNameAttr = attributeMap.get(CLASS_NAME);
+ if (attributeMap.get(SERIALIZED_DATA) != null) {
+ if (classNameAttr != null) {
+ String className = classNameAttr.get().toString();
+ if (!allowedClasses.contains(className)) {
+ LOGGER.warn("Deserialization of {} is not allowed", className);
+ return null;
+ }
+ } else {
+ LOGGER.warn("No class name provided for {}", name);
return null;
}
- } else {
- LOGGER.warn("No class name provided for {}", name);
+ } else if (attributeMap.get(REFERENCE_ADDRESS) != null
+ || attributeMap.get(OBJECT_FACTORY) != null) {
+ LOGGER.warn("Referenceable class is not allowed for {}", name);
return null;
}
- } else if (attributeMap.get(REFERENCE_ADDRESS) != null
- || attributeMap.get(OBJECT_FACTORY) != null) {
- LOGGER.warn("Referenceable class is not allowed for {}", name);
- return null;
}
}
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NetUtils.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NetUtils.java
index b9a01ae..6adac59 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NetUtils.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NetUtils.java
@@ -83,6 +83,10 @@ public final class NetUtils {
}
}
+ /**
+ * Returns all the local host names and ip addresses.
+ * @return The local host names and ip addresses.
+ */
public static List<String> getLocalIps() {
List<String> localIps = new ArrayList<>();
localIps.add("localhost");
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9c44684..f2d9c57 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,9 @@
- "remove" - Removed
-->
<release version="2.13.2" date="2020-04-23" description="GA Release 2.13.2">
+ <action issue="LOG4J2-3201" dev="rgoers" type="fix">
+ Limit the protocols JNDI can use by default. Limit the servers and classes that can be accessed via LDAP.
+ </action>
<action issue="LOG4J2-2824" dev="rgoers" type="fix" due-to="CrazyBills">
Implement requiresLocation in GelfLayout to reflect whether location information is used in the message Pattern.
</action>
diff --git a/src/site/xdoc/manual/appenders.xml b/src/site/xdoc/manual/appenders.xml
index ab40094..267f54f 100644
--- a/src/site/xdoc/manual/appenders.xml
+++ b/src/site/xdoc/manual/appenders.xml
@@ -1542,6 +1542,33 @@ public class ConnectionFactory {
<th>Default</th>
<th>Description</th>
</tr>
+ <tr>
+ <td>allowdLdapClasses</td>
+ <td>String</td>
+ <td>null</td>
+ <td>
+ A comma separated list of fully qualified class names that may be accessed by LDAP. The classes
+ must implement Serializable. Only applies when the JMS Appender By default only Java primative classes are allowed.
+ </td>
+ </tr>
+ <tr>
+ <td>allowdLdapHosts</td>
+ <td>String</td>
+ <td>null</td>
+ <td>
+ A comma separated list of host names or ip addresses that may be accessed by LDAP. By default only
+ the local host names and ip addresses are allowed.
+ </td>
+ </tr>
+ <tr>
+ <td>allowdJndiProtocols</td>
+ <td>String</td>
+ <td>null</td>
+ <td>
+ A comma separated list of protocol names that JNDI will allow. By default only java, ldap, and ldaps
+ are the only allowed protocols.
+ </td>
+ </tr>
<tr>
<td>factoryBindingName</td>
<td>String</td>
diff --git a/src/site/xdoc/manual/configuration.xml.vm b/src/site/xdoc/manual/configuration.xml.vm
index a3da3b8..402a96c 100644
--- a/src/site/xdoc/manual/configuration.xml.vm
+++ b/src/site/xdoc/manual/configuration.xml.vm
@@ -1960,6 +1960,32 @@ public class AwesomeTest {
before falling back to the default class loader.
</td>
</tr>
+ <tr>
+ <td><a name="allowedLdapClasses"/>log4j2.allowedLdapClasses</td>
+ <td>LOG4J_ALLOWED_LDAP_CLASSES</td>
+ <td>&nbsp;</td>
+ <td>
+ System property that specifies fully qualified class names that may be accessed by LDAP. The classes
+ must implement Serializable. By default only Java primative classes are allowed.
+ </td>
+ </tr>
+ <tr>
+ <td><a name="allowedLdapHosts"/>log4j2.allowedLdapHosts</td>
+ <td>LOG4J_ALLOWED_LDAP_HOSTS</td>
+ <td>&nbsp;</td>
+ <td>
+ System property that adds host names or ip addresses that may be access by LDAP. By default it only allows
+ the local host names and ip addresses.
+ </td>
+ </tr>
+ <tr>
+ <td><a name="allowedJndiProtocols"/>log4j2.allowedJndiProtocols</td>
+ <td>LOG4J_ALLOWED_JNDI_PROTOCOLS</td>
+ <td>&nbsp;</td>
+ <td>
+ System property that adds protocol names that JNDI will allow. By default it only allows java, ldap, and ldaps.
+ </td>
+ </tr>
<tr>
<td><a name="uuidSequence"/>log4j2.uuidSequence
<br />
diff --git a/src/site/xdoc/manual/extending.xml b/src/site/xdoc/manual/extending.xml
index ba04d68..04c742a 100644
--- a/src/site/xdoc/manual/extending.xml
+++ b/src/site/xdoc/manual/extending.xml
@@ -92,7 +92,10 @@
<dd>Associates LoggerContexts with the ClassLoader that created the caller of the getLogger call. This is
the default ContextSelector.</dd>
<dt><a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/selector/JndiContextSelector.html">JndiContextSelector</a></dt>
- <dd>Locates the LoggerContext by querying JNDI.</dd>
+ <dd>Locates the LoggerContext by querying JNDI. Please see <a href="../manual/configuration.html#allowedJndiProtocols">log4j2.allowedJndiProtocols</a>,
+ <a href="../manual/configuration.html#allowedLdapClasses">log4j2.allowedLdapClasses</a>, and
+ <a href="../manual/configuration.html#allowedLdapHosts">log4j2.allowedLdapHosts</a> for restrictions on using JNDI
+ with Log4j.</dd>
<dt><a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/async/AsyncLoggerContextSelector.html">AsyncLoggerContextSelector</a></dt>
<dd>Creates a LoggerContext that ensures that all loggers are AsyncLoggers.</dd>
<dt><a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/osgi/BundleContextSelector.html">BundleContextSelector</a></dt>
diff --git a/src/site/xdoc/manual/lookups.xml b/src/site/xdoc/manual/lookups.xml
index d699e78..cc6a66f 100644
--- a/src/site/xdoc/manual/lookups.xml
+++ b/src/site/xdoc/manual/lookups.xml
@@ -270,6 +270,13 @@
The JndiLookup allows variables to be retrieved via JNDI. By default the key will be prefixed with
java:comp/env/, however if the key contains a ":" no prefix will be added.
</p>
+ <p>By default the JDNI Lookup only supports the java, ldap, and ldaps protocols or no protocol. Additional
+ protocols may be supported by specifying them on the <code>log4j2.allowedJndiProtocols</code> property.
+ When using LDAP Java classes that implement the Referenceable interface are not supported for security
+ reasons. Only the Java primative classes are supported by default as well as any classes specified by the
+ <code>log4j2.allowedLdapClasses</code> property. When using LDAP only references to the local host name
+ or ip address are supported along with any hosts or ip addresses listed in the
+ <code>log4j2.allowedLdapHosts</code> property.</p>
<pre class="prettyprint linenums"><![CDATA[
<File name="Application" fileName="application.log">
<PatternLayout>
--
2.27.0