|
17 | 17 |
|
18 | 18 | packageorg.apache.logging.log4j.core.net;
|
19 | 19 |
|
| 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; |
20 | 28 | importjava.util.Properties;
|
21 | 29 | importjava.util.concurrent.TimeUnit;
|
22 | 30 |
|
23 | 31 | importjavax.naming.Context;
|
24 |
| -importjavax.naming.InitialContext; |
| 32 | +importjavax.naming.NamingEnumeration; |
25 | 33 | importjavax.naming.NamingException;
|
| 34 | +importjavax.naming.directory.Attribute; |
| 35 | +importjavax.naming.directory.Attributes; |
| 36 | +importjavax.naming.directory.DirContext; |
| 37 | +importjavax.naming.directory.InitialDirContext; |
26 | 38 |
|
27 | 39 | importorg.apache.logging.log4j.core.appender.AbstractManager;
|
28 | 40 | importorg.apache.logging.log4j.core.appender.ManagerFactory;
|
29 | 41 | importorg.apache.logging.log4j.core.util.JndiCloser;
|
| 42 | +importorg.apache.logging.log4j.core.util.NetUtils; |
| 43 | +importorg.apache.logging.log4j.util.PropertiesUtil; |
30 | 44 |
|
31 | 45 | /**
|
32 |
| - * Manages a JNDI {@link javax.naming.Context}. |
| 46 | + * Manages a JNDI {@link javax.naming.directory.DirContext}. |
33 | 47 | *
|
34 | 48 | * @since 2.1
|
35 | 49 | */
|
36 | 50 | publicclassJndiManagerextendsAbstractManager {
|
37 | 51 |
|
| 52 | +publicstaticfinalStringALLOWED_HOSTS ="allowedLdapHosts"; |
| 53 | +publicstaticfinalStringALLOWED_CLASSES ="allowedLdapClasses"; |
| 54 | +publicstaticfinalStringALLOWED_PROTOCOLS ="allowedJndiProtocols"; |
| 55 | + |
38 | 56 | 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; |
39 | 73 |
|
40 |
| -privatefinalContextcontext; |
| 74 | +privatefinalDirContextcontext; |
41 | 75 |
|
42 |
| -privateJndiManager(finalStringname,finalContextcontext) { |
| 76 | +privateJndiManager(finalStringname,finalDirContextcontext,finalList<String>allowedHosts, |
| 77 | +finalList<String>allowedClasses,finalList<String>allowedProtocols) { |
43 | 78 | super(null,name);
|
44 | 79 | this.context =context;
|
| 80 | +this.allowedHosts =allowedHosts; |
| 81 | +this.allowedClasses =allowedClasses; |
| 82 | +this.allowedProtocols =allowedProtocols; |
45 | 83 | }
|
46 | 84 |
|
47 | 85 | /**
|
@@ -168,21 +206,91 @@ protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
|
168 | 206 | * @throws NamingException if a naming exception is encountered
|
169 | 207 | */
|
170 | 208 | @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 | + } |
172 | 257 | return (T)this.context.lookup(name);
|
173 | 258 | }
|
174 | 259 |
|
175 | 260 | privatestaticclassJndiManagerFactoryimplementsManagerFactory<JndiManager,Properties> {
|
176 | 261 |
|
177 | 262 | @Override
|
178 | 263 | 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); |
179 | 273 | try {
|
180 |
| -returnnewJndiManager(name,newInitialContext(data)); |
| 274 | +returnnewJndiManager(name,newInitialDirContext(data),allowedHosts,allowedClasses, |
| 275 | +allowedProtocols); |
181 | 276 | }catch (finalNamingExceptione) {
|
182 | 277 | LOGGER.error("Error creating JNDI InitialContext.",e);
|
183 | 278 | returnnull;
|
184 | 279 | }
|
185 | 280 | }
|
| 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 | + } |
186 | 294 | }
|
187 | 295 |
|
188 | 296 | @Override
|
|