Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc77b3cb

Browse files
authored
Restrict LDAP access via JNDI (#608)
* Restrict LDAP access via JNDI* Disable most JNDI protocols* Rename test. Various minor fixes*LOG4J2-3201 - Limit the protocols JNDI can use by default. Limit the servers and classes that can be accessed via LDAP.
1 parent2315969 commitc77b3cb

File tree

13 files changed

+465
-9
lines changed

13 files changed

+465
-9
lines changed

‎log4j-core/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,11 @@
349349
<artifactId>awaitility</artifactId>
350350
<scope>test</scope>
351351
</dependency>
352+
<dependency>
353+
<groupId>org.zapodot</groupId>
354+
<artifactId>embedded-ldap-junit</artifactId>
355+
<scope>test</scope>
356+
</dependency>
352357
</dependencies>
353358
<build>
354359
<plugins>

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

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ public static class Builder<B extends Builder<B>> extends AbstractAppender.Build
8888
@PluginBuilderAttribute
8989
privatebooleanimmediateFail;
9090

91+
@PluginBuilderAttribute
92+
privateStringallowedLdapClasses;
93+
94+
@PluginBuilderAttribute
95+
privateStringallowedLdapHosts;
96+
97+
@PluginBuilderAttribute
98+
privateStringallowedJndiProtocols;
99+
91100
// Programmatic access only for now.
92101
privateJmsManagerjmsManager;
93102

@@ -100,8 +109,21 @@ public JmsAppender build() {
100109
JmsManageractualJmsManager =jmsManager;
101110
JmsManagerConfigurationconfiguration =null;
102111
if (actualJmsManager ==null) {
112+
PropertiesadditionalProperties =null;
113+
if (allowedLdapClasses !=null ||allowedLdapHosts !=null) {
114+
additionalProperties =newProperties();
115+
if (allowedLdapHosts !=null) {
116+
additionalProperties.put(JndiManager.ALLOWED_HOSTS,allowedLdapHosts);
117+
}
118+
if (allowedLdapClasses !=null) {
119+
additionalProperties.put(JndiManager.ALLOWED_CLASSES,allowedLdapClasses);
120+
}
121+
if (allowedJndiProtocols !=null) {
122+
additionalProperties.put(JndiManager.ALLOWED_PROTOCOLS,allowedJndiProtocols);
123+
}
124+
}
103125
finalPropertiesjndiProperties =JndiManager.createProperties(factoryName,providerUrl,urlPkgPrefixes,
104-
securityPrincipalName,securityCredentials,null);
126+
securityPrincipalName,securityCredentials,additionalProperties);
105127
configuration =newJmsManagerConfiguration(jndiProperties,factoryBindingName,destinationBindingName,
106128
userName,password,false,reconnectIntervalMillis);
107129
actualJmsManager =AbstractManager.getManager(getName(),JmsManager.FACTORY,configuration);
@@ -202,6 +224,21 @@ public Builder setUserName(final String userName) {
202224
returnthis;
203225
}
204226

227+
publicBuildersetAllowedLdapClasses(finalStringallowedLdapClasses) {
228+
this.allowedLdapClasses =allowedLdapClasses;
229+
returnthis;
230+
}
231+
232+
publicBuildersetAllowedLdapHosts(finalStringallowedLdapHosts) {
233+
this.allowedLdapHosts =allowedLdapHosts;
234+
returnthis;
235+
}
236+
237+
publicBuildersetAllowedJndiProtocols(finalStringallowedJndiProtocols) {
238+
this.allowedJndiProtocols =allowedJndiProtocols;
239+
returnthis;
240+
}
241+
205242
/**
206243
* Does not include the password.
207244
*/
@@ -212,7 +249,8 @@ public String toString() {
212249
+", securityCredentials=" +securityCredentials +", factoryBindingName=" +factoryBindingName
213250
+", destinationBindingName=" +destinationBindingName +", username=" +userName +", layout="
214251
+getLayout() +", filter=" +getFilter() +", ignoreExceptions=" +isIgnoreExceptions()
215-
+", jmsManager=" +jmsManager +"]";
252+
+", jmsManager=" +jmsManager +", allowedLdapClasses=" +allowedLdapClasses
253+
+", allowedLdapHosts=" +allowedLdapHosts +", allowedJndiProtocols=" +allowedJndiProtocols +"]";
216254
}
217255

218256
}

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

Lines changed: 114 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,69 @@
1717

1818
packageorg.apache.logging.log4j.core.net;
1919

20+
importjava.net.URI;
21+
importjava.net.URISyntaxException;
22+
importjava.util.ArrayList;
23+
importjava.util.Arrays;
24+
importjava.util.HashMap;
25+
importjava.util.List;
26+
importjava.util.Locale;
27+
importjava.util.Map;
2028
importjava.util.Properties;
2129
importjava.util.concurrent.TimeUnit;
2230

2331
importjavax.naming.Context;
24-
importjavax.naming.InitialContext;
32+
importjavax.naming.NamingEnumeration;
2533
importjavax.naming.NamingException;
34+
importjavax.naming.directory.Attribute;
35+
importjavax.naming.directory.Attributes;
36+
importjavax.naming.directory.DirContext;
37+
importjavax.naming.directory.InitialDirContext;
2638

2739
importorg.apache.logging.log4j.core.appender.AbstractManager;
2840
importorg.apache.logging.log4j.core.appender.ManagerFactory;
2941
importorg.apache.logging.log4j.core.util.JndiCloser;
42+
importorg.apache.logging.log4j.core.util.NetUtils;
43+
importorg.apache.logging.log4j.util.PropertiesUtil;
3044

3145
/**
32-
* Manages a JNDI {@link javax.naming.Context}.
46+
* Manages a JNDI {@link javax.naming.directory.DirContext}.
3347
*
3448
* @since 2.1
3549
*/
3650
publicclassJndiManagerextendsAbstractManager {
3751

52+
publicstaticfinalStringALLOWED_HOSTS ="allowedLdapHosts";
53+
publicstaticfinalStringALLOWED_CLASSES ="allowedLdapClasses";
54+
publicstaticfinalStringALLOWED_PROTOCOLS ="allowedJndiProtocols";
55+
3856
privatestaticfinalJndiManagerFactoryFACTORY =newJndiManagerFactory();
57+
privatestaticfinalStringPREFIX ="log4j2.";
58+
privatestaticfinalStringLDAP ="ldap";
59+
privatestaticfinalStringLDAPS ="ldaps";
60+
privatestaticfinalStringJAVA ="java";
61+
privatestaticfinalList<String>permanentAllowedHosts =NetUtils.getLocalIps();
62+
privatestaticfinalList<String>permanentAllowedClasses =Arrays.asList(Boolean.class.getName(),
63+
Byte.class.getName(),Character.class.getName(),Double.class.getName(),Float.class.getName(),
64+
Integer.class.getName(),Long.class.getName(),Short.class.getName(),String.class.getName());
65+
privatestaticfinalList<String>permanentAllowedProtocols =Arrays.asList(JAVA,LDAP,LDAPS);
66+
privatestaticfinalStringSERIALIZED_DATA ="javaSerializedData";
67+
privatestaticfinalStringCLASS_NAME ="javaClassName";
68+
privatestaticfinalStringREFERENCE_ADDRESS ="javaReferenceAddress";
69+
privatestaticfinalStringOBJECT_FACTORY ="javaFactory";
70+
privatefinalList<String>allowedHosts;
71+
privatefinalList<String>allowedClasses;
72+
privatefinalList<String>allowedProtocols;
3973

40-
privatefinalContextcontext;
74+
privatefinalDirContextcontext;
4175

42-
privateJndiManager(finalStringname,finalContextcontext) {
76+
privateJndiManager(finalStringname,finalDirContextcontext,finalList<String>allowedHosts,
77+
finalList<String>allowedClasses,finalList<String>allowedProtocols) {
4378
super(null,name);
4479
this.context =context;
80+
this.allowedHosts =allowedHosts;
81+
this.allowedClasses =allowedClasses;
82+
this.allowedProtocols =allowedProtocols;
4583
}
4684

4785
/**
@@ -168,21 +206,91 @@ protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
168206
* @throws NamingException if a naming exception is encountered
169207
*/
170208
@SuppressWarnings("unchecked")
171-
public <T>Tlookup(finalStringname)throwsNamingException {
209+
publicsynchronized <T>Tlookup(finalStringname)throwsNamingException {
210+
try {
211+
URIuri =newURI(name);
212+
if (uri.getScheme() !=null) {
213+
if (!allowedProtocols.contains(uri.getScheme().toLowerCase(Locale.ROOT))) {
214+
LOGGER.warn("Log4j JNDI does not allow protocol {}",uri.getScheme());
215+
returnnull;
216+
}
217+
if (LDAP.equalsIgnoreCase(uri.getScheme()) ||LDAPS.equalsIgnoreCase(uri.getScheme())) {
218+
if (!allowedHosts.contains(uri.getHost())) {
219+
LOGGER.warn("Attempt to access ldap server not in allowed list");
220+
returnnull;
221+
}
222+
Attributesattributes =this.context.getAttributes(name);
223+
if (attributes !=null) {
224+
// In testing the "key" for attributes seems to be lowercase while the attribute id is
225+
// camelcase, but that may just be true for the test LDAP used here. This copies the Attributes
226+
// to a Map ignoring the "key" and using the Attribute's id as the key in the Map so it matches
227+
// the Java schema.
228+
Map<String,Attribute>attributeMap =newHashMap<>();
229+
NamingEnumeration<?extendsAttribute>enumeration =attributes.getAll();
230+
while (enumeration.hasMore()) {
231+
Attributeattribute =enumeration.next();
232+
attributeMap.put(attribute.getID(),attribute);
233+
}
234+
AttributeclassNameAttr =attributeMap.get(CLASS_NAME);
235+
if (attributeMap.get(SERIALIZED_DATA) !=null) {
236+
if (classNameAttr !=null) {
237+
StringclassName =classNameAttr.get().toString();
238+
if (!allowedClasses.contains(className)) {
239+
LOGGER.warn("Deserialization of {} is not allowed",className);
240+
returnnull;
241+
}
242+
}else {
243+
LOGGER.warn("No class name provided for {}",name);
244+
returnnull;
245+
}
246+
}elseif (attributeMap.get(REFERENCE_ADDRESS) !=null
247+
||attributeMap.get(OBJECT_FACTORY) !=null) {
248+
LOGGER.warn("Referenceable class is not allowed for {}",name);
249+
returnnull;
250+
}
251+
}
252+
}
253+
}
254+
}catch (URISyntaxExceptionex) {
255+
// This is OK.
256+
}
172257
return (T)this.context.lookup(name);
173258
}
174259

175260
privatestaticclassJndiManagerFactoryimplementsManagerFactory<JndiManager,Properties> {
176261

177262
@Override
178263
publicJndiManagercreateManager(finalStringname,finalPropertiesdata) {
264+
Stringhosts =data !=null ?data.getProperty(ALLOWED_HOSTS) :null;
265+
Stringclasses =data !=null ?data.getProperty(ALLOWED_CLASSES) :null;
266+
Stringprotocols =data !=null ?data.getProperty(ALLOWED_PROTOCOLS) :null;
267+
List<String>allowedHosts =newArrayList<>();
268+
List<String>allowedClasses =newArrayList<>();
269+
List<String>allowedProtocols =newArrayList<>();
270+
addAll(hosts,allowedHosts,permanentAllowedHosts,ALLOWED_HOSTS,data);
271+
addAll(classes,allowedClasses,permanentAllowedClasses,ALLOWED_CLASSES,data);
272+
addAll(protocols,allowedProtocols,permanentAllowedProtocols,ALLOWED_PROTOCOLS,data);
179273
try {
180-
returnnewJndiManager(name,newInitialContext(data));
274+
returnnewJndiManager(name,newInitialDirContext(data),allowedHosts,allowedClasses,
275+
allowedProtocols);
181276
}catch (finalNamingExceptione) {
182277
LOGGER.error("Error creating JNDI InitialContext.",e);
183278
returnnull;
184279
}
185280
}
281+
282+
privatevoidaddAll(StringtoSplit,List<String>list,List<String>permanentList,StringpropertyName,
283+
Propertiesdata) {
284+
if (toSplit !=null) {
285+
list.addAll(Arrays.asList(toSplit.split("\\s*,\\s*")));
286+
data.remove(propertyName);
287+
}
288+
toSplit =PropertiesUtil.getProperties().getStringProperty(PREFIX +propertyName);
289+
if (toSplit !=null) {
290+
list.addAll(Arrays.asList(toSplit.split("\\s*,\\s*")));
291+
}
292+
list.addAll(permanentList);
293+
}
186294
}
187295

188296
@Override

‎log4j-core/src/main/java/org/apache/logging/log4j/core/util/NetUtils.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
packageorg.apache.logging.log4j.core.util;
1818

1919
importjava.io.File;
20+
importjava.net.Inet4Address;
21+
importjava.net.Inet6Address;
2022
importjava.net.InetAddress;
2123
importjava.net.MalformedURLException;
2224
importjava.net.NetworkInterface;
@@ -25,11 +27,14 @@
2527
importjava.net.URISyntaxException;
2628
importjava.net.URL;
2729
importjava.net.UnknownHostException;
30+
importjava.util.ArrayList;
2831
importjava.util.Arrays;
2932
importjava.util.Enumeration;
33+
importjava.util.List;
3034

3135
importorg.apache.logging.log4j.Logger;
3236
importorg.apache.logging.log4j.status.StatusLogger;
37+
importorg.apache.logging.log4j.util.Strings;
3338

3439
/**
3540
* Networking-related convenience methods.
@@ -79,6 +84,49 @@ public static String getLocalHostname() {
7984
}
8085
}
8186

87+
/**
88+
* Returns all the local host names and ip addresses.
89+
* @return The local host names and ip addresses.
90+
*/
91+
publicstaticList<String>getLocalIps() {
92+
List<String>localIps =newArrayList<>();
93+
localIps.add("localhost");
94+
localIps.add("127.0.0.1");
95+
try {
96+
finalInetAddressaddr =Inet4Address.getLocalHost();
97+
setHostName(addr,localIps);
98+
}catch (finalUnknownHostExceptionex) {
99+
// Ignore this.
100+
}
101+
try {
102+
finalEnumeration<NetworkInterface>interfaces =NetworkInterface.getNetworkInterfaces();
103+
if (interfaces !=null) {
104+
while (interfaces.hasMoreElements()) {
105+
finalNetworkInterfacenic =interfaces.nextElement();
106+
finalEnumeration<InetAddress>addresses =nic.getInetAddresses();
107+
while (addresses.hasMoreElements()) {
108+
finalInetAddressaddress =addresses.nextElement();
109+
setHostName(address,localIps);
110+
}
111+
}
112+
}
113+
}catch (finalSocketExceptionse) {
114+
// ignore.
115+
}
116+
returnlocalIps;
117+
}
118+
119+
privatestaticvoidsetHostName(InetAddressaddress,List<String>localIps) {
120+
String[]parts =address.toString().split("\\s*/\\s*");
121+
if (parts.length >0) {
122+
for (Stringpart :parts) {
123+
if (Strings.isNotBlank(part) && !localIps.contains(part)) {
124+
localIps.add(part);
125+
}
126+
}
127+
}
128+
}
129+
82130
/**
83131
* Returns the local network interface's MAC address if possible. The local network interface is defined here as
84132
* the {@link java.net.NetworkInterface} that is both up and not a loopback interface.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
packageorg.apache.logging.log4j.core.lookup;
18+
19+
importjavax.naming.Context;
20+
importjavax.naming.Name;
21+
importjavax.naming.spi.ObjectFactory;
22+
importjava.util.Hashtable;
23+
24+
importstaticorg.junit.jupiter.api.Assertions.fail;
25+
26+
/**
27+
* Test LDAP object
28+
*/
29+
publicclassJndiExploitimplementsObjectFactory {
30+
@Override
31+
publicObjectgetObjectInstance(Objectobj,Namename,ContextnameCtx,Hashtable<?, ?>environment)
32+
throwsException {
33+
fail("getObjectInstance must not be allowed");
34+
returnnull;
35+
}
36+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp