This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 7.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit c4b1559d6e7f131be804373719fe41c26969df54 Author: Kyle Stiemann <kyle.stiem...@contrastsecurity.com> AuthorDate: Thu Sep 10 16:47:21 2020 -0400 Fix 64735 ServletContext.addJspFile() always fails with SecurityManager --- build.properties.default | 24 ++-- build.xml | 13 ++ .../catalina/core/ApplicationContextFacade.java | 1 + ...estApplicationContextFacadeSecurityManager.java | 153 +++++++++++++++++++++ .../util/security/SecurityManagerBaseTest.java | 50 +++++++ webapps/docs/changelog.xml | 5 + 6 files changed, 234 insertions(+), 12 deletions(-) diff --git a/build.properties.default b/build.properties.default index 96637e5..c4ab2ba 100644 --- a/build.properties.default +++ b/build.properties.default @@ -257,29 +257,29 @@ hamcrest.home=${base.path}/hamcrest-${hamcrest.version} hamcrest.jar=${hamcrest.home}/hamcrest-core-${hamcrest.version}.jar hamcrest.loc=${base-maven.loc}/org/hamcrest/hamcrest-core/${hamcrest.version}/hamcrest-core-${hamcrest.version}.jar -# ----- EasyMock, version 3.2 or later ----- -easymock.version=3.2 +# ----- EasyMock, version 3.6 or later ----- +easymock.version=3.6 easymock.checksum.enabled=true -easymock.checksum.algorithm=MD5|SHA-1 -easymock.checksum.value=0da4291328e18798621c36fdf8bc4c3a|00c82f7fa3ef377d8954b1db25123944b5af2ba4 +easymock.checksum.algorithm=SHA-512 +easymock.checksum.value=b49673e7872aa8b006d377a4ca56eed8f6e98e45ea4efe26d24cafe81ea569b557f5a326edcd387288267db8c4b59f2c3c61010f3ad01c4d1067a35430533794 easymock.home=${base.path}/easymock-${easymock.version} easymock.jar=${easymock.home}/easymock-${easymock.version}.jar easymock.loc=${base-maven.loc}/org/easymock/easymock/${easymock.version}/easymock-${easymock.version}.jar -# ----- cglib, used by EasyMock, version 2.2 or later ----- -cglib.version=2.2.3 +# ----- cglib, used by EasyMock, version 3.3 or later ----- +cglib.version=3.3.0 cglib.checksum.enabled=true -cglib.checksum.algorithm=MD5|SHA-1 -cglib.checksum.value=694815351007f966c14ea093ec838323|6a4af5d9112066a5baf235fd55d5876969bc813c +cglib.checksum.algorithm=SHA-512 +cglib.checksum.value=faa1d2121e87ae69e179e3aae217accd0834e0da716b91a029fd526e192612e71675f2740bedf48e23ef1edc45f672a2be1b3e78bbfb1ad59c96dd3d2feeedba cglib.home=${base.path}/cglib-${cglib.version} cglib.jar=${cglib.home}/cglib-nodep-${cglib.version}.jar cglib.loc=${base-sf.loc}/cglib/cglib-nodep-${cglib.version}.jar -# ----- objenesis, used by EasyMock, version 1.2 or later ----- -objenesis.version=1.2 +# ----- objenesis, used by EasyMock, version 2.6 or later ----- +objenesis.version=2.6 objenesis.checksum.enabled=true -objenesis.checksum.algorithm=MD5|SHA-1 -objenesis.checksum.value=bee117291d50b41b8e8cf0ac5435df1d|bfcb0539a071a4c5a30690388903ac48c0667f2a +objenesis.checksum.algorithm=SHA-512 +objenesis.checksum.value=23a593bded8cb43236faad2018b008da47bf4e29cc60c2e98fd4f2ed578fe2baddd3a98547dc14273017c82cb19ce8eaaab71d49273411856a2ba1a5d51015fc objenesis.home=${base.path}/objenesis-${objenesis.version} objenesis.jar=${objenesis.home}/objenesis-${objenesis.version}.jar objenesis.loc=${base-maven.loc}/org/objenesis/objenesis/${objenesis.version}/objenesis-${objenesis.version}.jar diff --git a/build.xml b/build.xml index 707e630..5bd5a7c 100644 --- a/build.xml +++ b/build.xml @@ -1569,8 +1569,21 @@ <exclude unless="java.7.home" name="org/apache/tomcat/websocket/**" /> <!-- Exclude performance tests. E.g. on systems with slow/inconsistent timing --> <exclude name="**/*Performance.java" if="${test.excludePerformance}" /> + <!-- + Avoid running tests which require the SecurityManager with other + tests. See below for more details. + --> + <exclude name="**/*SecurityManager.java" /> </fileset> </batchtest> + <!-- + Run tests which require the SecurityManager in their own forked + batch to ensure that global/system security settings don't pollute + other tests. See SecurityManagerBaseTest.java for more details. + --> + <batchtest todir="${test.reports}" unless="test.entry" fork="true"> + <fileset dir="test" includes="**/SecurityManager.java" excludes="${test.exclude}" /> + </batchtest> </junit> </sequential> </macrodef> diff --git a/java/org/apache/catalina/core/ApplicationContextFacade.java b/java/org/apache/catalina/core/ApplicationContextFacade.java index 2c39404..544b219 100644 --- a/java/org/apache/catalina/core/ApplicationContextFacade.java +++ b/java/org/apache/catalina/core/ApplicationContextFacade.java @@ -117,6 +117,7 @@ public class ApplicationContextFacade implements ServletContext { classCache.put("getAttribute", clazz); classCache.put("log", clazz); classCache.put("setSessionTrackingModes", new Class[]{Set.class} ); + classCache.put("declareRoles", new Class[]{String[].class}); } diff --git a/test/org/apache/catalina/core/TestApplicationContextFacadeSecurityManager.java b/test/org/apache/catalina/core/TestApplicationContextFacadeSecurityManager.java new file mode 100644 index 0000000..442e1e7 --- /dev/null +++ b/test/org/apache/catalina/core/TestApplicationContextFacadeSecurityManager.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * 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.catalina.core; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +import org.apache.catalina.security.SecurityUtil; +import org.apache.tomcat.util.security.SecurityManagerBaseTest; +import org.easymock.EasyMock; +import org.easymock.IAnswer; +import org.easymock.IExpectationSetters; +import org.easymock.internal.LastControl; + +@RunWith(Parameterized.class) +public final class TestApplicationContextFacadeSecurityManager extends SecurityManagerBaseTest { + + /** + * @return {@link Collection} of non-static, non-object, public {@link + * Method}s in {@link ApplicationContextFacade} to be run with the the Java + * 2 {@link SecurityManager} been enabled. + */ + @Parameterized.Parameters(name = "{index}: method={0}") + public static Collection<Method> publicApplicationContextFacadeMethods() { + List<Method> result = new ArrayList<Method>(); + for (Method m : ApplicationContextFacade.class.getMethods()) { + if (!Modifier.isStatic(m.getModifiers())) { + try { + Object.class.getMethod(m.getName(), m.getParameterTypes()); + // Skip; + } catch (final NoSuchMethodException e) { + result.add(m); + } + } + } + return result; + } + + + private static Object[] getDefaultParams(final Method method) { + final Class<?>[] paramTypes = method.getParameterTypes(); + final Object[] params = new Object[paramTypes.length]; + for (int i = 0; i < params.length; i++) { + params[i] = getDefaultValue(paramTypes[i]); + } + return params; + } + + + @SuppressWarnings("unchecked") + private static <T> T getDefaultValue(final Class<T> clazz) { + return !isVoid(clazz) ? (T) Array.get(Array.newInstance(clazz, 1), 0) : null; + } + + + private static <T> boolean isVoid(Class<T> clazz) { + return void.class.equals(clazz) || Void.class.equals(clazz); + } + + + @Parameter(0) + public Method methodToTest; + + + /** + * Test for + * <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=64735">Bug + * 64735</a> which confirms that {@link ApplicationContextFacade} behaves + * correctly when the Java 2 {@link SecurityManager} has been enabled. + * + * @throws NoSuchMethodException Should never happen + * @throws IllegalAccessException Should never happen + * @throws InvocationTargetException Should never happen + */ + @Test + public void testBug64735() + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + Assert.assertTrue(SecurityUtil.isPackageProtectionEnabled()); + + // Mock the ApplicationContext that we provide to the ApplicationContextFacade. + final ApplicationContext mockAppContext = EasyMock.createMock(ApplicationContext.class); + final Method expectedAppContextMethod = + ApplicationContext.class.getMethod( + methodToTest.getName(), + methodToTest.getParameterTypes()); + + // Expect that only the provided method which is being tested will be called exactly once. + final IExpectationSetters<Object> expectationSetters; + if (isVoid(expectedAppContextMethod.getReturnType())) { + expectedAppContextMethod.invoke(mockAppContext, getDefaultParams(methodToTest)); + expectationSetters = EasyMock.expectLastCall(); + } else { + expectationSetters = + EasyMock.expect(expectedAppContextMethod.invoke( + mockAppContext, getDefaultParams(methodToTest))); + } + expectationSetters.andAnswer(new IAnswer<Object>() { + @Override + public Object answer() throws Throwable { + Assert.assertEquals( + expectedAppContextMethod, + LastControl.getCurrentInvocation().getMethod()); + return getDefaultValue(expectedAppContextMethod.getReturnType()); + } + + }).once(); + + EasyMock.replay(mockAppContext); + EasyMock.verifyUnexpectedCalls(mockAppContext); + + // Invoke the method on ApplicationContextFacade. Fail if any unexpected exceptions are + // thrown. + try { + methodToTest.invoke( + new ApplicationContextFacade(mockAppContext), + getDefaultParams(methodToTest)); + } catch (final IllegalAccessException e) { + throw new RuntimeException( + "Failed to call " + methodToTest + " with SecurityManager enabled.", e); + } catch (final InvocationTargetException e) { + throw new RuntimeException( + "Failed to call " + methodToTest + " with SecurityManager enabled.", e); + } + + // Verify that the method called through to the wrapped ApplicationContext correctly. + EasyMock.verifyRecording(); + } +} diff --git a/test/org/apache/tomcat/util/security/SecurityManagerBaseTest.java b/test/org/apache/tomcat/util/security/SecurityManagerBaseTest.java new file mode 100644 index 0000000..af7ff8f --- /dev/null +++ b/test/org/apache/tomcat/util/security/SecurityManagerBaseTest.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * 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.tomcat.util.security; + +import java.security.Permission; + +import org.apache.catalina.security.SecurityUtil; + +/** + * Base test class for unit tests which require the Java 2 {@link + * SecurityManager} to be enabled. Tests that extend this class must be run in a + * forked SecurityManager test batch since this class modifies global {@link + * System} settings which may interfere with other tests. On static class + * initialization, this class sets up the {@code "package.definition"} and + * {@code "package.access"} system properties and adds a no-op SecurityManager + * which does not check permissions. These settings are required in order to + * make {@link org.apache.catalina.Globals#IS_SECURITY_ENABLED} and {@link + * SecurityUtil#isPackageProtectionEnabled()} return true. + */ +public abstract class SecurityManagerBaseTest { + static { + System.setProperty("package.definition", "test"); + System.setProperty("package.access", "test"); + System.setSecurityManager(new SecurityManager() { + @Override + public void checkPermission(final Permission permission) { + // no-op + } + + @Override + public void checkPermission(final Permission permission, Object context) { + // no-op + } + }); + } +} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 4471506..df99d4d 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -67,6 +67,11 @@ request from John Bampton. (markt) </fix> <fix> + <bug>64735</bug>: Ensure that none of the methods on a + <code>ServletContext</code> instance always fail when running under a + SecurityManager. Pull request provided by Kyle Stiemann. (markt) + </fix> + <fix> <bug>64765</bug>: Ensure that the number of currently processing threads is tracked correctly when a web application is undeployed, long running requests are being processed and --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org