Den 2016-11-23 kl. 17:52, skrev Emmanuel Bourg: > Would you be able to rebuild with this version of the > ResourceLinkFactory class and see if it works better? > > https://raw.githubusercontent.com/apache/tomcat70/TOMCAT_7_0_73/java/org/apache/naming/factory/ResourceLinkFactory.java >
Indeed, with this file, things seem to work (crossing my fingers that I have understood how to use quilt, and thus built correctly). I had to make one change to line 43 to make it compile. I am attaching the refreshed version of CVE-2016-6797.patch for reference. Merci beaucoup, Arne
Description: Fixes CVE-2016-6797: The ResourceLinkFactory did not limit web application access to global JNDI resources to those resources explicitly linked to the web application. Therefore, it was possible for a web application to access any global JNDI resource whether an explicit ResourceLink had been configured or not. Origin: backport, https://svn.apache.org/r1757275 --- a/java/org/apache/catalina/core/NamingContextListener.java +++ b/java/org/apache/catalina/core/NamingContextListener.java @@ -41,6 +41,7 @@ import org.apache.catalina.ContainerEvent; import org.apache.catalina.ContainerListener; import org.apache.catalina.Context; +import org.apache.catalina.Engine; import org.apache.catalina.Host; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; @@ -68,6 +69,7 @@ import org.apache.naming.ResourceRef; import org.apache.naming.ServiceRef; import org.apache.naming.TransactionRef; +import org.apache.naming.factory.ResourceLinkFactory; import org.apache.tomcat.util.modeler.Registry; import org.apache.tomcat.util.res.StringManager; @@ -344,6 +346,11 @@ registry.unregisterComponent(objectName); } } + + javax.naming.Context global = getGlobalNamingContext(); + if (global != null) { + ResourceLinkFactory.deregisterGlobalResourceAccess(global); + } } finally { objectNames.clear(); @@ -1167,6 +1174,17 @@ logger.error(sm.getString("naming.bindFailed", e)); } + ResourceLinkFactory.registerGlobalResourceAccess( + getGlobalNamingContext(), resourceLink.getName(), resourceLink.getGlobal()); + } + + + private javax.naming.Context getGlobalNamingContext() { + if (container instanceof Context) { + Engine e = (Engine) ((Context) container).getParent().getParent(); + return e.getService().getServer().getGlobalNamingContext(); + } + return null; } @@ -1270,6 +1288,7 @@ logger.error(sm.getString("naming.unbindFailed", e)); } + ResourceLinkFactory.deregisterGlobalResourceAccess(getGlobalNamingContext(), name); } --- a/java/org/apache/naming/factory/ResourceLinkFactory.java +++ b/java/org/apache/naming/factory/ResourceLinkFactory.java @@ -5,20 +5,21 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ - - + */ package org.apache.naming.factory; +import java.util.HashMap; import java.util.Hashtable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.naming.Context; import javax.naming.Name; @@ -28,35 +29,32 @@ import javax.naming.spi.ObjectFactory; import org.apache.naming.ResourceLinkRef; - +import org.apache.naming.StringManager; /** * <p>Object factory for resource links.</p> - * + * * @author Remy Maucherat */ -public class ResourceLinkFactory - implements ObjectFactory { - - - // ----------------------------------------------------------- Constructors - +public class ResourceLinkFactory implements ObjectFactory { // ------------------------------------------------------- Static Variables + private static final StringManager sm = StringManager.getManager(Constants.Package); /** * Global naming context. */ private static Context globalContext = null; + private static Map<ClassLoader,Map<String,String>> globalResourceRegistrations = + new ConcurrentHashMap<ClassLoader,Map<String,String>>(); // --------------------------------------------------------- Public Methods - /** * Set the global context (note: can only be used once). - * + * * @param newGlobalContext new global context value */ public static void setGlobalContext(Context newGlobalContext) { @@ -69,21 +67,73 @@ } - // -------------------------------------------------- ObjectFactory Methods + public static void registerGlobalResourceAccess(Context globalContext, String localName, + String globalName) { + validateGlobalContext(globalContext); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Map<String,String> registrations = globalResourceRegistrations.get(cl); + if (registrations == null) { + // Web application initialization is single threaded so this is + // safe. + registrations = new HashMap<String,String>(); + globalResourceRegistrations.put(cl, registrations); + } + registrations.put(localName, globalName); + } + + + public static void deregisterGlobalResourceAccess(Context globalContext, String localName) { + validateGlobalContext(globalContext); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Map<String,String> registrations = globalResourceRegistrations.get(cl); + if (registrations != null) { + registrations.remove(localName); + } + } + public static void deregisterGlobalResourceAccess(Context globalContext) { + validateGlobalContext(globalContext); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + globalResourceRegistrations.remove(cl); + } + + + private static void validateGlobalContext(Context globalContext) { + if (ResourceLinkFactory.globalContext != null && + ResourceLinkFactory.globalContext != globalContext) { + throw new SecurityException("Caller provided invalid global context"); + } + } + + + private static boolean validateGlobalResourceAccess(String globalName) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + while (cl != null) { + Map<String,String> registrations = globalResourceRegistrations.get(cl); + if (registrations != null && registrations.containsValue(globalName)) { + return true; + } + cl = cl.getParent(); + } + return false; + } + + + // -------------------------------------------------- ObjectFactory Methods + /** * Create a new DataSource instance. - * + * * @param obj The reference object describing the DataSource */ @Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, - Hashtable<?,?> environment) - throws NamingException { - - if (!(obj instanceof ResourceLinkRef)) + Hashtable<?,?> environment) throws NamingException { + + if (!(obj instanceof ResourceLinkRef)) { return null; + } // Can we process this request? Reference ref = (Reference) obj; @@ -93,16 +143,33 @@ RefAddr refAddr = ref.get(ResourceLinkRef.GLOBALNAME); if (refAddr != null) { globalName = refAddr.getContent().toString(); + // Confirm that the current web application is currently configured + // to access the specified global resource + if (!validateGlobalResourceAccess(globalName)) { + return null; + } Object result = null; result = globalContext.lookup(globalName); - // FIXME: Check type + // Check the expected type + String expectedClassName = ref.getClassName(); + if (expectedClassName == null) { + throw new IllegalArgumentException( + sm.getString("resourceLinkFactory.nullType", name, globalName)); + } + try { + Class<?> expectedClazz = Class.forName( + expectedClassName, true, Thread.currentThread().getContextClassLoader()); + if (!expectedClazz.isAssignableFrom(result.getClass())) { + throw new IllegalArgumentException(sm.getString("resourceLinkFactory.wrongType", + name, globalName, expectedClassName, result.getClass().getName())); + } + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException(sm.getString("resourceLinkFactory.unknownType", + name, globalName, expectedClassName), e); + } return result; } - return (null); - - + return null; } - - } --- /dev/null +++ b/test/org/apache/naming/TestNamingContext.java @@ -0,0 +1,87 @@ +package org.apache.naming; + +import javax.naming.Context; +import javax.naming.NamingException; + +import org.apache.catalina.deploy.ContextEnvironment; +import org.apache.catalina.deploy.ContextResourceLink; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.naming.factory.ResourceLinkFactory; +import org.junit.Assert; +import org.junit.Test; + +public class TestNamingContext extends TomcatBaseTest { + + private static final String COMP_ENV = "comp/env"; + private static final String GLOBAL_NAME = "global"; + private static final String LOCAL_NAME = "local"; + private static final String DATA = "Cabbage"; + + + @Test + public void testGlobalNaming() throws Exception { + Tomcat tomcat = getTomcatInstance(); + tomcat.enableNaming(); + + org.apache.catalina.Context ctx = tomcat.addContext("", null); + + tomcat.start(); + + Context webappInitial = ContextBindings.getContext(ctx); + + // Nothing added at the moment so should be null + Object obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertNull(obj); + + ContextEnvironment ce = new ContextEnvironment(); + ce.setName(GLOBAL_NAME); + ce.setValue(DATA); + ce.setType(DATA.getClass().getName()); + + tomcat.getServer().getGlobalNamingResources().addEnvironment(ce); + + // No link so still should be null + obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertNull(obj); + + // Now add a resource link to the context + ContextResourceLink crl = new ContextResourceLink(); + crl.setGlobal(GLOBAL_NAME); + crl.setName(LOCAL_NAME); + crl.setType(DATA.getClass().getName()); + ctx.getNamingResources().addResourceLink(crl); + + // Link exists so should be OK now + obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertEquals(DATA, obj); + + // Try shortcut + ResourceLinkFactory factory = new ResourceLinkFactory(); + ResourceLinkRef rlr = new ResourceLinkRef(DATA.getClass().getName(), GLOBAL_NAME, null, null); + obj = factory.getObjectInstance(rlr, null, null, null); + Assert.assertEquals(DATA, obj); + + // Remove the link + ctx.getNamingResources().removeResourceLink(LOCAL_NAME); + + // No link so should be null + obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertNull(obj); + + // Shortcut should fail too + obj = factory.getObjectInstance(rlr, null, null, null); + Assert.assertNull(obj); + } + + + private Object doLookup(Context context, String name) { + Object result = null; + try { + result = context.lookup(name); + } catch (NamingException nnfe) { + // Ignore + } + return result; + } +}