From c362aff473e9812798ff8f25f30a2619996605d5 Mon Sep 17 00:00:00 2001
From: Ralph Goers <rgoers@apache.org>
Date: Sat, 11 Dec 2021 16:05:14 -0700
Subject: [PATCH] LOG4J2-3208 - Disable JNDI by default

---
 .../log4j/core/appender/mom/JmsManager.java   | 13 ++--
 .../log4j/core/lookup/Interpolator.java       | 19 ++++--
 .../logging/log4j/core/net/JndiManager.java   | 54 +++++++++++-----
 .../core/selector/JndiContextSelector.java    |  6 ++
 .../core/appender/mom/JmsAppenderTest.java    |  6 ++
 .../routing/RoutingAppenderWithJndiTest.java  | 10 ++-
 .../log4j/core/lookup/InterpolatorTest.java   |  3 +
 .../core/lookup/JndiDisabledLookupTest.java   | 64 +++++++++++++++++++
 .../log4j/core/lookup/JndiLookupTest.java     |  6 ++
 .../core/lookup/JndiRestrictedLookupTest.java |  1 +
 src/changes/changes.xml                       |  3 +
 src/site/markdown/index.md.vm                 |  2 +-
 src/site/markdown/security.md                 |  2 +-
 src/site/xdoc/manual/appenders.xml            |  3 +
 src/site/xdoc/manual/configuration.xml.vm     |  8 +++
 src/site/xdoc/manual/logsep.xml               |  3 +
 src/site/xdoc/manual/lookups.xml              |  5 ++
 17 files changed, 177 insertions(+), 31 deletions(-)
 create mode 100644 log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiDisabledLookupTest.java

Index: apache-log4j-2.13.0-src/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
===================================================================
--- apache-log4j-2.13.0-src.orig/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java	2021-12-14 18:39:45.617280560 +0100
@@ -125,12 +125,17 @@ public class JmsManager extends Abstract
 
         @Override
         public JmsManager createManager(final String name, final JmsManagerConfiguration data) {
+            if (JndiManager.isIsJndiEnabled()) {
             try {
                 return new JmsManager(name, data);
             } catch (final Exception e) {
                 logger().error("Error creating JmsManager using JmsManagerConfiguration [{}]", data, e);
                 return null;
             }
+            } else {
+                logger().error("JNDI has not been enabled. The log4j2.enableJndi property must be set to true");
+                return null;
+            }
         }
     }
 
Index: apache-log4j-2.13.0-src/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
===================================================================
--- apache-log4j-2.13.0-src.orig/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java	2021-12-14 18:39:45.617280560 +0100
@@ -26,6 +26,7 @@ import org.apache.logging.log4j.core.Log
 import org.apache.logging.log4j.core.config.ConfigurationAware;
 import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
 import org.apache.logging.log4j.core.config.plugins.util.PluginType;
+import org.apache.logging.log4j.core.net.JndiManager;
 import org.apache.logging.log4j.core.util.Loader;
 import org.apache.logging.log4j.core.util.ReflectionUtil;
 import org.apache.logging.log4j.status.StatusLogger;
@@ -77,7 +78,9 @@ public class Interpolator extends Abstra
         for (final Map.Entry<String, PluginType<?>> entry : plugins.entrySet()) {
             try {
                 final Class<? extends StrLookup> clazz = entry.getValue().getPluginClass().asSubclass(StrLookup.class);
+                if (!clazz.getName().equals(JndiLookup.class.getName()) || JndiManager.isIsJndiEnabled()) {
                 strLookupMap.put(entry.getKey().toLowerCase(), ReflectionUtil.instantiate(clazz));
+                }
             } catch (final Throwable t) {
                 handleError(entry.getKey(), t);
             }
@@ -106,6 +109,7 @@ public class Interpolator extends Abstra
         strLookupMap.put("lower", new LowerLookup());
         strLookupMap.put("upper", new UpperLookup());
         // JNDI
+        if (JndiManager.isIsJndiEnabled()) {
         try {
             // [LOG4J2-703] We might be on Android
             strLookupMap.put(LOOKUP_KEY_JNDI,
@@ -113,6 +117,7 @@ public class Interpolator extends Abstra
         } catch (final LinkageError | Exception e) {
             handleError(LOOKUP_KEY_JNDI, e);
         }
+        }
         // JMX input args
         try {
             // We might be on Android
Index: apache-log4j-2.13.0-src/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
===================================================================
--- apache-log4j-2.13.0-src.orig/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java	2021-12-14 18:39:45.617280560 +0100
@@ -73,6 +73,10 @@ public class JndiManager extends Abstrac
 
     private final DirContext context;
 
+    public static boolean isIsJndiEnabled() {
+        return PropertiesUtil.getProperties().getBooleanProperty("log4j2.enableJndi", false);
+    }
+
     private JndiManager(final String name, final DirContext context, final List<String> allowedHosts,
             final List<String> allowedClasses, final List<String> allowedProtocols) {
         super(null, name);
@@ -82,6 +86,14 @@ public class JndiManager extends Abstrac
         this.allowedProtocols = allowedProtocols;
     }
 
+    private JndiManager(final String name) {
+        super(null, name);
+        this.context = null;
+        this.allowedProtocols = null;
+        this.allowedClasses = null;
+        this.allowedHosts = null;
+    }
+
     /**
      * Gets the default JndiManager using the default {@link javax.naming.InitialContext}.
      *
@@ -194,8 +206,11 @@ public class JndiManager extends Abstrac
 
     @Override
     protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
+        if (context != null) {
         return JndiCloser.closeSilently(this.context);
     }
+        return true;
+    }
 
     /**
      * Looks up a named object through this JNDI context.
@@ -207,6 +222,9 @@ public class JndiManager extends Abstrac
      */
     @SuppressWarnings("unchecked")
     public synchronized <T> T lookup(final String name) throws NamingException {
+        if (context == null) {
+            return null;
+        }
         try {
             URI uri = new URI(name);
             if (uri.getScheme() != null) {
@@ -261,6 +279,7 @@ public class JndiManager extends Abstrac
 
         @Override
         public JndiManager createManager(final String name, final Properties data) {
+            if (isIsJndiEnabled()) {
             String hosts = data != null ? data.getProperty(ALLOWED_HOSTS) : null;
             String classes = data != null ? data.getProperty(ALLOWED_CLASSES) : null;
             String protocols = data != null ? data.getProperty(ALLOWED_PROTOCOLS) : null;
@@ -277,6 +296,9 @@ public class JndiManager extends Abstrac
                 LOGGER.error("Error creating JNDI InitialContext.", e);
                 return null;
             }
+            } else {
+                return new JndiManager(name);
+            }
         }
 
         private void addAll(String toSplit, List<String> list, List<String> permanentList, String propertyName,
Index: apache-log4j-2.13.0-src/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
===================================================================
--- apache-log4j-2.13.0-src.orig/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java	2021-12-14 18:39:45.617280560 +0100
@@ -93,6 +93,12 @@ public class JndiContextSelector impleme
 
     private static final StatusLogger LOGGER = StatusLogger.getLogger();
 
+    public JndiContextSelector() {
+        if (!JndiManager.isIsJndiEnabled()) {
+            throw new IllegalStateException("JNDI must be enabled by setting log4j2.enableJndi=true");
+        }
+    }
+
     @Override
     public void shutdown(String fqcn, ClassLoader loader, boolean currentContext, boolean allContexts) {
         LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
Index: apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/mom/JmsAppenderTest.java
===================================================================
--- apache-log4j-2.13.0-src.orig/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/mom/JmsAppenderTest.java	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/mom/JmsAppenderTest.java	2021-12-14 18:39:45.617280560 +0100
@@ -49,6 +49,7 @@ import org.apache.logging.log4j.message.
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.message.StringMapMessage;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -83,6 +84,11 @@ public class JmsAppenderTest {
     @Rule
     public RuleChain rules = RuleChain.outerRule(jndiRule).around(ctx);
 
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        System.setProperty("log4j2.enableJndi", "true");
+    }
+
     public JmsAppenderTest() throws Exception {
         // this needs to set up before LoggerContextRule
         given(connectionFactory.createConnection()).willReturn(connection);
Index: apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
===================================================================
--- apache-log4j-2.13.0-src.orig/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java	2021-12-14 18:39:45.617280560 +0100
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.ap
 
 import java.io.File;
 import java.util.Collections;
+import java.util.Map;
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
@@ -29,6 +30,7 @@ import org.apache.logging.log4j.message.
 import org.apache.logging.log4j.test.appender.ListAppender;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.rules.RuleChain;
@@ -47,8 +49,12 @@ public class RoutingAppenderWithJndiTest
     public static LoggerContextRule loggerContextRule = new LoggerContextRule("log4j-routing-by-jndi.xml");
 
     @ClassRule
-    public static RuleChain rules = RuleChain.outerRule(new JndiRule(Collections.<String, Object>emptyMap()))
-        .around(loggerContextRule);
+    public static RuleChain rules = RuleChain.outerRule(new JndiRule(initBindings())).around(loggerContextRule);
+
+    private static Map<String, Object> initBindings() {
+        System.setProperty("log4j2.enableJndi", "true");
+        return Collections.emptyMap();
+    }
 
     @Before
     public void before() throws NamingException {
Index: apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java
===================================================================
--- apache-log4j-2.13.0-src.orig/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java	2021-12-14 18:39:45.617280560 +0100
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.junit.JndiRule;
+import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.rules.ExternalResource;
@@ -48,12 +49,14 @@ public class InterpolatorTest {
         protected void before() throws Throwable {
             System.setProperty(TESTKEY, TESTVAL);
             System.setProperty(TESTKEY2, TESTVAL);
+            System.setProperty("log4j2.enableJndi", "true");
         }
 
         @Override
         protected void after() {
             System.clearProperty(TESTKEY);
             System.clearProperty(TESTKEY2);
+            System.clearProperty("log4j2.enableJndi");
         }
     }).around(new JndiRule(
         JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME));
Index: apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiDisabledLookupTest.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiDisabledLookupTest.java	2021-12-14 18:39:45.617280560 +0100
@@ -0,0 +1,64 @@
+/*
+ * 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.lookup;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.junit.JndiRule;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * JndiDisabledLookupTest
+ *
+ * Verifies the Lookups are disabled without the log4j2.enableJndi property set to true.
+ */
+public class JndiDisabledLookupTest {
+
+    private static final String TEST_CONTEXT_RESOURCE_NAME = "logging/context-name";
+    private static final String TEST_CONTEXT_NAME = "app-1";
+    private static final String TEST_INTEGRAL_NAME = "int-value";
+    private static final int TEST_INTEGRAL_VALUE = 42;
+    private static final String TEST_STRINGS_NAME = "string-collection";
+    private static final Collection<String> TEST_STRINGS_COLLECTION = Arrays.asList("one", "two", "three");
+
+    @Rule
+    public JndiRule jndiRule = new JndiRule(createBindings());
+
+    private Map<String, Object> createBindings() {
+        final Map<String, Object> map = new HashMap<>();
+        map.put(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME);
+        map.put(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_INTEGRAL_NAME, TEST_INTEGRAL_VALUE);
+        map.put(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_STRINGS_NAME, TEST_STRINGS_COLLECTION);
+        return map;
+    }
+
+    @Test
+    public void testLookup() {
+        final StrLookup lookup = new JndiLookup();
+
+        String contextName = lookup.lookup(TEST_CONTEXT_RESOURCE_NAME);
+        assertNull(contextName);
+    }
+}
Index: apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java
===================================================================
--- apache-log4j-2.13.0-src.orig/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java	2021-12-14 18:39:45.617280560 +0100
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.logging.log4j.junit.JndiRule;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -42,6 +43,11 @@ public class JndiLookupTest {
     @Rule
     public JndiRule jndiRule = new JndiRule(createBindings());
 
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty("log4j2.enableJndi", "true");
+    }
+
     private Map<String, Object> createBindings() {
         final Map<String, Object> map = new HashMap<>();
         map.put(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME);
Index: apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.java
===================================================================
--- apache-log4j-2.13.0-src.orig/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.java	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.java	2021-12-14 18:39:45.617280560 +0100
@@ -54,6 +54,7 @@ public class JndiRestrictedLookupTest {
     public static void beforeClass() {
         System.setProperty("log4j2.allowedLdapClasses", Level.class.getName());
         System.setProperty("log4j2.allowedJndiProtocols", "dns");
+        System.setProperty("log4j2.enableJndi", "true");
     }
 
     @Test
Index: apache-log4j-2.13.0-src/src/site/xdoc/manual/appenders.xml
===================================================================
--- apache-log4j-2.13.0-src.orig/src/site/xdoc/manual/appenders.xml	2021-12-14 18:39:38.369421272 +0100
+++ apache-log4j-2.13.0-src/src/site/xdoc/manual/appenders.xml	2021-12-14 18:39:45.621280482 +0100
@@ -1527,6 +1527,9 @@ public class ConnectionFactory {
         <a name="JMSTopicAppender"/>
         <subsection name="JMS Appender">
           <p>The JMS Appender sends the formatted log event to a JMS Destination.</p>
+          <p>The JMS Appender requires JNDI support so as of release 2.15.1 this appender will not function unless
+            <code>log4j2.enableJndi=true</code>log4j2.enableJndi=true is configured as a system property or environment
+            variable. See the <a href="./configuration.html#enableJndi">enableJndi</a> system property.</p>
           <p>
             Note that in Log4j 2.0, this appender was split into a JMSQueueAppender and a JMSTopicAppender. Starting
             in Log4j 2.1, these appenders were combined into the JMS Appender which makes no distinction between queues
Index: apache-log4j-2.13.0-src/src/site/xdoc/manual/configuration.xml.vm
===================================================================
--- apache-log4j-2.13.0-src.orig/src/site/xdoc/manual/configuration.xml.vm	2021-12-14 18:39:38.373421194 +0100
+++ apache-log4j-2.13.0-src/src/site/xdoc/manual/configuration.xml.vm	2021-12-14 18:39:45.621280482 +0100
@@ -1938,6 +1938,14 @@ public class AwesomeTest {
     </td>
   </tr>
   <tr>
+    <td><a name="enableJndi"/>log4j2.enableJndi</td>
+    <td>LOG4J_ENABLE_JNDI</td>
+    <td>false</td>
+    <td>
+      When true, Log4j components that use JNDI are enabled. When false, the default, they are disabled.
+    </td>
+  </tr>
+  <tr>
     <td><a name="allowedLdapClasses"/>log4j2.allowedLdapClasses</td>
     <td>LOG4J_ALLOWED_LDAP_CLASSES</td>
     <td>&nbsp;</td>
Index: apache-log4j-2.13.0-src/src/site/xdoc/manual/logsep.xml
===================================================================
--- apache-log4j-2.13.0-src.orig/src/site/xdoc/manual/logsep.xml	2021-12-14 18:39:38.373421194 +0100
+++ apache-log4j-2.13.0-src/src/site/xdoc/manual/logsep.xml	2021-12-14 18:39:45.621280482 +0100
@@ -111,6 +111,9 @@
                   to use JNDI to locate each web application's <code>LoggerContext</code>. Be sure to set the
                   <code>isLog4jContextSelectorNamed</code> context parameter to <kbd>true</kbd> and also set the
                   <code>log4jContextName</code> and <code>log4jConfiguration</code> context parameters.
+                  Note that the JndiContextSelector will not work unless <code>log4j2.enableJndi=true</code> is set as a
+                  system property or environment variable. See the
+                  <a href="./configuration.html#enableJndi">enableJndi</a> system property.
                 </li>
               </ol>
             <p>
Index: apache-log4j-2.13.0-src/src/site/xdoc/manual/lookups.xml
===================================================================
--- apache-log4j-2.13.0-src.orig/src/site/xdoc/manual/lookups.xml	2021-12-14 18:39:38.373421194 +0100
+++ apache-log4j-2.13.0-src/src/site/xdoc/manual/lookups.xml	2021-12-14 18:39:45.621280482 +0100
@@ -184,6 +184,11 @@
         <a name="JndiLookup"/>
         <subsection name="Jndi Lookup">
           <p>
+            As of Log4j 2.15.1 JNDI operations require that <code>log4j2.enableJndi=true</code> be set as a system
+            property or the corresponding environment variable for this lookup to function. See the
+            <a href="./configuration.html#enableJndi">enableJndi</a> system property.
+          </p>
+          <p>
             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>
