diff --git a/SOURCES/tomcat-7.0.76-CVE-2018-1304.patch b/SOURCES/tomcat-7.0.76-CVE-2018-1304.patch new file mode 100644 index 0000000..e9536cb --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2018-1304.patch @@ -0,0 +1,46 @@ +commit 2d69fde135302e8cff984bb2131ec69f2e396964 +Author: Mark Thomas +Date: Tue Feb 6 11:41:16 2018 +0000 + + git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1823309 13f79535-47bb-0310-9956-ffa450edef68 + +diff --git a/java/org/apache/catalina/realm/RealmBase.java b/java/org/apache/catalina/realm/RealmBase.java +index 2098c2e8cc..9697440d35 100644 +--- java/org/apache/catalina/realm/RealmBase.java ++++ java/org/apache/catalina/realm/RealmBase.java +@@ -688,9 +688,9 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { + + // Check each defined security constraint + String uri = request.getRequestPathMB().toString(); +- // Bug47080 - in rare cases this may be null ++ // Bug47080 - in rare cases this may be null or "" + // Mapper treats as '/' do the same to prevent NPE +- if (uri == null) { ++ if (uri == null || uri.length() == 0) { + uri = "/"; + } + +@@ -722,7 +722,8 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { + } + + for(int k=0; k < patterns.length; k++) { +- if(uri.equals(patterns[k])) { ++ // Exact match including special case for the context root. ++ if(uri.equals(patterns[k]) || patterns[k].length() == 0 && uri.equals("/")) { + found = true; + if(collection[j].findMethod(method)) { + if(results == null) { +diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml +--- webapps/docs/changelog.xml.orig 2019-02-28 15:11:59.735767416 -0500 ++++ webapps/docs/changelog.xml 2019-02-28 15:12:23.805697236 -0500 +@@ -64,6 +64,10 @@ + When generating a redirect to a directory in the Default Servlet, avoid + generating a protocol relative redirect. (markt) + ++ ++ 62067: Correctly apply security constraints mapped to the ++ context root using a URL pattern of "". (markt) ++ + + + diff --git a/SOURCES/tomcat-7.0.76-CVE-2018-1305.patch b/SOURCES/tomcat-7.0.76-CVE-2018-1305.patch new file mode 100644 index 0000000..1fe8bc8 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2018-1305.patch @@ -0,0 +1,974 @@ +commit 2aac69f694d42d9219eb27018b3da0ae1bdd73ab +Author: Mark Thomas +Date: Tue Feb 6 12:49:49 2018 +0000 + + Process all ServletSecurity annotations at web application start rather + than at servlet load time to ensure constraints are applied + consistently. + + git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1823322 13f79535-47bb-0310-9956-ffa450edef68 + +diff -up java/org/apache/catalina/authenticator/AuthenticatorBase.java.orig java/org/apache/catalina/authenticator/AuthenticatorBase.java +--- java/org/apache/catalina/authenticator/AuthenticatorBase.java.orig 2019-02-28 15:41:52.561346049 -0500 ++++ java/org/apache/catalina/authenticator/AuthenticatorBase.java 2019-02-28 15:44:09.129930251 -0500 +@@ -40,7 +40,6 @@ import org.apache.catalina.Manager; + import org.apache.catalina.Realm; + import org.apache.catalina.Session; + import org.apache.catalina.Valve; +-import org.apache.catalina.Wrapper; + import org.apache.catalina.connector.Request; + import org.apache.catalina.connector.Response; + import org.apache.catalina.deploy.LoginConfig; +@@ -488,13 +487,6 @@ public abstract class AuthenticatorBase + } + } + +- // The Servlet may specify security constraints through annotations. +- // Ensure that they have been processed before constraints are checked +- Wrapper wrapper = (Wrapper) request.getMappingData().wrapper; +- if (wrapper != null) { +- wrapper.servletSecurityAnnotationScan(); +- } +- + Realm realm = this.context.getRealm(); + // Is this request URI subject to a security constraint? + SecurityConstraint [] constraints +diff -up java/org/apache/catalina/core/ApplicationContext.java.orig java/org/apache/catalina/core/ApplicationContext.java +--- java/org/apache/catalina/core/ApplicationContext.java.orig 2017-03-09 08:51:39.000000000 -0500 ++++ java/org/apache/catalina/core/ApplicationContext.java 2019-02-28 15:44:09.130930248 -0500 +@@ -53,8 +53,10 @@ import javax.servlet.ServletException; + import javax.servlet.ServletRegistration; + import javax.servlet.ServletRequestAttributeListener; + import javax.servlet.ServletRequestListener; ++import javax.servlet.ServletSecurityElement; + import javax.servlet.SessionCookieConfig; + import javax.servlet.SessionTrackingMode; ++import javax.servlet.annotation.ServletSecurity; + import javax.servlet.descriptor.JspConfigDescriptor; + import javax.servlet.http.HttpSessionAttributeListener; + import javax.servlet.http.HttpSessionListener; +@@ -69,6 +71,7 @@ import org.apache.catalina.Wrapper; + import org.apache.catalina.connector.Connector; + import org.apache.catalina.deploy.FilterDef; + import org.apache.catalina.util.ResourceSet; ++import org.apache.catalina.util.Introspection; + import org.apache.catalina.util.ServerInfo; + import org.apache.catalina.util.URLEncoder; + import org.apache.naming.resources.DirContextURLStreamHandler; +@@ -1186,14 +1189,27 @@ public class ApplicationContext implemen + } + } + ++ ServletSecurity annotation = null; + if (servlet == null) { + wrapper.setServletClass(servletClass); ++ Class clazz = Introspection.loadClass(context, servletClass); ++ if (clazz != null) { ++ annotation = clazz.getAnnotation(ServletSecurity.class); ++ } + } else { + wrapper.setServletClass(servlet.getClass().getName()); + wrapper.setServlet(servlet); ++ if (context.wasCreatedDynamicServlet(servlet)) { ++ annotation = servlet.getClass().getAnnotation(ServletSecurity.class); ++ } + } + +- return context.dynamicServletAdded(wrapper); ++ ServletRegistration.Dynamic registration = ++ new ApplicationServletRegistration(wrapper, context); ++ if (annotation != null) { ++ registration.setServletSecurity(new ServletSecurityElement(annotation)); ++ } ++ return registration; + } + + +diff -up java/org/apache/catalina/core/ApplicationServletRegistration.java.orig java/org/apache/catalina/core/ApplicationServletRegistration.java +--- java/org/apache/catalina/core/ApplicationServletRegistration.java.orig 2019-02-28 15:41:52.568346028 -0500 ++++ java/org/apache/catalina/core/ApplicationServletRegistration.java 2019-02-28 15:44:09.130930248 -0500 +@@ -5,9 +5,9 @@ + * 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. +@@ -41,15 +41,16 @@ public class ApplicationServletRegistrat + */ + private static final StringManager sm = + StringManager.getManager(Constants.Package); +- +- private Wrapper wrapper; +- private Context context; +- ++ ++ private final Wrapper wrapper; ++ private final Context context; ++ private ServletSecurityElement constraint; ++ + public ApplicationServletRegistration(Wrapper wrapper, + Context context) { + this.wrapper = wrapper; + this.context = context; +- ++ + } + + @Override +@@ -65,9 +66,9 @@ public class ApplicationServletRegistrat + @Override + public Map getInitParameters() { + ParameterMap result = new ParameterMap(); +- ++ + String[] parameterNames = wrapper.findInitParameters(); +- ++ + for (String parameterName : parameterNames) { + result.put(parameterName, wrapper.findInitParameter(parameterName)); + } +@@ -91,7 +92,7 @@ public class ApplicationServletRegistrat + if (getInitParameter(name) != null) { + return false; + } +- ++ + wrapper.addInitParameter(name, value); + + return true; +@@ -99,9 +100,9 @@ public class ApplicationServletRegistrat + + @Override + public Set setInitParameters(Map initParameters) { +- ++ + Set conflicts = new HashSet(); +- ++ + for (Map.Entry entry : initParameters.entrySet()) { + if (entry.getKey() == null || entry.getValue() == null) { + throw new IllegalArgumentException(sm.getString( +@@ -151,13 +152,14 @@ public class ApplicationServletRegistrat + "applicationServletRegistration.setServletSecurity.iae", + getName(), context.getName())); + } +- ++ + if (!context.getState().equals(LifecycleState.STARTING_PREP)) { + throw new IllegalStateException(sm.getString( + "applicationServletRegistration.setServletSecurity.ise", + getName(), context.getName())); + } + ++ this.constraint = constraint; + return context.addServletSecurity(this, constraint); + } + +@@ -167,9 +169,9 @@ public class ApplicationServletRegistrat + if (urlPatterns == null) { + return Collections.emptySet(); + } +- ++ + Set conflicts = new HashSet(); +- ++ + for (String urlPattern : urlPatterns) { + String wrapperName = context.findServletMapping(urlPattern); + if (wrapperName != null) { +@@ -187,10 +189,15 @@ public class ApplicationServletRegistrat + if (!conflicts.isEmpty()) { + return conflicts; + } +- ++ + for (String urlPattern : urlPatterns) { + context.addServletMapping(urlPattern, wrapper.getName()); + } ++ ++ if (constraint != null) { ++ context.addServletSecurity(this, constraint); ++ } ++ + return Collections.emptySet(); + } + +@@ -199,7 +206,7 @@ public class ApplicationServletRegistrat + + Set result = new HashSet(); + String servletName = wrapper.getName(); +- ++ + String[] urlPatterns = context.findServletMappings(); + for (String urlPattern : urlPatterns) { + String name = context.findServletMapping(urlPattern); +@@ -214,5 +221,5 @@ public class ApplicationServletRegistrat + public String getRunAsRole() { + return wrapper.getRunAs(); + } +- ++ + } +diff -up java/org/apache/catalina/core/StandardContext.java.orig java/org/apache/catalina/core/StandardContext.java +--- java/org/apache/catalina/core/StandardContext.java.orig 2017-03-09 08:51:39.000000000 -0500 ++++ java/org/apache/catalina/core/StandardContext.java 2019-02-28 15:44:09.132930242 -0500 +@@ -4813,27 +4813,36 @@ public class StandardContext extends Con + } + + /** +- * hook to register that we need to scan for security annotations. +- * @param wrapper The wrapper for the Servlet that was added ++ * Create a servlet registration. ++ * ++ * @param wrapper The wrapper for which the registration should be created. ++ * ++ * @return An appropriate registration ++ * ++ * @deprecated This will be removed in Tomcat 9. The registration should be ++ * created directly. + */ ++ @Deprecated + public ServletRegistration.Dynamic dynamicServletAdded(Wrapper wrapper) { +- Servlet s = wrapper.getServlet(); +- if (s != null && createdServlets.contains(s)) { +- // Mark the wrapper to indicate annotations need to be scanned +- wrapper.setServletSecurityAnnotationScanRequired(true); +- } + return new ApplicationServletRegistration(wrapper, this); + } + + /** +- * hook to track which registrations need annotation scanning +- * @param servlet ++ * Hook to track which Servlets were created via ++ * {@link ServletContext#createServlet(Class)}. ++ * ++ * @param servlet the created Servlet + */ + public void dynamicServletCreated(Servlet servlet) { + createdServlets.add(servlet); + } + + ++ public boolean wasCreatedDynamicServlet(Servlet servlet) { ++ return createdServlets.contains(servlet); ++ } ++ ++ + /** + * A helper class to manage the filter mappings in a Context. + */ +diff -up java/org/apache/catalina/core/StandardWrapper.java.orig java/org/apache/catalina/core/StandardWrapper.java +--- java/org/apache/catalina/core/StandardWrapper.java.orig 2019-02-28 15:41:52.576346004 -0500 ++++ java/org/apache/catalina/core/StandardWrapper.java 2019-02-28 15:44:09.133930239 -0500 +@@ -5,17 +5,15 @@ + * 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.io.PrintStream; +@@ -44,11 +42,9 @@ import javax.servlet.ServletContext; + import javax.servlet.ServletException; + import javax.servlet.ServletRequest; + import javax.servlet.ServletResponse; +-import javax.servlet.ServletSecurityElement; + import javax.servlet.SingleThreadModel; + import javax.servlet.UnavailableException; + import javax.servlet.annotation.MultipartConfig; +-import javax.servlet.annotation.ServletSecurity; + + import org.apache.catalina.Container; + import org.apache.catalina.ContainerServlet; +@@ -114,12 +110,12 @@ public class StandardWrapper extends Con + * servlet is considered permanent. + */ + protected long available = 0L; +- ++ + /** +- * The broadcaster that sends j2ee notifications. ++ * The broadcaster that sends j2ee notifications. + */ + protected NotificationBroadcasterSupport broadcaster = null; +- ++ + /** + * The count of allocations that are currently active (even if they + * are for the same instance, as will be true on a non-STM servlet). +@@ -231,12 +227,12 @@ public class StandardWrapper extends Con + */ + protected Stack instancePool = null; + +- ++ + /** + * Wait time for servlet unload in ms. + */ + protected long unloadDelay = 2000; +- ++ + + /** + * True if this StandardWrapper is for the JspServlet +@@ -259,12 +255,12 @@ public class StandardWrapper extends Con + protected StandardWrapperValve swValve; + protected long loadTime=0; + protected int classLoadTime=0; +- ++ + /** + * Multipart config + */ + protected MultipartConfigElement multipartConfigElement = null; +- ++ + /** + * Async support + */ +@@ -275,26 +271,24 @@ public class StandardWrapper extends Con + */ + protected boolean enabled = true; + +- protected volatile boolean servletSecurityAnnotationScanRequired = false; +- + private boolean overridable = false; +- ++ + /** +- * Static class array used when the SecurityManager is turned on and ++ * Static class array used when the SecurityManager is turned on and + * Servlet.init is invoked. + */ + protected static Class[] classType = new Class[]{ServletConfig.class}; +- +- ++ ++ + /** +- * Static class array used when the SecurityManager is turned on and ++ * Static class array used when the SecurityManager is turned on and + * Servlet.service is invoked. +- */ ++ */ + @Deprecated + protected static Class[] classTypeUsedInService = new Class[]{ + ServletRequest.class, + ServletResponse.class}; +- ++ + + private final ReentrantReadWriteLock parametersLock = + new ReentrantReadWriteLock(); +@@ -625,7 +619,7 @@ public class StandardWrapper extends Con + public String[] getServletMethods() throws ServletException { + + instance = loadServlet(); +- ++ + Class servletClazz = instance.getClass(); + if (!javax.servlet.http.HttpServlet.class.isAssignableFrom( + servletClazz)) { +@@ -665,8 +659,8 @@ public class StandardWrapper extends Con + public Servlet getServlet() { + return instance; + } +- +- ++ ++ + /** + * Set the associated servlet instance. + */ +@@ -675,13 +669,13 @@ public class StandardWrapper extends Con + instance = servlet; + } + +- ++ + /** + * {@inheritDoc} + */ + @Override + public void setServletSecurityAnnotationScanRequired(boolean b) { +- this.servletSecurityAnnotationScanRequired = b; ++ // NO-OP + } + + // --------------------------------------------------------- Public Methods +@@ -695,19 +689,19 @@ public class StandardWrapper extends Con + @Override + public void backgroundProcess() { + super.backgroundProcess(); +- ++ + if (!getState().isAvailable()) + return; +- ++ + if (getServlet() != null && (getServlet() instanceof PeriodicEventListener)) { + ((PeriodicEventListener) getServlet()).periodicEvent(); + } + } +- +- ++ ++ + /** + * Extract the root cause from a servlet exception. +- * ++ * + * @param e The servlet exception + */ + public static Throwable getRootCause(ServletException e) { +@@ -836,7 +830,7 @@ public class StandardWrapper extends Con + } + + boolean newInstance = false; +- ++ + // If not SingleThreadedModel, return the same instance every time + if (!singleThreadModel) { + // Load and initialize our instance if necessary +@@ -1070,7 +1064,7 @@ public class StandardWrapper extends Con + @Override + public synchronized void load() throws ServletException { + instance = loadServlet(); +- ++ + if (!instanceInitialized) { + initServlet(instance); + } +@@ -1078,12 +1072,12 @@ public class StandardWrapper extends Con + if (isJspServlet) { + StringBuilder oname = + new StringBuilder(MBeanUtils.getDomain(getParent())); +- ++ + oname.append(":type=JspMonitor,name="); + oname.append(getName()); +- ++ + oname.append(getWebModuleKeyProperties()); +- ++ + try { + jspMonitorON = new ObjectName(oname.toString()); + Registry.getRegistry(null, null) +@@ -1161,8 +1155,6 @@ public class StandardWrapper extends Con + } + } + +- processServletSecurityAnnotation(servlet.getClass()); +- + // Special handling for ContainerServlet instances + if ((servlet instanceof ContainerServlet) && + (isContainerProvidedServlet(servletClass) || +@@ -1205,44 +1197,13 @@ public class StandardWrapper extends Con + */ + @Override + public void servletSecurityAnnotationScan() throws ServletException { +- if (getServlet() == null) { +- Class clazz = null; +- try { +- clazz = getParent().getLoader().getClassLoader().loadClass( +- getServletClass()); +- processServletSecurityAnnotation(clazz); +- } catch (ClassNotFoundException e) { +- // Safe to ignore. No class means no annotations to process +- } +- } else { +- if (servletSecurityAnnotationScanRequired) { +- processServletSecurityAnnotation(getServlet().getClass()); +- } +- } ++ // NO-OP + } + +- private void processServletSecurityAnnotation(Class clazz) { +- // Calling this twice isn't harmful so no syncs +- servletSecurityAnnotationScanRequired = false; +- +- Context ctxt = (Context) getParent(); +- +- if (ctxt.getIgnoreAnnotations()) { +- return; +- } +- +- ServletSecurity secAnnotation = +- clazz.getAnnotation(ServletSecurity.class); +- if (secAnnotation != null) { +- ctxt.addServletSecurity( +- new ApplicationServletRegistration(this, ctxt), +- new ServletSecurityElement(secAnnotation)); +- } +- } + + private synchronized void initServlet(Servlet servlet) + throws ServletException { +- ++ + if (instanceInitialized && !singleThreadModel) return; + + // Call the initialization method of this servlet +@@ -1454,12 +1415,12 @@ public class StandardWrapper extends Con + if (swallowOutput) { + SystemLogHandler.startCapture(); + } +- ++ + // Call the servlet destroy() method + try { + instanceSupport.fireInstanceEvent + (InstanceEvent.BEFORE_DESTROY_EVENT, instance); +- ++ + if( Globals.IS_SECURITY_ENABLED) { + try { + SecurityUtil.doAsPrivilege("destroy", +@@ -1470,7 +1431,7 @@ public class StandardWrapper extends Con + } else { + instance.destroy(); + } +- ++ + instanceSupport.fireInstanceEvent + (InstanceEvent.AFTER_DESTROY_EVENT, instance); + +@@ -1701,7 +1662,7 @@ public class StandardWrapper extends Con + public boolean isAsyncSupported() { + return asyncSupported; + } +- ++ + @Override + public void setAsyncSupported(boolean asyncSupported) { + this.asyncSupported = asyncSupported; +@@ -1711,7 +1672,7 @@ public class StandardWrapper extends Con + public boolean isEnabled() { + return enabled; + } +- ++ + @Override + public void setEnabled(boolean enabled) { + this.enabled = enabled; +@@ -1787,24 +1748,24 @@ public class StandardWrapper extends Con + */ + @Override + protected synchronized void startInternal() throws LifecycleException { +- +- // Send j2ee.state.starting notification ++ ++ // Send j2ee.state.starting notification + if (this.getObjectName() != null) { +- Notification notification = new Notification("j2ee.state.starting", +- this.getObjectName(), ++ Notification notification = new Notification("j2ee.state.starting", ++ this.getObjectName(), + sequenceNumber++); + broadcaster.sendNotification(notification); + } +- ++ + // Start up this component + super.startInternal(); + + setAvailable(0L); + +- // Send j2ee.state.running notification ++ // Send j2ee.state.running notification + if (this.getObjectName() != null) { +- Notification notification = +- new Notification("j2ee.state.running", this.getObjectName(), ++ Notification notification = ++ new Notification("j2ee.state.running", this.getObjectName(), + sequenceNumber++); + broadcaster.sendNotification(notification); + } +@@ -1823,15 +1784,15 @@ public class StandardWrapper extends Con + protected synchronized void stopInternal() throws LifecycleException { + + setAvailable(Long.MAX_VALUE); +- +- // Send j2ee.state.stopping notification ++ ++ // Send j2ee.state.stopping notification + if (this.getObjectName() != null) { +- Notification notification = +- new Notification("j2ee.state.stopping", this.getObjectName(), ++ Notification notification = ++ new Notification("j2ee.state.stopping", this.getObjectName(), + sequenceNumber++); + broadcaster.sendNotification(notification); + } +- ++ + // Shut down our servlet instance (if it has been initialized) + try { + unload(); +@@ -1843,43 +1804,43 @@ public class StandardWrapper extends Con + // Shut down this component + super.stopInternal(); + +- // Send j2ee.state.stopped notification ++ // Send j2ee.state.stopped notification + if (this.getObjectName() != null) { +- Notification notification = +- new Notification("j2ee.state.stopped", this.getObjectName(), ++ Notification notification = ++ new Notification("j2ee.state.stopped", this.getObjectName(), + sequenceNumber++); + broadcaster.sendNotification(notification); + } +- +- // Send j2ee.object.deleted notification +- Notification notification = +- new Notification("j2ee.object.deleted", this.getObjectName(), ++ ++ // Send j2ee.object.deleted notification ++ Notification notification = ++ new Notification("j2ee.object.deleted", this.getObjectName(), + sequenceNumber++); + broadcaster.sendNotification(notification); + + } + +- ++ + @Override + protected String getObjectNameKeyProperties() { + + StringBuilder keyProperties = + new StringBuilder("j2eeType=Servlet,name="); +- ++ + String name = getName(); + if (Util.objectNameValueNeedsQuote(name)) { + name = ObjectName.quote(name); + } + keyProperties.append(name); +- ++ + keyProperties.append(getWebModuleKeyProperties()); + + return keyProperties.toString(); + } +- ++ + + private String getWebModuleKeyProperties() { +- ++ + StringBuilder keyProperties = new StringBuilder(",WebModule=//"); + String hostName = getParent().getParent().getName(); + if (hostName == null) { +@@ -1887,7 +1848,7 @@ public class StandardWrapper extends Con + } else { + keyProperties.append(hostName); + } +- ++ + String contextName = ((Context) getParent()).getName(); + if (!contextName.startsWith("/")) { + keyProperties.append('/'); +@@ -1898,7 +1859,7 @@ public class StandardWrapper extends Con + if (parent instanceof StandardContext) { + ctx = (StandardContext) getParent(); + } +- ++ + keyProperties.append(",J2EEApplication="); + if (ctx == null) { + keyProperties.append("none"); +@@ -1911,10 +1872,10 @@ public class StandardWrapper extends Con + } else { + keyProperties.append(ctx.getJ2EEServer()); + } +- ++ + return keyProperties.toString(); + } +- ++ + + /** + * JSR 77. Always return false. +@@ -1922,19 +1883,19 @@ public class StandardWrapper extends Con + public boolean isStateManageable() { + return false; + } +- + +- /* Remove a JMX notificationListener ++ ++ /* Remove a JMX notificationListener + * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) + */ + @Override +- public void removeNotificationListener(NotificationListener listener, ++ public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, Object object) throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener,filter,object); + } +- ++ + protected MBeanNotificationInfo[] notificationInfo; +- ++ + /* Get JMX Broadcaster Info + * @TODO use StringManager for international support! + * @TODO This two events we not send j2ee.state.failed and j2ee.attribute.changed! +@@ -1949,7 +1910,7 @@ public class StandardWrapper extends Con + "j2ee.object.created"}, + Notification.class.getName(), + "servlet is created" +- ), ++ ), + new MBeanNotificationInfo(new String[] { + "j2ee.state.starting"}, + Notification.class.getName(), +@@ -1980,37 +1941,37 @@ public class StandardWrapper extends Con + + return notificationInfo; + } +- +- ++ ++ + /* Add a JMX-NotificationListener + * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) + */ + @Override +- public void addNotificationListener(NotificationListener listener, ++ public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, Object object) throws IllegalArgumentException { + broadcaster.addNotificationListener(listener,filter,object); + } +- +- ++ ++ + /** +- * Remove a JMX-NotificationListener ++ * Remove a JMX-NotificationListener + * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener) + */ + @Override +- public void removeNotificationListener(NotificationListener listener) ++ public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener); + } +- +- ++ ++ + // ------------------------------------------------------------- Attributes +- +- ++ ++ + @Deprecated + public boolean isEventProvider() { + return false; + } +- ++ + @Deprecated + public boolean isStatisticsProvider() { + return false; +diff -up java/org/apache/catalina/startup/ContextConfig.java.orig java/org/apache/catalina/startup/ContextConfig.java +--- java/org/apache/catalina/startup/ContextConfig.java.orig 2019-02-28 15:41:52.580345992 -0500 ++++ java/org/apache/catalina/startup/ContextConfig.java 2019-02-28 15:44:09.134930236 -0500 +@@ -433,15 +433,14 @@ public class ContextConfig implements Li + LoginConfig loginConfig = context.getLoginConfig(); + + SecurityConstraint constraints[] = context.findConstraints(); +- if (context.getIgnoreAnnotations() && +- (constraints == null || constraints.length ==0) && ++ if ((constraints == null || constraints.length ==0) && + !context.getPreemptiveAuthentication()) { ++ // No need for an authenticator + return; + } else { + if (loginConfig == null) { +- // Not metadata-complete or security constraints present, need +- // an authenticator to support @ServletSecurity annotations +- // and/or constraints ++ // Security constraints present. Need an authenticator to ++ // support them. + loginConfig = DUMMY_LOGIN_CONFIG; + context.setLoginConfig(loginConfig); + } +diff -up java/org/apache/catalina/startup/Tomcat.java.orig java/org/apache/catalina/startup/Tomcat.java +--- java/org/apache/catalina/startup/Tomcat.java.orig 2019-02-28 15:44:09.135930233 -0500 ++++ java/org/apache/catalina/startup/Tomcat.java 2019-02-28 15:47:52.376250650 -0500 +@@ -928,6 +928,9 @@ public class Tomcat { + Context context = (Context) event.getLifecycle(); + if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { + context.setConfigured(true); ++ ++ // Process annotations ++ WebAnnotationSet.loadApplicationAnnotations(context); + } + // LoginConfig is required to process @ServletSecurity + // annotations +diff -up java/org/apache/catalina/startup/WebAnnotationSet.java.orig java/org/apache/catalina/startup/WebAnnotationSet.java +--- java/org/apache/catalina/startup/WebAnnotationSet.java.orig 2017-03-09 08:51:40.000000000 -0500 ++++ java/org/apache/catalina/startup/WebAnnotationSet.java 2019-02-28 15:47:07.249388035 -0500 +@@ -23,10 +23,13 @@ import javax.annotation.Resource; + import javax.annotation.Resources; + import javax.annotation.security.DeclareRoles; + import javax.annotation.security.RunAs; ++import javax.servlet.ServletSecurityElement; ++import javax.servlet.annotation.ServletSecurity; + + import org.apache.catalina.Container; + import org.apache.catalina.Context; + import org.apache.catalina.Wrapper; ++import org.apache.catalina.core.ApplicationServletRegistration; + import org.apache.catalina.deploy.ContextEnvironment; + import org.apache.catalina.deploy.ContextResource; + import org.apache.catalina.deploy.ContextResourceEnvRef; +@@ -143,9 +146,17 @@ public class WebAnnotationSet { + * Ref JSR 250, equivalent to the run-as element in + * the deployment descriptor + */ +- RunAs annotation = classClass.getAnnotation(RunAs.class); +- if (annotation != null) { +- wrapper.setRunAs(annotation.value()); ++ RunAs runAs = classClass.getAnnotation(RunAs.class); ++ if (runAs != null) { ++ wrapper.setRunAs(runAs.value()); ++ } ++ ++ // Process ServletSecurity annotation ++ ServletSecurity servletSecurity = classClass.getAnnotation(ServletSecurity.class); ++ if (servletSecurity != null) { ++ context.addServletSecurity( ++ new ApplicationServletRegistration(wrapper, context), ++ new ServletSecurityElement(servletSecurity)); + } + } + } +diff -up java/org/apache/catalina/Wrapper.java.orig java/org/apache/catalina/Wrapper.java +--- java/org/apache/catalina/Wrapper.java.orig 2019-02-28 15:41:52.554346071 -0500 ++++ java/org/apache/catalina/Wrapper.java 2019-02-28 15:44:09.129930251 -0500 +@@ -370,19 +370,23 @@ public interface Wrapper extends Contain + public void setEnabled(boolean enabled); + + /** +- * Set the flag that indicates +- * {@link javax.servlet.annotation.ServletSecurity} annotations must be +- * scanned when the Servlet is first used. ++ * This method is no longer used. All implementations should be NO-OPs. + * +- * @param b The new value of the flag ++ * @param b Unused. ++ * ++ * @deprecated This will be removed in Tomcat 9. + */ ++ @Deprecated + public void setServletSecurityAnnotationScanRequired(boolean b); + + /** +- * Scan for (if necessary) and process (if found) the +- * {@link javax.servlet.annotation.ServletSecurity} annotations for the +- * Servlet associated with this wrapper. ++ * This method is no longer used. All implementations should be NO-OPs. ++ * ++ * @throws ServletException Never thrown ++ * ++ * @deprecated This will be removed in Tomcat 9. + */ ++ @Deprecated + public void servletSecurityAnnotationScan() throws ServletException; + + /** +diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml +--- webapps/docs/changelog.xml.orig 2019-02-28 15:41:52.591345958 -0500 ++++ webapps/docs/changelog.xml 2019-02-28 15:44:09.142930212 -0500 +@@ -68,6 +68,11 @@ + 62067: Correctly apply security constraints mapped to the + context root using a URL pattern of "". (markt) + ++ ++ Process all ServletSecurity annotations at web application ++ start rather than at servlet load time to ensure constraints are applied ++ consistently. (markt) ++ + + + +commit c63b96d72cd39287e17b2ba698f4eee0ba508073 +Author: Mark Thomas +Date: Thu Feb 15 20:21:56 2018 +0000 + + Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62104 + Fix programmatic login regression as the NonLoginAuthenticator has to be set for it to work (if no login method is specified). + + git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1824360 13f79535-47bb-0310-9956-ffa450edef68 + +diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java +index d913b0e489..2d8aae0ff1 100644 +--- java/org/apache/catalina/startup/ContextConfig.java ++++ java/org/apache/catalina/startup/ContextConfig.java +@@ -431,19 +431,10 @@ public class ContextConfig implements LifecycleListener { + protected void authenticatorConfig() { + + LoginConfig loginConfig = context.getLoginConfig(); +- +- SecurityConstraint constraints[] = context.findConstraints(); +- if ((constraints == null || constraints.length ==0) && +- !context.getPreemptiveAuthentication()) { +- // No need for an authenticator +- return; +- } else { +- if (loginConfig == null) { +- // Security constraints present. Need an authenticator to +- // support them. +- loginConfig = DUMMY_LOGIN_CONFIG; +- context.setLoginConfig(loginConfig); +- } ++ if (loginConfig == null) { ++ // Need an authenticator to support HttpServletRequest.login() ++ loginConfig = DUMMY_LOGIN_CONFIG; ++ context.setLoginConfig(loginConfig); + } + + // Has an authenticator been configured already? +diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml +--- webapps/docs/changelog.xml.orig 2019-02-28 16:12:24.743968359 -0500 ++++ webapps/docs/changelog.xml 2019-02-28 16:12:39.412925943 -0500 +@@ -73,6 +73,11 @@ + start rather than at servlet load time to ensure constraints are applied + consistently. (markt) + ++ ++ 62104: Fix programmatic login regression as the ++ NonLoginAuthenticator has to be set for it to work (if no login method ++ is specified). (remm) ++ + + + diff --git a/SOURCES/tomcat-7.0.76-CVE-2018-8014.patch b/SOURCES/tomcat-7.0.76-CVE-2018-8014.patch new file mode 100644 index 0000000..35f179d --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2018-8014.patch @@ -0,0 +1,280 @@ +commit 5877390a9605f56d9bd6859a54ccbfb16374a78b +Author: Mark Thomas +Date: Wed May 16 14:56:34 2018 +0000 + + Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62343 + Make CORS filter defaults more secure. + This is the fix for CVE-2018-8014. + + git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1831730 13f79535-47bb-0310-9956-ffa450edef68 + +diff -up java/org/apache/catalina/filters/CorsFilter.java.orig java/org/apache/catalina/filters/CorsFilter.java +--- java/org/apache/catalina/filters/CorsFilter.java.orig 2019-02-28 16:37:12.512591576 -0500 ++++ java/org/apache/catalina/filters/CorsFilter.java 2019-02-28 16:39:43.449141647 -0500 +@@ -260,17 +260,14 @@ public class CorsFilter implements Filte + + // Section 6.1.3 + // Add a single Access-Control-Allow-Origin header. +- if (anyOriginAllowed && !supportsCredentials) { +- // If resource doesn't support credentials and if any origin is +- // allowed +- // to make CORS request, return header with '*'. ++ if (anyOriginAllowed) { ++ // If any origin is allowed, return header with '*'. + response.addHeader( + CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, + "*"); + } else { +- // If the resource supports credentials add a single +- // Access-Control-Allow-Origin header, with the value of the Origin +- // header as value. ++ // Add a single Access-Control-Allow-Origin header, with the value ++ // of the Origin header as value. + response.addHeader( + CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, + origin); +@@ -799,6 +796,10 @@ public class CorsFilter implements Filte + .parseBoolean(supportsCredentials); + } + ++ if (this.supportsCredentials && this.anyOriginAllowed) { ++ throw new ServletException(sm.getString("corsFilter.invalidSupportsCredentials")); ++ } ++ + if (preflightMaxAge != null) { + try { + if (!preflightMaxAge.isEmpty()) { +@@ -1156,7 +1156,7 @@ public class CorsFilter implements Filte + /** + * By default, all origins are allowed to make requests. + */ +- public static final String DEFAULT_ALLOWED_ORIGINS = "*"; ++ public static final String DEFAULT_ALLOWED_ORIGINS = ""; + + /** + * By default, following methods are supported: GET, POST, HEAD and OPTIONS. +@@ -1172,7 +1172,7 @@ public class CorsFilter implements Filte + /** + * By default, support credentials is turned on. + */ +- public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true"; ++ public static final String DEFAULT_SUPPORTS_CREDENTIALS = "false"; + + /** + * By default, following headers are supported: +diff --git a/java/org/apache/catalina/filters/LocalStrings.properties b/java/org/apache/catalina/filters/LocalStrings.properties +index 5c4c792f82..921f101456 100644 +--- java/org/apache/catalina/filters/LocalStrings.properties ++++ java/org/apache/catalina/filters/LocalStrings.properties +@@ -14,6 +14,8 @@ + # limitations under the License. + + addDefaultCharset.unsupportedCharset=Specified character set [{0}] is not supported ++ ++corsFilter.invalidSupportsCredentials=It is not allowed to configure supportsCredentials=[true] when allowedOrigins=[*] + corsFilter.invalidPreflightMaxAge=Unable to parse preflightMaxAge + corsFilter.nullRequest=HttpServletRequest object is null + corsFilter.nullRequestType=CORSRequestType object is null +diff --git a/test/org/apache/catalina/filters/TestCorsFilter.java b/test/org/apache/catalina/filters/TestCorsFilter.java +index 06634fbb57..47d520a08e 100644 +--- test/org/apache/catalina/filters/TestCorsFilter.java ++++ test/org/apache/catalina/filters/TestCorsFilter.java +@@ -52,8 +52,7 @@ public class TestCorsFilter { + corsFilter.doFilter(request, response, filterChain); + + Assert.assertTrue(response.getHeader( +- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( +- "https://www.apache.org")); ++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*")); + Assert.assertTrue(((Boolean) request.getAttribute( + CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue()); + Assert.assertTrue(request.getAttribute( +@@ -85,8 +84,7 @@ public class TestCorsFilter { + corsFilter.doFilter(request, response, filterChain); + + Assert.assertTrue(response.getHeader( +- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( +- "https://www.apache.org")); ++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*")); + Assert.assertTrue(((Boolean) request.getAttribute( + CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue()); + Assert.assertTrue(request.getAttribute( +@@ -117,8 +115,7 @@ public class TestCorsFilter { + corsFilter.doFilter(request, response, filterChain); + + Assert.assertTrue(response.getHeader( +- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( +- "https://www.apache.org")); ++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*")); + Assert.assertTrue(((Boolean) request.getAttribute( + CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue()); + Assert.assertTrue(request.getAttribute( +@@ -163,41 +160,15 @@ public class TestCorsFilter { + } + + /** +- * Tests the presence of the origin (and not '*') in the response, when +- * supports credentials is enabled alongwith any origin, '*'. ++ * Tests the that supports credentials may not be enabled with any origin, ++ * '*'. + * +- * @throws IOException + * @throws ServletException + */ +- @Test +- public void testDoFilterSimpleAnyOriginAndSupportsCredentials() +- throws IOException, ServletException { +- TesterHttpServletRequest request = new TesterHttpServletRequest(); +- request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, +- TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); +- request.setMethod("GET"); +- TesterHttpServletResponse response = new TesterHttpServletResponse(); +- ++ @Test(expected=ServletException.class) ++ public void testDoFilterSimpleAnyOriginAndSupportsCredentials() throws ServletException { + CorsFilter corsFilter = new CorsFilter(); +- corsFilter.init(TesterFilterConfigs +- .getFilterConfigAnyOriginAndSupportsCredentials()); +- corsFilter.doFilter(request, response, filterChain); +- +- Assert.assertTrue(response.getHeader( +- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( +- TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); +- Assert.assertTrue(response.getHeader( +- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS) +- .equals( +- "true")); +- Assert.assertTrue(((Boolean) request.getAttribute( +- CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue()); +- Assert.assertTrue(request.getAttribute( +- CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( +- TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); +- Assert.assertTrue(request.getAttribute( +- CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( +- CorsFilter.CORSRequestType.SIMPLE.name().toLowerCase(Locale.ENGLISH))); ++ corsFilter.init(TesterFilterConfigs.getFilterConfigAnyOriginAndSupportsCredentials()); + } + + /** +@@ -258,8 +229,7 @@ public class TestCorsFilter { + corsFilter.doFilter(request, response, filterChain); + + Assert.assertTrue(response.getHeader( +- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( +- "https://www.apache.org")); ++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*")); + Assert.assertTrue(response.getHeader( + CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS) + .equals(TesterFilterConfigs.EXPOSED_HEADERS)); +@@ -707,9 +677,8 @@ public class TestCorsFilter { + corsFilter.init(null); + corsFilter.doFilter(request, response, filterChain); + +- Assert.assertTrue(response.getHeader( +- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( +- "https://www.apache.org")); ++ Assert.assertNull(response.getHeader( ++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN)); + Assert.assertTrue(((Boolean) request.getAttribute( + CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue()); + Assert.assertTrue(request.getAttribute( +@@ -1401,7 +1370,7 @@ public class TestCorsFilter { + Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0); + Assert.assertTrue(corsFilter.isAnyOriginAllowed()); + Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0); +- Assert.assertTrue(corsFilter.isSupportsCredentials()); ++ Assert.assertFalse(corsFilter.isSupportsCredentials()); + Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800); + } + +@@ -1437,9 +1406,9 @@ public class TestCorsFilter { + Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6); + Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4); + Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0); +- Assert.assertTrue(corsFilter.isAnyOriginAllowed()); ++ Assert.assertFalse(corsFilter.isAnyOriginAllowed()); + Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0); +- Assert.assertTrue(corsFilter.isSupportsCredentials()); ++ Assert.assertFalse(corsFilter.isSupportsCredentials()); + Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800); + } + +@@ -1543,8 +1512,7 @@ public class TestCorsFilter { + corsFilter.doFilter(request, response, filterChain); + + Assert.assertTrue(response.getHeader( +- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( +- "https://www.apache.org")); ++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*")); + Assert.assertNull(request + .getAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)); + Assert.assertNull(request +diff --git a/test/org/apache/catalina/filters/TesterFilterConfigs.java b/test/org/apache/catalina/filters/TesterFilterConfigs.java +index 941d8949d7..cb3d04f813 100644 +--- test/org/apache/catalina/filters/TesterFilterConfigs.java ++++ test/org/apache/catalina/filters/TesterFilterConfigs.java +@@ -36,12 +36,13 @@ public class TesterFilterConfigs { + public static final TesterServletContext mockServletContext = + new TesterServletContext(); + ++ // Default config for the test is to allow any origin + public static FilterConfig getDefaultFilterConfig() { + final String allowedHttpHeaders = + CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS; + final String allowedHttpMethods = + CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS; +- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS; ++ final String allowedOrigins = ANY_ORIGIN; + final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS; + final String supportCredentials = + CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS; +@@ -59,7 +60,7 @@ public class TesterFilterConfigs { + CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS; + final String allowedHttpMethods = + CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT"; +- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS; ++ final String allowedOrigins = ANY_ORIGIN; + final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS; + final String supportCredentials = "true"; + final String preflightMaxAge = +@@ -77,7 +78,7 @@ public class TesterFilterConfigs { + CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS; + final String allowedHttpMethods = + CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT"; +- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS; ++ final String allowedOrigins = ANY_ORIGIN; + final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS; + final String supportCredentials = "false"; + final String preflightMaxAge = +@@ -131,7 +132,7 @@ public class TesterFilterConfigs { + CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS; + final String allowedHttpMethods = + CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS; +- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS; ++ final String allowedOrigins = ANY_ORIGIN; + final String exposedHeaders = EXPOSED_HEADERS; + final String supportCredentials = + CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS; +@@ -240,7 +241,7 @@ public class TesterFilterConfigs { + CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS; + final String allowedHttpMethods = + CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS; +- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS; ++ final String allowedOrigins = ANY_ORIGIN; + final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS; + final String supportCredentials = + CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS; +diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml +--- webapps/docs/changelog.xml.orig 2019-02-28 16:33:00.254343407 -0500 ++++ webapps/docs/changelog.xml 2019-02-28 16:33:24.135272232 -0500 +@@ -78,6 +78,10 @@ + NonLoginAuthenticator has to be set for it to work (if no login method + is specified). (remm) + ++ ++ 62343: Make CORS filter defaults more secure. This is the fix ++ for CVE-2018-8014. (markt) ++ + + + diff --git a/SOURCES/tomcat-7.0.76-CVE-2018-8034.patch b/SOURCES/tomcat-7.0.76-CVE-2018-8034.patch new file mode 100644 index 0000000..68035e1 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2018-8034.patch @@ -0,0 +1,109 @@ +commit 2c522795166c930741a9cecca76797bf48cb1634 +Author: Mark Thomas +Date: Mon Jun 18 19:45:13 2018 +0000 + + Enable host name verification for secure WebSocket client connections by default. + + git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1833760 13f79535-47bb-0310-9956-ffa450edef68 + +diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java +index ac5122a89c..b526022958 100644 +--- java/org/apache/tomcat/websocket/WsWebSocketContainer.java ++++ java/org/apache/tomcat/websocket/WsWebSocketContainer.java +@@ -53,6 +53,7 @@ import java.util.concurrent.TimeoutException; + import javax.net.ssl.SSLContext; + import javax.net.ssl.SSLEngine; + import javax.net.ssl.SSLException; ++import javax.net.ssl.SSLParameters; + import javax.net.ssl.TrustManagerFactory; + import javax.websocket.ClientEndpoint; + import javax.websocket.ClientEndpointConfig; +@@ -363,7 +364,7 @@ public class WsWebSocketContainer + // proxy CONNECT, need to use TLS from this point on so wrap the + // original AsynchronousSocketChannel + SSLEngine sslEngine = createSSLEngine( +- clientEndpointConfiguration.getUserProperties()); ++ clientEndpointConfiguration.getUserProperties(), host, port); + channel = new AsyncChannelWrapperSecure(socketChannel, sslEngine); + } else if (channel == null) { + // Only need to wrap as this point if it wasn't wrapped to process a +@@ -931,7 +932,7 @@ public class WsWebSocketContainer + } + + +- private SSLEngine createSSLEngine(Map userProperties) ++ private SSLEngine createSSLEngine(Map userProperties, String host, int port) + throws DeploymentException { + + try { +@@ -979,7 +980,7 @@ public class WsWebSocketContainer + } + } + +- SSLEngine engine = sslContext.createSSLEngine(); ++ SSLEngine engine = sslContext.createSSLEngine(host, port); + + String sslProtocolsValue = + (String) userProperties.get(SSL_PROTOCOLS_PROPERTY); +@@ -989,6 +990,14 @@ public class WsWebSocketContainer + + engine.setUseClientMode(true); + ++ // Enable host verification ++ // Start with current settings (returns a copy) ++ SSLParameters sslParams = engine.getSSLParameters(); ++ // Use HTTPS since WebSocket starts over HTTP(S) ++ sslParams.setEndpointIdentificationAlgorithm("HTTPS"); ++ // Write the parameters back ++ engine.setSSLParameters(sslParams); ++ + return engine; + } catch (Exception e) { + throw new DeploymentException(sm.getString( +diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml +--- webapps/docs/changelog.xml.orig 2019-03-01 09:38:54.202817893 -0500 ++++ webapps/docs/changelog.xml 2019-03-01 09:39:29.413704569 -0500 +@@ -84,6 +84,14 @@ + + + ++ ++ ++ ++ Enable host name verification when using TLS with the WebSocket client. ++ (markt) ++ ++ ++ + +
+ +diff -up webapps/docs/web-socket-howto.xml.orig webapps/docs/web-socket-howto.xml +--- webapps/docs/web-socket-howto.xml.orig 2019-03-01 12:46:34.515904379 -0500 ++++ webapps/docs/web-socket-howto.xml 2019-03-01 12:51:00.900175575 -0500 +@@ -148,10 +148,21 @@ implement its own timeout mechanism to h +
  • org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD
  • + +

    The default truststore password is changeit.

    +-

    If the org.apache.tomcat.websocket.SSL_CONTEXT property is +- set then the org.apache.tomcat.websocket.SSL_TRUSTSTORE and +- org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD properties +- will be ignored.

    ++ ++

    If the org.apache.tomcat.websocket.SSL_CONTEXT property is ++ set then the org.apache.tomcat.websocket.SSL_TRUSTSTORE and ++ org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD properties ++ will be ignored.

    ++ ++

    For secure server end points, host name verification is enabled by default. ++ To bypass this verification (not recommended), it is necessary to provide a ++ custom SSLContext via the ++ org.apache.tomcat.websocket.SSL_CONTEXT user property. The ++ custom SSLContext must be configured with a custom ++ TrustManager that extends ++ javax.net.ssl.X509ExtendedTrustManager. The desired verification ++ (or lack of verification) can then be controlled by appropriate ++ implementations of the individual abstract methods.

    +
    + +
    diff --git a/SOURCES/tomcat-7.0.76-rhbz-1455483.patch b/SOURCES/tomcat-7.0.76-rhbz-1455483.patch new file mode 100644 index 0000000..cb1c256 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-rhbz-1455483.patch @@ -0,0 +1,828 @@ +diff -up java/org/apache/coyote/http11/AbstractHttp11Processor.java.orig java/org/apache/coyote/http11/AbstractHttp11Processor.java +--- java/org/apache/coyote/http11/AbstractHttp11Processor.java.orig 2017-03-09 08:51:40.000000000 -0500 ++++ java/org/apache/coyote/http11/AbstractHttp11Processor.java 2019-03-05 14:58:20.285295932 -0500 +@@ -48,6 +48,7 @@ import org.apache.tomcat.util.buf.HexUti + import org.apache.tomcat.util.buf.MessageBytes; + import org.apache.tomcat.util.http.FastHttpDateFormat; + import org.apache.tomcat.util.http.MimeHeaders; ++import org.apache.tomcat.util.http.parser.HttpParser; + import org.apache.tomcat.util.log.UserDataHelper; + import org.apache.tomcat.util.net.AbstractEndpoint; + import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; +@@ -262,6 +263,9 @@ public abstract class AbstractHttp11Proc + protected org.apache.coyote.http11.upgrade.UpgradeInbound upgradeInbound = null; + + ++ protected HttpParser httpParser; ++ ++ + /** + * Instance of the new protocol to use after the HTTP connection has been + * upgraded using the Servlet 3.1 based upgrade process. +@@ -1301,33 +1305,62 @@ public abstract class AbstractHttp11Proc + } + } + +- // Check for a full URI (including protocol://host:port/) ++ // Check for an absolute-URI less the query string which has already ++ // been removed during the parsing of the request line + ByteChunk uriBC = request.requestURI().getByteChunk(); ++ byte[] uriB = uriBC.getBytes(); + if (uriBC.startsWithIgnoreCase("http", 0)) { + +- int pos = uriBC.indexOf("://", 0, 3, 4); +- int uriBCStart = uriBC.getStart(); +- int slashPos = -1; +- if (pos != -1) { +- byte[] uriB = uriBC.getBytes(); +- slashPos = uriBC.indexOf('/', pos + 3); ++ int pos = 4; ++ // Check for https ++ if (uriBC.startsWithIgnoreCase("s", pos)) { ++ pos++; ++ } ++ // Next 3 characters must be "://" ++ if (uriBC.startsWith("://", pos)) { ++ int uriBCStart = uriBC.getStart(); ++ ++ // '/' does not appear in the authority so use the first ++ // instance to split the authority and the path segments ++ int slashPos = uriBC.indexOf('/', pos); ++ // '@' in the authority delimits the userinfo ++ + if (slashPos == -1) { + slashPos = uriBC.getLength(); +- // Set URI as "/" +- request.requestURI().setBytes +- (uriB, uriBCStart + pos + 1, 1); ++ // Set URI as "/". Use 6 as it will always be a '/'. ++ // 01234567 ++ // http:// ++ // https:// ++ request.requestURI().setBytes(uriB, uriBCStart + 6, 1); + } else { +- request.requestURI().setBytes +- (uriB, uriBCStart + slashPos, +- uriBC.getLength() - slashPos); ++ request.requestURI().setBytes(uriB, uriBCStart + slashPos, uriBC.getLength() - slashPos); + } + MessageBytes hostMB = headers.setValue("host"); + hostMB.setBytes(uriB, uriBCStart + pos + 3, + slashPos - pos - 3); ++ } else { ++ response.setStatus(400); ++ setErrorState(ErrorState.CLOSE_CLEAN, null); ++ if (getLog().isDebugEnabled()) { ++ getLog().debug(sm.getString("http11processor.request.invalidScheme")); ++ } + } + + } + ++ // Validate the characters in the URI. %nn decoding will be checked at ++ // the point of decoding. ++ for (int i = uriBC.getStart(); i < uriBC.getEnd(); i++) { ++ if (!httpParser.isAbsolutePathRelaxed(uriB[i])) { ++ response.setStatus(400); ++ setErrorState(ErrorState.CLOSE_CLEAN, null); ++ if (getLog().isDebugEnabled()) { ++ getLog().debug(sm.getString("http11processor.request.invalidUri")); ++ } ++ break; ++ } ++ } ++ + // Input filter setup + InputFilter[] inputFilters = getInputBuffer().getFilters(); + +@@ -1364,8 +1397,7 @@ public abstract class AbstractHttp11Proc + headers.removeHeader("content-length"); + request.setContentLength(-1); + } else { +- getInputBuffer().addActiveFilter +- (inputFilters[Constants.IDENTITY_FILTER]); ++ getInputBuffer().addActiveFilter(inputFilters[Constants.IDENTITY_FILTER]); + contentDelimitation = true; + } + } +@@ -1383,14 +1415,14 @@ public abstract class AbstractHttp11Proc + } + } + ++ // Validate host name and extract port if present + parseHost(valueMB); + + if (!contentDelimitation) { + // If there's no content length + // (broken HTTP/1.0 or HTTP/1.1), assume + // the client is not broken and didn't send a body +- getInputBuffer().addActiveFilter +- (inputFilters[Constants.VOID_FILTER]); ++ getInputBuffer().addActiveFilter(inputFilters[Constants.VOID_FILTER]); + contentDelimitation = true; + } + +diff -up java/org/apache/coyote/http11/AbstractHttp11Protocol.java.orig java/org/apache/coyote/http11/AbstractHttp11Protocol.java +--- java/org/apache/coyote/http11/AbstractHttp11Protocol.java.orig 2019-03-05 12:14:08.096279991 -0500 ++++ java/org/apache/coyote/http11/AbstractHttp11Protocol.java 2019-03-05 14:03:32.186921274 -0500 +@@ -45,6 +45,23 @@ public abstract class AbstractHttp11Prot + // ------------------------------------------------ HTTP specific properties + // ------------------------------------------ managed in the ProtocolHandler + ++ private String relaxedPathChars = null; ++ public String getRelaxedPathChars() { ++ return relaxedPathChars; ++ } ++ public void setRelaxedPathChars(String relaxedPathChars) { ++ this.relaxedPathChars = relaxedPathChars; ++ } ++ ++ ++ private String relaxedQueryChars = null; ++ public String getRelaxedQueryChars() { ++ return relaxedQueryChars; ++ } ++ public void setRelaxedQueryChars(String relaxedQueryChars) { ++ this.relaxedQueryChars = relaxedQueryChars; ++ } ++ + private int socketBuffer = 9000; + public int getSocketBuffer() { return socketBuffer; } + public void setSocketBuffer(int socketBuffer) { +diff -up java/org/apache/coyote/http11/AbstractInputBuffer.java.orig java/org/apache/coyote/http11/AbstractInputBuffer.java +--- java/org/apache/coyote/http11/AbstractInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500 ++++ java/org/apache/coyote/http11/AbstractInputBuffer.java 2019-03-05 12:14:08.096279991 -0500 +@@ -22,6 +22,7 @@ import org.apache.coyote.InputBuffer; + import org.apache.coyote.Request; + import org.apache.tomcat.util.buf.ByteChunk; + import org.apache.tomcat.util.http.MimeHeaders; ++import org.apache.tomcat.util.http.parser.HttpParser; + import org.apache.tomcat.util.net.AbstractEndpoint; + import org.apache.tomcat.util.net.SocketWrapper; + import org.apache.tomcat.util.res.StringManager; +@@ -108,6 +109,9 @@ public abstract class AbstractInputBuffe + protected int lastActiveFilter; + + ++ protected HttpParser httpParser; ++ ++ + // ------------------------------------------------------------- Properties + + +diff -up java/org/apache/coyote/http11/Http11AprProcessor.java.orig java/org/apache/coyote/http11/Http11AprProcessor.java +--- java/org/apache/coyote/http11/Http11AprProcessor.java.orig 2019-03-05 12:13:47.032344988 -0500 ++++ java/org/apache/coyote/http11/Http11AprProcessor.java 2019-03-05 14:58:20.298295897 -0500 +@@ -35,6 +35,7 @@ import org.apache.tomcat.jni.SSLSocket; + import org.apache.tomcat.jni.Sockaddr; + import org.apache.tomcat.jni.Socket; + import org.apache.tomcat.util.ExceptionUtils; ++import org.apache.tomcat.util.http.parser.HttpParser; + import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; + import org.apache.tomcat.util.net.AprEndpoint; + import org.apache.tomcat.util.net.SSLSupport; +@@ -61,11 +62,15 @@ public class Http11AprProcessor extends + + + public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint, int maxTrailerSize, +- Set allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) { ++ Set allowedTrailerHeaders, ++ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars, ++ String relaxedQueryChars) { + + super(endpoint); + +- inputBuffer = new InternalAprInputBuffer(request, headerBufferSize); ++ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars); ++ ++ inputBuffer = new InternalAprInputBuffer(request, headerBufferSize, httpParser); + request.setInputBuffer(inputBuffer); + + outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize); +diff -up java/org/apache/coyote/http11/Http11AprProtocol.java.orig java/org/apache/coyote/http11/Http11AprProtocol.java +--- java/org/apache/coyote/http11/Http11AprProtocol.java.orig 2019-03-05 12:14:08.097279988 -0500 ++++ java/org/apache/coyote/http11/Http11AprProtocol.java 2019-03-05 13:59:45.131631454 -0500 +@@ -301,7 +301,9 @@ public class Http11AprProtocol extends A + Http11AprProcessor processor = new Http11AprProcessor( + proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint, + proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(), +- proto.getMaxExtensionSize(), proto.getMaxSwallowSize()); ++ proto.getMaxExtensionSize(), ++ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(), ++ proto.getRelaxedQueryChars()); + processor.setAdapter(proto.adapter); + processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); + processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); +diff -up java/org/apache/coyote/http11/Http11NioProcessor.java.orig java/org/apache/coyote/http11/Http11NioProcessor.java +--- java/org/apache/coyote/http11/Http11NioProcessor.java.orig 2019-03-05 12:13:47.033344985 -0500 ++++ java/org/apache/coyote/http11/Http11NioProcessor.java 2019-03-05 13:04:00.335042387 -0500 +@@ -31,6 +31,7 @@ import org.apache.coyote.http11.filters. + import org.apache.juli.logging.Log; + import org.apache.juli.logging.LogFactory; + import org.apache.tomcat.util.ExceptionUtils; ++import org.apache.tomcat.util.http.parser.HttpParser; + import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; + import org.apache.tomcat.util.net.NioChannel; + import org.apache.tomcat.util.net.NioEndpoint; +@@ -66,11 +67,15 @@ public class Http11NioProcessor extends + + + public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint, int maxTrailerSize, +- Set allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) { ++ Set allowedTrailerHeaders, ++ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars, ++ String relaxedQueryChars) { + + super(endpoint); + +- inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize); ++ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars); ++ ++ inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize, httpParser); + request.setInputBuffer(inputBuffer); + + outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize); +diff -up java/org/apache/coyote/http11/Http11NioProtocol.java.orig java/org/apache/coyote/http11/Http11NioProtocol.java +--- java/org/apache/coyote/http11/Http11NioProtocol.java.orig 2019-03-05 12:14:08.098279985 -0500 ++++ java/org/apache/coyote/http11/Http11NioProtocol.java 2019-03-05 14:00:15.034537932 -0500 +@@ -266,7 +266,9 @@ public class Http11NioProtocol extends A + Http11NioProcessor processor = new Http11NioProcessor( + proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint, + proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(), +- proto.getMaxExtensionSize(), proto.getMaxSwallowSize()); ++ proto.getMaxExtensionSize(), ++ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(), ++ proto.getRelaxedQueryChars()); + processor.setAdapter(proto.adapter); + processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); + processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); +diff -up java/org/apache/coyote/http11/Http11Processor.java.orig java/org/apache/coyote/http11/Http11Processor.java +--- java/org/apache/coyote/http11/Http11Processor.java.orig 2017-03-09 08:51:40.000000000 -0500 ++++ java/org/apache/coyote/http11/Http11Processor.java 2019-03-05 14:58:20.306295875 -0500 +@@ -26,6 +26,7 @@ import org.apache.coyote.ActionCode; + import org.apache.coyote.http11.filters.BufferedInputFilter; + import org.apache.juli.logging.Log; + import org.apache.juli.logging.LogFactory; ++import org.apache.tomcat.util.http.parser.HttpParser; + import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; + import org.apache.tomcat.util.net.JIoEndpoint; + import org.apache.tomcat.util.net.SSLSupport; +@@ -51,11 +52,16 @@ public class Http11Processor extends Abs + + + public Http11Processor(int headerBufferSize, JIoEndpoint endpoint, int maxTrailerSize, +- Set allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) { ++ Set allowedTrailerHeaders, ++ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars, ++ String relaxedQueryChars) { + + super(endpoint); ++ ++ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars); ++ ++ inputBuffer = new InternalInputBuffer(request, headerBufferSize, httpParser); + +- inputBuffer = new InternalInputBuffer(request, headerBufferSize); + request.setInputBuffer(inputBuffer); + + outputBuffer = new InternalOutputBuffer(response, headerBufferSize); +diff -up java/org/apache/coyote/http11/Http11Protocol.java.orig java/org/apache/coyote/http11/Http11Protocol.java +--- java/org/apache/coyote/http11/Http11Protocol.java.orig 2019-03-05 12:14:08.099279982 -0500 ++++ java/org/apache/coyote/http11/Http11Protocol.java 2019-03-05 13:02:36.769301263 -0500 +@@ -165,7 +165,9 @@ public class Http11Protocol extends Abst + Http11Processor processor = new Http11Processor( + proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint, + proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(), +- proto.getMaxExtensionSize(), proto.getMaxSwallowSize()); ++ proto.getMaxExtensionSize(), ++ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(), ++ proto.getRelaxedQueryChars()); + processor.setAdapter(proto.adapter); + processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); + processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); +diff -up java/org/apache/coyote/http11/InternalAprInputBuffer.java.orig java/org/apache/coyote/http11/InternalAprInputBuffer.java +--- java/org/apache/coyote/http11/InternalAprInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500 ++++ java/org/apache/coyote/http11/InternalAprInputBuffer.java 2019-03-05 14:58:20.312295859 -0500 +@@ -51,7 +51,8 @@ public class InternalAprInputBuffer exte + /** + * Alternate constructor. + */ +- public InternalAprInputBuffer(Request request, int headerBufferSize) { ++ public InternalAprInputBuffer(Request request, int headerBufferSize, ++ HttpParser httpParser) { + + this.request = request; + headers = request.getMimeHeaders(); +@@ -63,6 +64,8 @@ public class InternalAprInputBuffer exte + bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500); + } + ++ this.httpParser = httpParser; ++ + inputStreamInputBuffer = new SocketInputBuffer(); + + filterLibrary = new InputFilter[0]; +@@ -231,7 +234,13 @@ public class InternalAprInputBuffer exte + end = pos; + } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) { + questionPos = pos; +- } else if (HttpParser.isNotRequestTarget(buf[pos])) { ++ } else if (questionPos != -1 && !httpParser.isQueryRelaxed(buf[pos])) { ++ // %nn decoding will be checked at the point of decoding ++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); ++ } else if (httpParser.isNotRequestTargetRelaxed(buf[pos])) { ++ // This is a general check that aims to catch problems early ++ // Detailed checking of each part of the request target will ++ // happen in AbstractHttp11Processor#prepareRequest() + throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); + } + +diff -up java/org/apache/coyote/http11/InternalInputBuffer.java.orig java/org/apache/coyote/http11/InternalInputBuffer.java +--- java/org/apache/coyote/http11/InternalInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500 ++++ java/org/apache/coyote/http11/InternalInputBuffer.java 2019-03-05 14:58:21.215293426 -0500 +@@ -52,13 +52,16 @@ public class InternalInputBuffer extends + /** + * Default constructor. + */ +- public InternalInputBuffer(Request request, int headerBufferSize) { ++ public InternalInputBuffer(Request request, int headerBufferSize, ++ HttpParser httpParser) { + + this.request = request; + headers = request.getMimeHeaders(); + + buf = new byte[headerBufferSize]; + ++ this.httpParser = httpParser; ++ + inputStreamInputBuffer = new InputStreamInputBuffer(); + + filterLibrary = new InputFilter[0]; +@@ -185,7 +188,13 @@ public class InternalInputBuffer extends + end = pos; + } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) { + questionPos = pos; +- } else if (HttpParser.isNotRequestTarget(buf[pos])) { ++ } else if (questionPos != -1 && !httpParser.isQueryRelaxed(buf[pos])) { ++ // %nn decoding will be checked at the point of decoding ++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); ++ } else if (httpParser.isNotRequestTargetRelaxed(buf[pos])) { ++ // This is a general check that aims to catch problems early ++ // Detailed checking of each part of the request target will ++ // happen in AbstractHttp11Processor#prepareRequest() + throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); + } + +diff -up java/org/apache/coyote/http11/InternalNioInputBuffer.java.orig java/org/apache/coyote/http11/InternalNioInputBuffer.java +--- java/org/apache/coyote/http11/InternalNioInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500 ++++ java/org/apache/coyote/http11/InternalNioInputBuffer.java 2019-03-05 14:58:20.272295967 -0500 +@@ -98,12 +98,14 @@ public class InternalNioInputBuffer exte + /** + * Alternate constructor. + */ +- public InternalNioInputBuffer(Request request, int headerBufferSize) { ++ public InternalNioInputBuffer(Request request, int headerBufferSize, ++ HttpParser httpParser) { + + this.request = request; + headers = request.getMimeHeaders(); + + this.headerBufferSize = headerBufferSize; ++ this.httpParser = httpParser; + + inputStreamInputBuffer = new SocketInputBuffer(); + +@@ -313,7 +315,13 @@ public class InternalNioInputBuffer exte + end = pos; + } else if ((buf[pos] == Constants.QUESTION) && (parsingRequestLineQPos == -1)) { + parsingRequestLineQPos = pos; +- } else if (HttpParser.isNotRequestTarget(buf[pos])) { ++ } else if (parsingRequestLineQPos != -1 && !httpParser.isQueryRelaxed(buf[pos])) { ++ // %nn decoding will be checked at the point of decoding ++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); ++ } else if (httpParser.isNotRequestTargetRelaxed(buf[pos])) { ++ // This is a general check that aims to catch problems early ++ // Detailed checking of each part of the request target will ++ // happen in AbstractHttp11Processor#prepareRequest() + throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); + } + pos++; +diff -up java/org/apache/coyote/http11/LocalStrings.properties.orig java/org/apache/coyote/http11/LocalStrings.properties +--- java/org/apache/coyote/http11/LocalStrings.properties.orig 2019-03-05 12:14:08.092280004 -0500 ++++ java/org/apache/coyote/http11/LocalStrings.properties 2019-03-05 12:23:45.474498387 -0500 +@@ -27,6 +27,9 @@ http11processor.filter.unknown=Unknown f + http11processor.filter.error=Error intializing filter {0} + http11processor.header.parse=Error parsing HTTP request header + http11processor.neverused=This method should never be used ++http11processor.request.invalidScheme=The HTTP request contained an absolute URI with an invalid scheme ++http11processor.request.invalidUri==The HTTP request contained an invalid URI ++http11processor.request.invalidUserInfo=The HTTP request contained an absolute URI with an invalid userinfo + http11processor.request.prepare=Error preparing request + http11processor.request.process=Error processing request + http11processor.request.finish=Error finishing request +diff -up java/org/apache/tomcat/util/buf/ByteChunk.java.orig java/org/apache/tomcat/util/buf/ByteChunk.java +--- java/org/apache/tomcat/util/buf/ByteChunk.java.orig 2017-03-09 08:51:41.000000000 -0500 ++++ java/org/apache/tomcat/util/buf/ByteChunk.java 2019-03-05 12:16:53.404769901 -0500 +@@ -668,7 +668,8 @@ public final class ByteChunk implements + } + + /** +- * Returns true if the message bytes starts with the specified string. ++ * Returns true if the buffer starts with the specified string when tested ++ * in a case sensitive manner. + * @param s the string + * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards. + */ +@@ -717,6 +718,31 @@ public final class ByteChunk implements + * @param s the string + * @param pos The position + */ ++ public boolean startsWith(String s, int pos) { ++ byte[] b = buff; ++ int len = s.length(); ++ if (b == null || len + pos > end - start) { ++ return false; ++ } ++ int off = start + pos; ++ for (int i = 0; i < len; i++) { ++ if (b[off++] != s.charAt(i)) { ++ return false; ++ } ++ } ++ return true; ++ } ++ ++ ++ /** ++ * Returns true if the buffer starts with the specified string when tested ++ * in a case insensitive manner. ++ * ++ * @param s the string ++ * @param pos The position ++ * ++ * @return true if the start matches ++ */ + public boolean startsWithIgnoreCase(String s, int pos) { + byte[] b = buff; + int len = s.length(); +diff -up java/org/apache/tomcat/util/http/parser/HttpParser.java.orig java/org/apache/tomcat/util/http/parser/HttpParser.java +--- java/org/apache/tomcat/util/http/parser/HttpParser.java.orig 2017-03-09 08:51:41.000000000 -0500 ++++ java/org/apache/tomcat/util/http/parser/HttpParser.java 2019-03-05 14:58:20.291295916 -0500 +@@ -67,9 +67,17 @@ public class HttpParser { + private static final boolean[] IS_SEPARATOR = new boolean[ARRAY_SIZE]; + private static final boolean[] IS_TOKEN = new boolean[ARRAY_SIZE]; + private static final boolean[] IS_HEX = new boolean[ARRAY_SIZE]; +- private static final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE]; + private static final boolean[] IS_HTTP_PROTOCOL = new boolean[ARRAY_SIZE]; ++ private static final boolean[] IS_ALPHA = new boolean[ARRAY_SIZE]; ++ private static final boolean[] IS_NUMERIC = new boolean[ARRAY_SIZE]; + private static final boolean[] REQUEST_TARGET_ALLOW = new boolean[ARRAY_SIZE]; ++ private static final boolean[] IS_UNRESERVED = new boolean[ARRAY_SIZE]; ++ private static final boolean[] IS_SUBDELIM = new boolean[ARRAY_SIZE]; ++ private static final boolean[] IS_USERINFO = new boolean[ARRAY_SIZE]; ++ private static final boolean[] IS_RELAXABLE = new boolean[ARRAY_SIZE]; ++ ++ private static final HttpParser DEFAULT; ++ + + static { + // Digest field types. +@@ -91,19 +99,6 @@ public class HttpParser { + // RFC2617 says nc is 8LHEX. <">8LHEX<"> will also be accepted + fieldTypes.put("nc", FIELD_TYPE_LHEX); + +- String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow"); +- if (prop != null) { +- for (int i = 0; i < prop.length(); i++) { +- char c = prop.charAt(i); +- if (c == '{' || c == '}' || c == '|') { +- REQUEST_TARGET_ALLOW[c] = true; +- } else { +- log.warn(sm.getString("httpparser.invalidRequestTargetCharacter", +- Character.valueOf(c))); +- } +- } +- } +- + for (int i = 0; i < ARRAY_SIZE; i++) { + // Control> 0-31, 127 + if (i < 32 || i == 127) { +@@ -128,6 +123,67 @@ public class HttpParser { + IS_HEX[i] = true; + } + ++ // Not valid for HTTP protocol ++ // "HTTP/" DIGIT "." DIGIT ++ if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) { ++ IS_HTTP_PROTOCOL[i] = true; ++ } ++ ++ if (i >= '0' && i <= '9') { ++ IS_NUMERIC[i] = true; ++ } ++ ++ if (i >= 'a' && i <= 'z' || i >= 'A' && i <= 'Z') { ++ IS_ALPHA[i] = true; ++ } ++ ++ if (IS_ALPHA[i] || IS_NUMERIC[i] || i == '-' || i == '.' || i == '_' || i == '~') { ++ IS_UNRESERVED[i] = true; ++ } ++ ++ if (i == '!' || i == '$' || i == '&' || i == '\'' || i == '(' || i == ')' || i == '*' || ++ i == '+' || i == ',' || i == ';' || i == '=') { ++ IS_SUBDELIM[i] = true; ++ } ++ ++ // userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) ++ if (IS_UNRESERVED[i] || i == '%' || IS_SUBDELIM[i] || i == ':') { ++ IS_USERINFO[i] = true; ++ } ++ ++ // The characters that are normally not permitted for which the ++ // restrictions may be relaxed when used in the path and/or query ++ // string ++ if (i == '\"' || i == '<' || i == '>' || i == '[' || i == '\\' || i == ']' || ++ i == '^' || i == '`' || i == '{' || i == '|' || i == '}') { ++ IS_RELAXABLE[i] = true; ++ } ++ } ++ ++ String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow"); ++ if (prop != null) { ++ for (int i = 0; i < prop.length(); i++) { ++ char c = prop.charAt(i); ++ if (c == '{' || c == '}' || c == '|') { ++ REQUEST_TARGET_ALLOW[c] = true; ++ } else { ++ log.warn(sm.getString("http.invalidRequestTargetCharacter", ++ Character.valueOf(c))); ++ } ++ } ++ } ++ ++ DEFAULT = new HttpParser(null, null); ++ } ++ ++ ++ private final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE]; ++ private final boolean[] IS_ABSOLUTEPATH_RELAXED = new boolean[ARRAY_SIZE]; ++ private final boolean[] IS_QUERY_RELAXED = new boolean[ARRAY_SIZE]; ++ ++ ++ public HttpParser(String relaxedPathChars, String relaxedQueryChars) { ++ for (int i = 0; i < ARRAY_SIZE; i++) { + // Not valid for request target. + // Combination of multiple rules from RFC7230 and RFC 3986. Must be + // ASCII, no controls plus a few additional characters excluded +@@ -139,12 +195,29 @@ public class HttpParser { + } + } + +- // Not valid for HTTP protocol +- // "HTTP/" DIGIT "." DIGIT +- if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) { +- IS_HTTP_PROTOCOL[i] = true; ++ /* ++ * absolute-path = 1*( "/" segment ) ++ * segment = *pchar ++ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" ++ * ++ * Note pchar allows everything userinfo allows plus "@" ++ */ ++ if (IS_USERINFO[i] || i == '@' || i == '/' || REQUEST_TARGET_ALLOW[i]) { ++ IS_ABSOLUTEPATH_RELAXED[i] = true; ++ } ++ ++ /* ++ * query = *( pchar / "/" / "?" ) ++ * ++ * Note query allows everything absolute-path allows plus "?" ++ */ ++ if (IS_ABSOLUTEPATH_RELAXED[i] || i == '?' || REQUEST_TARGET_ALLOW[i]) { ++ IS_QUERY_RELAXED[i] = true; + } + } ++ ++ relax(IS_ABSOLUTEPATH_RELAXED, relaxedPathChars); ++ relax(IS_QUERY_RELAXED, relaxedQueryChars); + } + + /** +@@ -277,6 +350,39 @@ public class HttpParser { + } + + ++ public boolean isNotRequestTargetRelaxed(int c) { ++ // Fast for valid request target characters, slower for some incorrect ++ // ones ++ try { ++ return IS_NOT_REQUEST_TARGET[c]; ++ } catch (ArrayIndexOutOfBoundsException ex) { ++ return true; ++ } ++ } ++ ++ ++ public boolean isAbsolutePathRelaxed(int c) { ++ // Fast for valid user info characters, slower for some incorrect ++ // ones ++ try { ++ return IS_ABSOLUTEPATH_RELAXED[c]; ++ } catch (ArrayIndexOutOfBoundsException ex) { ++ return false; ++ } ++ } ++ ++ ++ public boolean isQueryRelaxed(int c) { ++ // Fast for valid user info characters, slower for some incorrect ++ // ones ++ try { ++ return IS_QUERY_RELAXED[c]; ++ } catch (ArrayIndexOutOfBoundsException ex) { ++ return false; ++ } ++ } ++ ++ + public static String unquote(String input) { + if (input == null || input.length() < 2) { + return input; +@@ -329,27 +435,53 @@ public class HttpParser { + + + public static boolean isNotRequestTarget(int c) { +- // Fast for valid request target characters, slower for some incorrect ++ return DEFAULT.isNotRequestTargetRelaxed(c); ++ } ++ ++ ++ public static boolean isHttpProtocol(int c) { ++ // Fast for valid HTTP protocol characters, slower for some incorrect + // ones + try { +- return IS_NOT_REQUEST_TARGET[c]; ++ return IS_HTTP_PROTOCOL[c]; + } catch (ArrayIndexOutOfBoundsException ex) { +- return true; ++ return false; + } + } + + +- public static boolean isHttpProtocol(int c) { +- // Fast for valid HTTP protocol characters, slower for some incorrect ++ public static boolean isUserInfo(int c) { ++ // Fast for valid user info characters, slower for some incorrect + // ones + try { +- return IS_HTTP_PROTOCOL[c]; ++ return IS_USERINFO[c]; ++ } catch (ArrayIndexOutOfBoundsException ex) { ++ return false; ++ } ++ } ++ ++ ++ private static boolean isRelaxable(int c) { ++ // Fast for valid user info characters, slower for some incorrect ++ // ones ++ try { ++ return IS_RELAXABLE[c]; + } catch (ArrayIndexOutOfBoundsException ex) { + return false; + } + } + + ++ public static boolean isAbsolutePath(int c) { ++ return DEFAULT.isAbsolutePathRelaxed(c); ++ } ++ ++ ++ public static boolean isQuery(int c) { ++ return DEFAULT.isQueryRelaxed(c); ++ } ++ ++ + // Skip any LWS and return the next char + private static int skipLws(StringReader input, boolean withReset) + throws IOException { +@@ -579,6 +711,18 @@ public class HttpParser { + } + } + ++ private void relax(boolean[] flags, String relaxedChars) { ++ if (relaxedChars != null && relaxedChars.length() > 0) { ++ char[] chars = relaxedChars.toCharArray(); ++ for (char c : chars) { ++ if (isRelaxable(c)) { ++ flags[c] = true; ++ IS_NOT_REQUEST_TARGET[c] = false; ++ } ++ } ++ } ++ } ++ + private static enum SkipConstantResult { + FOUND, + NOT_FOUND, +diff -up conf/catalina.properties.orig conf/catalina.properties +--- conf/catalina.properties.orig 2019-03-05 12:13:51.934329862 -0500 ++++ conf/catalina.properties 2019-03-05 12:14:08.094279997 -0500 +@@ -132,6 +132,9 @@ tomcat.util.buf.StringCache.byte.enabled + #tomcat.util.buf.StringCache.trainThreshold=500000 + #tomcat.util.buf.StringCache.cacheSize=5000 + ++# This system property is deprecated. Use the relaxedPathChars relaxedQueryChars ++# attributes of the Connector instead. These attributes permit a wider range of ++# characters to be configured as valid. + # Allow for changes to HTTP request validation +-# WARNING: Using this option will expose the server to CVE-2016-6816 ++# WARNING: Using this option may expose the server to CVE-2016-6816 + #tomcat.util.http.parser.HttpParser.requestTargetAllow=| +diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml +--- webapps/docs/changelog.xml.orig 2019-03-05 12:13:47.068344877 -0500 ++++ webapps/docs/changelog.xml 2019-03-05 12:14:08.103279970 -0500 +@@ -108,6 +108,12 @@ + Improve handing of overflow in the UTF-8 decoder with supplementary + characters. (markt) + ++ ++ 62273: Implement configuration options to work-around ++ specification non-compliant user agents (including all the major ++ browsers) that do not correctly %nn encode URI paths and query strings ++ as required by RFC 7230 and RFC 3986. (markt) ++ + + +
    +diff -up webapps/docs/config/http.xml.orig webapps/docs/config/http.xml +--- webapps/docs/config/http.xml.orig 2017-03-09 08:51:43.000000000 -0500 ++++ webapps/docs/config/http.xml 2019-03-05 12:14:08.103279970 -0500 +@@ -516,6 +516,32 @@ + expected concurrent requests (synchronous and asynchronous).

    + + ++ ++

    The HTTP/1.1 ++ specification requires that certain characters are %nn encoded when ++ used in URI paths. Unfortunately, many user agents including all the major ++ browsers are not compliant with this specification and use these ++ characters in unencoded form. To prevent Tomcat rejecting such requests, ++ this attribute may be used to specify the additional characters to allow. ++ If not specified, no addtional characters will be allowed. The value may ++ be any combination of the following characters: ++ " < > [ \ ] ^ ` { | } . Any other characters ++ present in the value will be ignored.

    ++
    ++ ++ ++

    The HTTP/1.1 ++ specification requires that certain characters are %nn encoded when ++ used in URI query strings. Unfortunately, many user agents including all ++ the major browsers are not compliant with this specification and use these ++ characters in unencoded form. To prevent Tomcat rejecting such requests, ++ this attribute may be used to specify the additional characters to allow. ++ If not specified, no addtional characters will be allowed. The value may ++ be any combination of the following characters: ++ " < > [ \ ] ^ ` { | } . Any other characters ++ present in the value will be ignored.

    ++
    ++ + +

    The value is a regular expression (using java.util.regex) + matching the user-agent header of HTTP clients for which +diff -up webapps/docs/config/systemprops.xml.orig webapps/docs/config/systemprops.xml +--- webapps/docs/config/systemprops.xml.orig 2019-03-05 12:14:08.104279967 -0500 ++++ webapps/docs/config/systemprops.xml 2019-03-05 12:16:02.075928285 -0500 +@@ -709,11 +709,15 @@ + + + ++

    This system property is deprecated. Use the ++ relaxedPathChars and relaxedQueryChars ++ attributes of the Connector instead. These attributes permit a wider range ++ of characters to be configured as valid.

    +

    A string comprised of characters the server should allow even when they are not encoded. + These characters would normally result in a 400 status.

    +

    The acceptable characters for this property are: |, { + , and }

    +-

    WARNING: Use of this option will expose the server to CVE-2016-6816. ++

    WARNING: Use of this option may expose the server to CVE-2016-6816. +

    +

    If not specified, the default value of null will be used.

    + +diff -up test/org/apache/catalina/core/TestApplicationContext.java.orig test/org/apache/catalina/core/TestApplicationContext.java +--- test/org/apache/catalina/core/TestApplicationContext.java.orig 2019-03-05 12:13:51.981329717 -0500 ++++ test/org/apache/catalina/core/TestApplicationContext.java 2019-03-05 12:14:08.094279997 -0500 +@@ -77,7 +77,7 @@ public class TestApplicationContext exte + + ByteChunk res = new ByteChunk(); + int rc = getUrl("http://localhost:" + getPort() + +- "/test/bug5nnnn/bug53467].jsp", res, null); ++ "/test/bug5nnnn/bug53467%5D.jsp", res, null); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + Assert.assertTrue(res.toString().contains("

    OK

    ")); diff --git a/SOURCES/tomcat-7.0.76-rhbz-1588703.patch b/SOURCES/tomcat-7.0.76-rhbz-1588703.patch new file mode 100644 index 0000000..3834dd2 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-rhbz-1588703.patch @@ -0,0 +1,39 @@ +commit 7f474ba097347fbe4c3db776b46eef59a587c0aa +Author: Coty Sutherland +Date: Tue Sep 19 14:22:06 2017 +0000 + + Update fix for bug 59904 so that values less than zero are accepted instead of throwing a NegativeArraySizeException. + + git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1808887 13f79535-47bb-0310-9956-ffa450edef68 + +diff --git a/java/org/apache/tomcat/util/http/Cookies.java b/java/org/apache/tomcat/util/http/Cookies.java +index 5ae71ec97c..511bbf3243 100644 +--- java/org/apache/tomcat/util/http/Cookies.java ++++ java/org/apache/tomcat/util/http/Cookies.java +@@ -134,7 +134,7 @@ public final class Cookies { + } + + if (cookieCount >= scookies.length) { +- int newSize = Math.min(2*cookieCount, limit); ++ int newSize = limit > -1 ? Math.min(2*cookieCount, limit) : 2*cookieCount; + ServerCookie scookiesTmp[] = new ServerCookie[newSize]; + System.arraycopy( scookies, 0, scookiesTmp, 0, cookieCount); + scookies=scookiesTmp; +diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml +--- webapps/docs/changelog.xml.orig 2019-03-01 13:04:17.093997197 -0500 ++++ webapps/docs/changelog.xml 2019-03-01 13:05:04.168868394 -0500 +@@ -92,6 +92,14 @@ + + + ++ ++ ++ ++ Update fix for 59904 so that values less than zero are accepted ++ instead of throwing a NegativeArraySizeException. (remm) ++ ++ ++ + +
    + diff --git a/SOURCES/tomcat-7.0.conf b/SOURCES/tomcat-7.0.conf index 21e1506..07a1cf2 100644 --- a/SOURCES/tomcat-7.0.conf +++ b/SOURCES/tomcat-7.0.conf @@ -40,9 +40,8 @@ CATALINA_TMPDIR="/var/cache/tomcat/temp" # Run tomcat under the Java Security Manager SECURITY_MANAGER="false" -# Time to wait in seconds, before killing process -# TODO(stingray): does nothing, fix. -# SHUTDOWN_WAIT="30" +# SHUTDOWN_WAIT has been deprecated. To change the shutdown wait time, set +# TimeoutStopSec in tomcat.service. # If you wish to further customize your tomcat environment, # put your own definitions here diff --git a/SOURCES/tomcat-7.0.sysconfig b/SOURCES/tomcat-7.0.sysconfig index fd2274a..9167dd7 100644 --- a/SOURCES/tomcat-7.0.sysconfig +++ b/SOURCES/tomcat-7.0.sysconfig @@ -29,23 +29,14 @@ # Use JAVA_OPTS to set java.library.path for libtcnative.so #JAVA_OPTS="-Djava.library.path=@@@LIBDIR@@@" -# What user should run tomcat -#TOMCAT_USER="tomcat" - # You can change your tomcat locale here #LANG="en_US" # Run tomcat under the Java Security Manager #SECURITY_MANAGER="false" -# Time to wait in seconds, before killing process -#SHUTDOWN_WAIT="30" - -# Whether to annoy the user with "attempting to shut down" messages or not -#SHUTDOWN_VERBOSE="false" - -# Connector port is 8080 for this tomcat instance -#CONNECTOR_PORT="8080" +# SHUTDOWN_WAIT has been deprecated. To change the shutdown wait time, set +# TimeoutStopSec in tomcat.service. # If you wish to further customize your tomcat environment, # put your own definitions here diff --git a/SPECS/tomcat.spec b/SPECS/tomcat.spec index 2f01f9e..a83984e 100644 --- a/SPECS/tomcat.spec +++ b/SPECS/tomcat.spec @@ -95,6 +95,12 @@ Patch7: %{name}-7.0.76-CVE-2017-12617.patch Patch8: patch.rhbz1602060 Patch9: %{name}-7.0.76-CVE-2018-1336.patch Patch10: %{name}-7.0.76-CVE-2018-11784.patch +Patch11: %{name}-7.0.76-CVE-2018-1304.patch +Patch12: %{name}-7.0.76-CVE-2018-1305.patch +Patch13: %{name}-7.0.76-CVE-2018-8014.patch +Patch14: %{name}-7.0.76-CVE-2018-8034.patch +Patch15: %{name}-7.0.76-rhbz-1588703.patch +Patch16: %{name}-7.0.76-rhbz-1455483.patch BuildArch: noarch @@ -250,6 +256,12 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name " %patch8 -p0 %patch9 -p0 %patch10 -p0 +%patch11 -p0 +%patch12 -p0 +%patch13 -p0 +%patch14 -p0 +%patch15 -p0 +%patch16 -p0 %{__ln_s} $(build-classpath jakarta-taglibs-core) webapps/examples/WEB-INF/lib/jstl.jar %{__ln_s} $(build-classpath jakarta-taglibs-standard) webapps/examples/WEB-INF/lib/standard.jar @@ -696,6 +708,13 @@ fi %changelog * Tue Feb 12 2019 Coty Sutherland 0:7.0.76-9 - Resolves: rhbz#1641873 CVE-2018-11784 tomcat: Open redirect in default servlet +- Resolves: rhbz#1552375 CVE-2018-1304 tomcat: Incorrect handling of empty string URL in security constraints can lead to unintended exposure of resources +- Resolves: rhbz#1552374 CVE-2018-1305 tomcat: Late application of security constraints can lead to resource exposure for unauthorised users +- Resolves: rhbz#1590182 CVE-2018-8014 tomcat: Insecure defaults in CORS filter enable 'supportsCredentials' for all origins +- Resolves: rhbz#1608609 CVE-2018-8034 tomcat: host name verification missing in WebSocket client +- Resolves: rhbz#1588703 Backport of Negative maxCookieCount value causes exception for Tomcat +- Resolves: rhbz#1472950 shutdown_wait option is not working for Tomcat +- Resolves: rhbz#1455483 Add support for characters "<" and ">" to the possible whitelist values * Fri Oct 12 2018 Coty Sutherland 0:7.0.76-8 - Resolves: rhbz#1608607 CVE-2018-1336 tomcat: A bug in the UTF 8 decoder can lead to DoS