You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@logging.apache.org by "Uwe Schindler (Jira)" <ji...@apache.org> on 2020/03/25 16:07:00 UTC

[jira] [Comment Edited] (LOG4J2-2761) log4j2 fails when a whitespace is in the file path and Java security manager is used

    [ https://issues.apache.org/jira/browse/LOG4J2-2761?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17066789#comment-17066789 ] 

Uwe Schindler edited comment on LOG4J2-2761 at 3/25/20, 4:06 PM:
-----------------------------------------------------------------

Hi,
this issue hurted us several times with Solr (but also Elasticsearch). See SOLR-14362 for an example.

Apache Lucene/Solr tests run inside a security manager, Apache Solr also and Elasticsearch has a security manager and policy for every plugin. The policy is very restrictive and does not allow any read/write access outside the home folder of Solr/Elasticsearch and while running tests, every test is completely restricted to its working directory.

In most cases you can work around that issue by explicitely passing a -Dlog4j.configurationFile with a real filename (no URI or whatever). But as soon as you have something like a URI from ClassLoader (because by default log4j is searching for the config file in classpath), you are lost:

If the classes (that's usual in tests) are in a file system folder which has a whitespace in the name, log4j does the following:
- It calls {{classloader.getResource("log4j2.xml")}} this returns an URL
- It reads the URL using its InputStream (which is all fine), but for some reason it also calls the method FileUtils#fileFromUri() using a fully valid resource URL.
- The correct way would be to throw away that method and just use {{new File(url.toURI())}}. E.g. see a nice comment in the Maven docu about this: http://maven.apache.org/plugin-developers/common-bugs.html#Converting_between_URLs_and_Filesystem_Paths

Sorry to be a bit harsh here: fileFromUri() looks like a method from the early days of Java and Log4j1, never touched and broken to hell (to hell to hell!!!). It does so many shitty things, which is against all standards, just to try to convert an URL to a File object. I agree that some parts of the code should be OK to do when parsing user-supplied URLs (e.g. if you have just a relative path name or relative file URI), but all this JBOSS and escaping/unescaping to try to do file.exist() is - sorry - not acceptable and breaks too easy.

In short:
- Nuke most of that method and only use it to parse URLs coming from config files. The part with relative paths at begin is OK, but when you detected that it's a valid absolute URL it should not try to be clever - especially it should never do I/O with File.exist()! You need to catch SecurityException(), as calling exists() may not be allowed. But to make it easy, use the "official" way shown below.
- When loading the log4j from the classpath (ConfigurationSource.fromResource) don't call this method at all. You can 100% be sure that the URL is correctly formatted. Maybe just check that it's a "file"-URL.

The only correct way to convert an URL to a file is new File(url.toURI()).

FYI, URL#getFile() is a "forbidden method" in code from the Lucene community. We disallow any class we compile to call it really anywhere! The problem with this method is that it DOES NOT convert an URL to a File, its name is misleading.


was (Author: thetaphi):
Hi,
this issue hurted us several times with Solr (but also Elasticsearch). See SOLR-14362 for an example.

Apache Lucene/Solr tests run inside a security manager, Apache Solr also and Elasticsearch has a security manager and policy for every plugin. The policy is very restrictive and does not allow any read/write access outside the home folder of Solr/Elasticsearch and while running tests, every test is completely restricted to its working directory.

In most cases you can work around that issue by explicitely passing a -Dlog4j.configurationFile with a real filename (no URI or whatever). But as soon as you have something like a URI from ClassLoader (because by default log4j is searching for the config file in classpath, you are lost:

If the classes (that's usual in tests) are in a file system folder which has a whitespace in the name, log4j does the following:
- It calls {{classloader.getResource("log4j2.xml")}} this returns an URL
- It reads the URL using its InputStream (which is all fine), but for some reason it also calls the method FileUtils#fileFromUri() using a fully valid resource URL.
- The correct way would be to throw away that method and just use {{new File(url.toURI())}}. E.g. see a nice comment in the Maven docu about this: http://maven.apache.org/plugin-developers/common-bugs.html#Converting_between_URLs_and_Filesystem_Paths

Sorry to be a bit harsh here: fileFromUri() looks like a method from the early days of Java and Log4j1, never touched and broken to hell (to hell to hell!!!). It does so many shitty things, which is against all standards, just to try to convert an URL to a File object. I agree that some parts of the code should be OK to do when parsing user-supplied URLs (e.g. if you have just a relative path name or relative file URI), but all this JBOSS and escaping/unescaping to try to do file.exist() is - sorry - not acceptable and breaks too easy.

In short:
- Nuke most of that method and only use it to parse URLs coming from config files. The part with relative paths at begin is OK, but when you detected that it's a valid absolute URL it should not try to be clever - especially it should never do I/O with File.exist()! You need to catch SecurityException(), as calling exists() may not be allowed. But to make it easy, use the "official" way shown below.
- When loading the log4j from the classpath (ConfigurationSource.fromResource) don't call this method at all. You can 100% be sure that the URL is correctly formatted. Maybe just check that it's a "file"-URL.

The only correct way to convert an URL to a file is new File(url.toURI()).

FYI, URL#getFile() is a "forbidden method" in code from the Lucene community. We disallow any class we compile to call it really anywhere! The problem with this method is that it DOES NOT convert an URL to a File, its name is misleading.

> log4j2 fails when a whitespace is in the file path and Java security manager is used
> ------------------------------------------------------------------------------------
>
>                 Key: LOG4J2-2761
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-2761
>             Project: Log4j 2
>          Issue Type: Bug
>    Affects Versions: 2.13.0
>         Environment: Windows 7/10, Java 8/11/13 with configured Java Security Manager
>            Reporter: Yury Molchan
>            Priority: Major
>
> {code}
> SEVERE: Error configuring application listener of class [org.yurkom.navigator.web.servlet.StartupListener]
> java.security.AccessControlException: access denied ("java.io.FilePermission" "C:\My%20Space\apache-tomcat-9.0.30\webapps\navigator\WEB-INF\classes\log4j2.properties" "read")
>         at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
>         at java.security.AccessController.checkPermission(AccessController.java:884)
>         at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
>         at java.lang.SecurityManager.checkRead(SecurityManager.java:888)
>         at java.io.File.exists(File.java:814)
>         at org.apache.logging.log4j.core.util.FileUtils.fileFromUri(FileUtils.java:88)
>         at org.apache.logging.log4j.core.config.ConfigurationSource.fromResource(ConfigurationSource.java:360)
>         at org.apache.logging.log4j.core.config.ConfigurationFactory$Factory.getConfiguration(ConfigurationFactory.java:527)
>         at org.apache.logging.log4j.core.config.ConfigurationFactory$Factory.getConfiguration(ConfigurationFactory.java:456)
>         at org.apache.logging.log4j.core.config.ConfigurationFactory.getConfiguration(ConfigurationFactory.java:318)
>         at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:687)
>         at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:708)
>         at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:263)
>         at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:153)
>         at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:45)
>         at org.apache.logging.log4j.LogManager.getContext(LogManager.java:194)
>         at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getContext(AbstractLoggerAdapter.java:138)
> {code}
> policy file contains the following permissions:
> {code}
> grant codeBase "file:${catalina.home}/webapps/navigator/-" {
>         permission java.io.FilePermission "${catalina.home}/-", "read";
>         permission java.io.FilePermission "${catalina.home}/", "read";
> };
> {code}
> where catalina.home is "C:\My Space\apache-tomcat-9.0.30"
> It is related to LOG4J2-466



--
This message was sent by Atlassian Jira
(v8.3.4#803005)