1

We update tomcat to 8.5.6 from 8.0.32 recently, and we meet a AccessControlException when try to load /opt/apache-tomcat-8.5.6_1/webapps/example/WEB-INF/classes/com/sun/xml/internal/ws/runtime/config/jaxb.properties, and I debug the source code between tomcat 8.5.6 and 8.0.32, it's different in org.apache.catalina.loader.WebappClassLoaderBase.findResource

Tomcat8.0

public URL findResource(final String name) {

    if (log.isDebugEnabled())
        log.debug("    findResource(" + name + ")");

    checkStateForResourceLoading(name);

    URL url = null;

    String path = nameToPath(name);

    ResourceEntry entry = resourceEntries.get(path);
    if (entry == null) {
        if (securityManager != null) {
            PrivilegedAction<ResourceEntry> dp =
                new PrivilegedFindResourceByName(name, path);
            entry = AccessController.doPrivileged(dp);
        } else {
            entry = findResourceInternal(name, path);
        }
    }
    if (entry != null) {
        url = entry.source;
        entry.webResource = null;
    }

    if ((url == null) && hasExternalRepositories) {
        url = super.findResource(name);
    }

    if (log.isDebugEnabled()) {
        if (url != null)
            log.debug("    --> Returning '" + url.toString() + "'");
        else
            log.debug("    --> Resource not found, returning null");
    }
    return url;
}

Tomcat8.5.6

public URL findResource(final String name) {

    if (log.isDebugEnabled())
        log.debug("    findResource(" + name + ")");

    checkStateForResourceLoading(name);

    URL url = null;

    String path = nameToPath(name);

    WebResource resource = resources.getClassLoaderResource(path);
    if (resource.exists()) {
        url = resource.getURL();
        trackLastModified(path, resource);
    }

    if ((url == null) && hasExternalRepositories) {
        url = super.findResource(name);
    }

    if (log.isDebugEnabled()) {
        if (url != null)
            log.debug("    --> Returning '" + url.toString() + "'");
        else
            log.debug("    --> Resource not found, returning null");
    }
    return url;
}

As you can see, tomcat8.0 load resource by AccessController.doPrivileged, but in tomcat8.5.6, it load the resource directly, I think that's why I got a Exception

java.security.AccessControlException: access denied 
("java.io.FilePermission" 
"/opt/apache-tomcat-8.5.6_1/webapps/example/WEB-INF/classes/com/sun/xml/internal/ws/runtime/config/jaxb.properties" 
"read")

java.lang.IllegalStateException: MASM0003: Default [ jaxws-tubes-default.xml ] configuration file was not loaded
        at com.sun.xml.internal.ws.assembler.MetroConfigLoader.init(MetroConfigLoader.java:133)
        at com.sun.xml.internal.ws.assembler.MetroConfigLoader.<init>(MetroConfigLoader.java:104)

this file is loaded by MetroConfigLoader,

private static JAXBContext createJAXBContext() throws Exception {
        return isJDKInternal()?(JAXBContext)AccessController.doPrivileged(new PrivilegedExceptionAction<JAXBContext>() {
            public JAXBContext run() throws Exception {
                return JAXBContext.newInstance(MetroConfig.class.getPackage().getName());
            }
        }, createSecurityContext()):JAXBContext.newInstance(MetroConfig.class.getPackage().getName());
    }

    private static AccessControlContext createSecurityContext() {
        PermissionCollection perms = new Permissions();
        perms.add(new RuntimePermission("accessClassInPackage.com.sun.xml.internal.ws.runtime.config"));
        perms.add(new ReflectPermission("suppressAccessChecks"));
        return new AccessControlContext(new ProtectionDomain[]{new ProtectionDomain((CodeSource)null, perms)});
    }

Does anyone meet same issue? or there is some other issues. thanks.

chaoluo
  • 2,596
  • 1
  • 17
  • 29

2 Answers2

1

After three days researching, now I use jaxws-rt instead of default implementation in JDK, and as you can read from the code in JDK:

private static JAXBContext createJAXBContext() throws Exception {
        return isJDKInternal()?(JAXBContext)AccessController.doPrivileged(new PrivilegedExceptionAction<JAXBContext>() {
            public JAXBContext run() throws Exception {
                return JAXBContext.newInstance(MetroConfig.class.getPackage().getName());
            }
        }, createSecurityContext()):JAXBContext.newInstance(MetroConfig.class.getPackage().getName());
}

If it's JDK internal, it will create the instance with specific privilege, and tomcat get resource by doPrivileged in tomcat8.0, but it's different in tomcat8.5, So it can't get the resource without privilege

java.security.AccessControlException: access denied ("java.io.FilePermission" 
"/opt/apache-tomcat-8.5.6_1/webapps/example/WEB-INF/classes/com/sun/xml/internal/ws/runtime/config/jaxb.properties" 
"read")

So I changed to external jaxws-rt, and it will create the instance directly. I just add jaxws-rt to pom.

 <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-rt</artifactId>
        <version>2.2.10</version>
 </dependency>
chaoluo
  • 2,596
  • 1
  • 17
  • 29
0

I'd like to add a notice to the answer by chaoluo above. In case you're using ssl certificates or request timeouts or connect timeouts on a per request basis, you should double-check this functionality after switching from default JDK implementation to external jaxws-rt. The external implementation uses different property names than JDK implementation. If you provided them using string constants, you might forget to update them. In such case the ssl certificates and timeouts will be completely ignored.

Example code for JDK implementation:

javax.xml.ws.BindingProvider proxy = someCustomCodeWhichCreatesProxy()
proxy.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", customCodeWhichCreateSSLFactory());
proxy.getRequestContext().put("com.sun.xml.internal.ws.request.timeout", customCodeWhichDefinesRequestTimeout());
proxy.getRequestContext().put("com.sun.xml.internal.ws.connect.timeout", customCodeWhichDefinesConnectionTimeout());
// ... some code, which uses the proxy for calling external webservice

Example code for external jaxws-rt:

javax.xml.ws.BindingProvider proxy = someCustomCodeWhichCreatesProxy()
proxy.getRequestContext().put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", customCodeWhichCreateSSLFactory());
proxy.getRequestContext().put("com.sun.xml.ws.request.timeout", customCodeWhichDefinesRequestTimeout());
proxy.getRequestContext().put("com.sun.xml.ws.connect.timeout", customCodeWhichDefinesConnectionTimeout());
// ... some code, which uses the proxy for calling external webservice

Alternatively one may use constants defined in com.sun.xml.ws.developer.JAXWSProperties from jaxws-rt instead of the hardcoded strings

jgolda
  • 11
  • 2