Bug 64488

Summary: EL API: AccessControlException -- Import Handler
Product: Tomcat 10 Reporter: Volodymyr Siedlecki <volosied>
Component: ELAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: normal    
Priority: P2    
Version: 10.0.0-M5   
Target Milestone: ------   
Hardware: Macintosh   
OS: Mac OS X 10.1   
Attachments: Patch

Description Volodymyr Siedlecki 2020-06-01 21:32:38 UTC
Created attachment 37286 [details]
Patch

Hello,

I encountered an AccessControlException when using the Tomcat 10.0.0-M5 EL API in Open Liberty. 

The stack trace is provided below, but the exception is thrown starting on this line: jakarta.el.ImportHandler.findClass(ImportHandler.java:455)

I would appreciate if someone look whether a security check should be added in the code. It appears to be a valid scenario. I've added a patch for reference (based off code from ExpressionFactory.java). 

We also used the same Tomcat 10.0.0-M5 Jasper EL Implementation.

The application was run on the following JDK: 

openjdk version "1.8.0_222"
OpenJDK Runtime Environment (build 1.8.0_222-b10)
Eclipse OpenJ9 VM (build openj9-0.15.1, JRE 1.8.0 Mac OS X amd64-64-Bit Compressed References 20190717_298 (JIT enabled, AOT enabled)
OpenJ9   - 0f66c6431
OMR      - ec782f26
JCL      - f147086df1 based on jdk8u222-b10)

Please let me know if you have any questions. Thank you. 
_________________________________________

Permission: 
("java.io.FilePermission" "/Library/Java/JavaVirtualMachines/adoptopenjdk-8-openj9.jdk/Contents/Home/jre/lib/rt.jar" "read")
Stack: 
java.security.AccessControlException: Access denied ("java.io.FilePermission" "/Library/Java/JavaVirtualMachines/adoptopenjdk-8-openj9.jdk/Contents/Home/jre/lib/rt.jar" "read")java.security.AccessController.throwACE(AccessController.java:176)
java.security.AccessController.checkPermissionHelper(AccessController.java:238)
java.security.AccessController.checkPermission(AccessController.java:385)
java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
com.ibm.ws.kernel.launch.internal.MissingDoPrivDetectionSecurityManager.checkPermission(MissingDoPrivDetectionSecurityManager.java:45)
com.ibm.oti.vm.AbstractClassLoader.findResource(AbstractClassLoader.java:194)
java.lang.ClassLoader.getResource(ClassLoader.java:584)
java.lang.ClassLoader.getResource(ClassLoader.java:586)
java.lang.ClassLoader.getResource(ClassLoader.java:586)
com.ibm.ws.kernel.internal.classloader.BootstrapChildFirstJarClassloader.getResource(BootstrapChildFirstJarClassloader.java:110)
org.eclipse.osgi.internal.loader.BundleLoader.findResource(BundleLoader.java:621)
org.eclipse.osgi.internal.loader.ModuleClassLoader.getResource(ModuleClassLoader.java:216)
com.ibm.ws.classloading.internal.GatewayClassLoader.findResource(GatewayClassLoader.java:134)
com.ibm.ws.classloading.internal.GatewayClassLoader.getResource(GatewayClassLoader.java:116)
java.lang.ClassLoader.getResource(ClassLoader.java:586)
jakarta.el.ImportHandler.findClass(ImportHandler.java:455)
jakarta.el.ImportHandler.resolveClass(ImportHandler.java:417)
jakarta.servlet.jsp.el.ScopedAttributeELResolver.getValue(ScopedAttributeELResolver.java:93)
org.apache.jasper.el.JasperELResolver.getValue(JasperELResolver.java:110)
org.apache.el.parser.AstIdentifier.getValue(AstIdentifier.java:94)
org.apache.el.parser.AstValue.getValue(AstValue.java:137)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:190)
org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:794)
com.ibm._jsp._EL30StaticFieldsAndMethodsTests._jspService(_EL30StaticFieldsAndMethodsTests.java:109)
com.ibm.ws.jsp.runtime.HttpJspBase.service(HttpJspBase.java:100)
Comment 1 Mark Thomas 2020-06-02 07:55:33 UTC
The ImportHandler code should not be made privileged.

You need to grant the necessary permissions to whatever code calls ImportHandler.

In a default Tomcat installation, the necessary permission should be granted in the catalina.policy file.

It is possible, but unlikely, that a privileged block is missing elsewhere. If you can provide the simplest possible JSP that triggers this issue on a clean Tomcat 10 install we can take a look.
Comment 2 Konstantin Kolinko 2020-06-02 15:11:01 UTC
(In reply to volosied+apache from comment #0)
> Permission: 
> ("java.io.FilePermission"
> "/Library/Java/JavaVirtualMachines/adoptopenjdk-8-openj9.jdk/Contents/Home/
> jre/lib/rt.jar" "read")

How does it happen that it does not have a read permission for "rt.jar"?

In your case (looking at the proposed patch - attachment 37286 [details]) it is a getResource() call that is blocked by lacking permissions. Does it mean that not only loading of resources, but loading classes from rt.jar is blocked as well? Why? For what purpose? (*)

Is it a real-world configuration? Why is it configured like that?


(*) E.g. looking a 'loadClass(name)' call a few lines later just below the code affected by the patch - at ImportHandler line 463. - Will it fail?


(In reply to Mark Thomas from comment #1)
> If you can provide the simplest possible JSP that triggers this issue on a
> clean Tomcat 10 install we can take a look.

+1

I would like to see steps and code that are sufficient to reproduce the behaviour.

(From your stack trace I guess that you are running a JSP page, but not from within Apache Tomcat.)
Comment 3 Konstantin Kolinko 2020-06-02 15:24:19 UTC
(In reply to volosied+apache from comment #0)

A pair of minor comments regarding the patch

> +  @Override
> +  public Boolean run() {
> +      return cl.getResource(path) == null;
> +  }

The code fragment above uses autoboxing. The code style in Tomcat is to use explicit boxing.

(Configuration of compiler warnings for Eclipse IDE is documented in /res/ide-support/eclipse/java-compiler-errors-warnings.txt)

> From:

If that was not intended, you may want to configure user.email setting in your clone of the repository.
Comment 4 Volodymyr Siedlecki 2020-06-02 21:40:40 UTC
Hello,

Thank you so much for the quick replies. I looked more into my issue, and I have a better idea of what’s occurring.  I do not believe anything is wrong with the ImportHandler after all. 

In my  Open Liberty build, I had a development security property enabled,  unknown to me,  that logs AccessControl exceptions and allows the application to continue.  When I removed that property, I encountered a different error: jakarta.el.ELException: Function [:Boolean] not found. This may relate to the the fact that rt.jar contains the Boolean class (which EL doesn't have access to?).

I tested the same JSP on Tomcat (with security enabled), and encountered the very same exception. 

This is the troublesome EL Expression: “${Boolean(true)}” 

I tested it in Tomcat 7 and 9, and the same exception is thrown. The behavior is consistent everywhere.  However, can anyone explain it is that way (or point me to any resources)? My current understanding is that, when security is enabled, EL(or Tomcat?) doesn’t have access, by default, to the java runtime jar, rt.jar? (Which may explain why the original error asked me add the java.io.FilePermission to the rt.jar)  Although I tried modifying the permissions in Tomcat but was unsuccessful in getting the EL code to run with security enabled.  

However, I tested further, and the following code does work?  I’m assuming because it’s not going through EL? 

    <%
        Boolean b = new Boolean("true");
        System.out.println("Boolean Result: " + b);
    %>

    <%= b %>

I’m not very familiar with java security and, this is beyond what I originally started looking into, but, once again, thank you for your help. And I'll mark this issue as resolved/invalid. 

Full Exception: 

javax.el.ELException: Function [:Boolean] not found
    org.apache.el.parser.AstFunction.getValue(AstFunction.java:148)
    org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:190)
    org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:701)
    org.apache.jsp.el_jsp._jspService(el_jsp.java:163)
    org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:71)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:477)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    sun.reflect.GeneratedMethodAccessor58.invoke(Unknown Source)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:498)
    org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
    org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
    java.security.AccessController.doPrivileged(AccessController.java:770)
    javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
    org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:170)
    java.security.AccessController.doPrivileged(AccessController.java:734)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    sun.reflect.GeneratedMethodAccessor57.invoke(Unknown Source)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:498)
    org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
    org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
    java.security.AccessController.doPrivileged(AccessController.java:770)
    javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
    org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
Comment 5 Mark Thomas 2020-06-02 21:51:16 UTC
At first glance, I'd expect that to work. Re-opening while I dig into what is going on...
Comment 6 Mark Thomas 2020-06-02 22:34:03 UTC
Thanks for the test case. It makes debugging what is going on a lot easier.

I think there is a bug here.
 
Over time we have added various optimisations to the ImportHandler to address performance issues caused by the ambiguity introduced in EL 3.0. A good summary of those ambiguities and the associated performance issues can be found in https://tomcat.markmail.org/thread/pcxxg4ql6mxjwcmd and the links in the first email of that thread.

One of those optimisations was to do a resource lookup before trying to load the class as this was considerably quicker for the "not a class" case and only marginally slower for the "is a class" case. It is this resource lookup that is failing due to a lack of read permission.

Given that this test is there to optimise the "not a class" case, that the return value is thrown away and that the class loading happens a few lines later, I think your proposed patch is along the right lines. We've been moving towards removing anonymous classes so I am going to try a variation of your patch that uses an inner class.
Comment 7 Mark Thomas 2020-06-02 23:00:28 UTC
Fixed in:
- master for 10.0.0-M6 onwards
- 9.0.x for 9.0.36 onwards
- 8.5.x for 8.5.56 onwards

Tomcat 7 doesn't support EL 3.0