Skip to content

Commit f6564bb

Browse files
committed
LOG4J2-3242 - Limit JNDI to the java protocol
1 parent ce6b78d commit f6564bb

File tree

16 files changed

+268
-18
lines changed

16 files changed

+268
-18
lines changed

log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ protected AbstractManager(final String name) {
6363
*/
6464
public static <M extends AbstractManager, T> M getManager(final String name, final ManagerFactory<M, T> factory,
6565
final T data) {
66+
if (factory == null) {
67+
throw new IllegalArgumentException("factory cannot be null");
68+
}
6669
LOCK.lock();
6770
try {
6871
@SuppressWarnings("unchecked")

log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,17 @@ private static class JmsManagerFactory implements ManagerFactory<JmsManager, Jms
156156

157157
@Override
158158
public JmsManager createManager(final String name, final JmsConfiguration data) {
159-
try {
160-
return new JmsManager(name, data.jndiManager, data.connectionFactoryName, data.destinationName,
161-
data.username, data.password);
162-
} catch (final Exception e) {
163-
LOGGER.error("Error creating JmsManager using ConnectionFactory [{}] and Destination [{}].",
164-
data.connectionFactoryName, data.destinationName, e);
159+
if (JndiManager.isJndiJmsEnabled()) {
160+
try {
161+
return new JmsManager(name, data.jndiManager, data.connectionFactoryName, data.destinationName,
162+
data.username, data.password);
163+
} catch (final Exception e) {
164+
LOGGER.error("Error creating JmsManager using ConnectionFactory [{}] and Destination [{}].",
165+
data.connectionFactoryName, data.destinationName, e);
166+
return null;
167+
}
168+
} else {
169+
LOGGER.error("JNDI must be enabled by setting log4j2.enableJndiJms=true");
165170
return null;
166171
}
167172
}

log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.logging.log4j.core.LogEvent;
2525
import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
2626
import org.apache.logging.log4j.core.config.plugins.util.PluginType;
27+
import org.apache.logging.log4j.core.net.JndiManager;
2728
import org.apache.logging.log4j.core.util.Loader;
2829
import org.apache.logging.log4j.core.util.ReflectionUtil;
2930
import org.apache.logging.log4j.status.StatusLogger;
@@ -62,7 +63,10 @@ public Interpolator(final StrLookup defaultLookup, final List<String> pluginPack
6263
for (final Map.Entry<String, PluginType<?>> entry : plugins.entrySet()) {
6364
try {
6465
final Class<? extends StrLookup> clazz = entry.getValue().getPluginClass().asSubclass(StrLookup.class);
65-
lookups.put(entry.getKey(), ReflectionUtil.instantiate(clazz));
66+
if (!clazz.getName().equals("org.apache.logging.log4j.core.lookup.JndiLookup")
67+
|| JndiManager.isJndiLookupEnabled()) {
68+
lookups.put(entry.getKey().toLowerCase(), ReflectionUtil.instantiate(clazz));
69+
}
6670
} catch (final Exception ex) {
6771
LOGGER.error("Unable to create Lookup for {}", entry.getKey(), ex);
6872
}
@@ -87,15 +91,16 @@ public Interpolator(final Map<String, String> properties) {
8791
lookups.put("main", MapLookup.MAIN_SINGLETON);
8892
lookups.put("java", new JavaLookup());
8993
// JNDI
90-
try {
91-
// [LOG4J2-703] We might be on Android
92-
lookups.put("jndi",
93-
Loader.newCheckedInstanceOf("org.apache.logging.log4j.core.lookup.JndiLookup", StrLookup.class));
94-
} catch (final Throwable e) {
95-
// java.lang.VerifyError: org/apache/logging/log4j/core/lookup/JndiLookup
96-
LOGGER.warn(
94+
if (JndiManager.isJndiLookupEnabled()) {
95+
try {
96+
// [LOG4J2-703] We might be on Android
97+
lookups.put("jndi", Loader.newCheckedInstanceOf("org.apache.logging.log4j.core.lookup.JndiLookup", StrLookup.class));
98+
} catch (final Throwable e) {
99+
// java.lang.VerifyError: org/apache/logging/log4j/core/lookup/JndiLookup
100+
LOGGER.warn(
97101
"JNDI lookup class is not available because this JRE does not support JNDI. JNDI string lookups will not be available, continuing configuration.",
98102
e);
103+
}
99104
}
100105
// JMX input args
101106
try {

log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ public class JndiLookup extends AbstractLookup {
3838
/** JNDI resource path prefix used in a J2EE container */
3939
static final String CONTAINER_JNDI_RESOURCE_PATH_PREFIX = "java:comp/env/";
4040

41+
/**
42+
* Constructs a new instance or throw IllegalStateException if this feature is disabled.
43+
*/
44+
public JndiLookup() {
45+
if (!JndiManager.isJndiLookupEnabled()) {
46+
throw new IllegalStateException("JNDI must be enabled by setting log4j2.enableJndiLookup=true");
47+
}
48+
}
49+
4150
/**
4251
* Looks up the value of the JNDI resource.
4352
* @param event The current LogEvent (is ignored by this StrLookup).

log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package org.apache.logging.log4j.core.net;
1919

20+
import java.net.URI;
21+
import java.net.URISyntaxException;
2022
import java.util.Properties;
2123

2224
import javax.naming.Context;
@@ -26,6 +28,7 @@
2628
import org.apache.logging.log4j.core.appender.AbstractManager;
2729
import org.apache.logging.log4j.core.appender.ManagerFactory;
2830
import org.apache.logging.log4j.core.util.JndiCloser;
31+
import org.apache.logging.log4j.util.PropertiesUtil;
2932

3033
/**
3134
* JNDI {@link javax.naming.Context} manager.
@@ -35,9 +38,31 @@
3538
public class JndiManager extends AbstractManager {
3639

3740
private static final JndiManagerFactory FACTORY = new JndiManagerFactory();
41+
private static final String PREFIX = "log4j2.enableJndi";
42+
private static final String JAVA_SCHEME = "java";
3843

3944
private final Context context;
4045

46+
private static boolean isJndiEnabled(final String subKey) {
47+
return PropertiesUtil.getProperties().getBooleanProperty(PREFIX + subKey, false);
48+
}
49+
50+
public static boolean isJndiEnabled() {
51+
return isJndiContextSelectorEnabled() || isJndiJmsEnabled() || isJndiLookupEnabled();
52+
}
53+
54+
public static boolean isJndiContextSelectorEnabled() {
55+
return isJndiEnabled("ContextSelector");
56+
}
57+
58+
public static boolean isJndiJmsEnabled() {
59+
return isJndiEnabled("Jms");
60+
}
61+
62+
public static boolean isJndiLookupEnabled() {
63+
return isJndiEnabled("Lookup");
64+
}
65+
4166
private JndiManager(final String name, final Context context) {
4267
super(name);
4368
this.context = context;
@@ -125,13 +150,28 @@ protected void releaseSub() {
125150
*/
126151
@SuppressWarnings("unchecked")
127152
public <T> T lookup(final String name) throws NamingException {
128-
return (T) this.context.lookup(name);
153+
if (context == null) {
154+
return null;
155+
}
156+
try {
157+
URI uri = new URI(name);
158+
if (uri.getScheme() == null || uri.getScheme().equals(JAVA_SCHEME)) {
159+
return (T) this.context.lookup(name);
160+
}
161+
LOGGER.warn("Unsupported JNDI URI - {}", name);
162+
} catch (URISyntaxException ex) {
163+
LOGGER.warn("Invalid JNDI URI - {}", name);
164+
}
165+
return null;
129166
}
130167

131168
private static class JndiManagerFactory implements ManagerFactory<JndiManager, Properties> {
132169

133170
@Override
134171
public JndiManager createManager(final String name, final Properties data) {
172+
if (!isJndiEnabled()) {
173+
throw new IllegalStateException(String.format("JNDI must be enabled by setting one of the %s* properties to true", PREFIX));
174+
}
135175
try {
136176
return new JndiManager(name, new InitialContext(data));
137177
} catch (final NamingException e) {

log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ public class JndiContextSelector implements NamedContextSelector {
9292

9393
private static final StatusLogger LOGGER = StatusLogger.getLogger();
9494

95+
public JndiContextSelector() {
96+
if (!JndiManager.isJndiContextSelectorEnabled()) {
97+
throw new IllegalStateException("JNDI must be enabled by setting log4j2.enableJndiContextSelector=true");
98+
}
99+
}
100+
95101
@Override
96102
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
97103
return getContext(fqcn, loader, currentContext, null);

log4j-core/src/test/java/org/apache/logging/log4j/core/appender/mom/JmsAppenderTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public class JmsAppenderTest {
5959

6060
@BeforeClass
6161
public static void setUpClass() throws Exception {
62+
System.setProperty("log4j2.enableJndiJms", "true");
6263
MockContextFactory.setAsInitial();
6364
context = new InitialContext();
6465
context.rebind(CONNECTION_FACTORY_NAME, new QueueConnectionFactoryImpl());

log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.logging.log4j.test.appender.ListAppender;
2929
import org.junit.After;
3030
import org.junit.Before;
31+
import org.junit.BeforeClass;
3132
import org.junit.Rule;
3233
import org.junit.Test;
3334
import org.mockejb.jndi.MockContextFactory;
@@ -46,6 +47,11 @@ public class RoutingAppenderWithJndiTest {
4647
@Rule
4748
public InitialLoggerContext init = new InitialLoggerContext("log4j-routing-by-jndi.xml");
4849

50+
@BeforeClass
51+
public static void beforeClass() {
52+
System.setProperty("log4j2.enableJndiLookup", "true");
53+
}
54+
4955
@Before
5056
public void before() throws NamingException {
5157
MockContextFactory.setAsInitial();

log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class InterpolatorTest {
4949
@BeforeClass
5050
public static void before() throws NamingException {
5151
System.setProperty(TESTKEY, TESTVAL);
52-
52+
System.setProperty("log4j2.enableJndiLookup", "true");
5353
MockContextFactory.setAsInitial();
5454
context = new InitialContext();
5555
context.bind(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME);
@@ -62,7 +62,7 @@ public static void after() {
6262
} catch (final NamingException ignored) {
6363
}
6464
MockContextFactory.revertSetAsInitial();
65-
65+
System.clearProperty("log4j2.enableJndiLookup");
6666
System.clearProperty(TESTKEY);
6767
}
6868

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache license, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the license for the specific language governing permissions and
15+
* limitations under the license.
16+
*/
17+
package org.apache.logging.log4j.core.lookup;
18+
19+
import javax.naming.Context;
20+
import javax.naming.InitialContext;
21+
import javax.naming.NamingException;
22+
23+
import org.junit.After;
24+
import org.junit.AfterClass;
25+
import org.junit.Before;
26+
import org.junit.BeforeClass;
27+
import org.junit.Test;
28+
import org.mockejb.jndi.MockContextFactory;
29+
30+
import static org.junit.Assert.assertEquals;
31+
import static org.junit.Assert.assertNull;
32+
33+
/**
34+
* JndiLookupTest
35+
*/
36+
public class JndiDisabledLookupTest {
37+
38+
private static final String TEST_CONTEXT_RESOURCE_NAME = "logging/context-name";
39+
private static final String TEST_CONTEXT_NAME = "app-1";
40+
41+
private Context context;
42+
43+
@Before
44+
public void before() throws NamingException {
45+
MockContextFactory.setAsInitial();
46+
context = new InitialContext();
47+
context.bind(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME);
48+
}
49+
50+
@After
51+
public void after() {
52+
try {
53+
context.close();
54+
} catch (final NamingException ignored) {
55+
}
56+
MockContextFactory.revertSetAsInitial();
57+
}
58+
59+
@Test(expected = IllegalStateException.class)
60+
public void testLookup() {
61+
final StrLookup lookup = new JndiLookup();
62+
}
63+
}

0 commit comments

Comments
 (0)