diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bb3184 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +SOURCES/apache-tomcat-7.0.76-src.tar.gz +SOURCES/tomcat-juli-adapters.jar +SOURCES/tomcat-juli.jar diff --git a/.tomcat.metadata b/.tomcat.metadata new file mode 100644 index 0000000..6a949e1 --- /dev/null +++ b/.tomcat.metadata @@ -0,0 +1,3 @@ +0a432e0853b399d7e9aec6df0679e11c6622ed51 SOURCES/apache-tomcat-7.0.76-src.tar.gz +aa3df4f86defedf62a5136d1137a1241837b0be2 SOURCES/tomcat-juli-adapters.jar +6b883e7be4738775c823e436b8b35753d82d4525 SOURCES/tomcat-juli.jar diff --git a/SOURCES/el-api-OSGi-MANIFEST.MF b/SOURCES/el-api-OSGi-MANIFEST.MF new file mode 100644 index 0000000..69b797f --- /dev/null +++ b/SOURCES/el-api-OSGi-MANIFEST.MF @@ -0,0 +1,13 @@ +Manifest-Version: 1.0 +Export-Package: javax.el;version="2.2.0" +Bundle-Vendor: %bundleProvider +Bundle-ClassPath: . +Bundle-Version: 2.2.0 +Bundle-Name: %bundleName +Bundle-Localization: plugin +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: javax.el +DynamicImport-Package: org.apache.el +Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J + 2SE-1.3 + diff --git a/SOURCES/jasper-OSGi-MANIFEST.MF b/SOURCES/jasper-OSGi-MANIFEST.MF new file mode 100644 index 0000000..9a3d98a --- /dev/null +++ b/SOURCES/jasper-OSGi-MANIFEST.MF @@ -0,0 +1,40 @@ +Manifest-Version: 1.0 +Export-Package: org.apache.jasper;version="7.0.21",org.apache.jasper.c + ompiler;version="7.0.21",org.apache.jasper.compiler.tagplugin;version + ="7.0.21",org.apache.jasper.resources;version="7.0.21",org.apache.jas + per.runtime;version="7.0.21",org.apache.jasper.security;version="7.0. + 19",org.apache.jasper.servlet;version="7.0.21",org.apache.jasper.tagp + lugins.jstl;version="7.0.21",org.apache.jasper.tagplugins.jstl.core;v + ersion="7.0.21",org.apache.jasper.util;version="7.0.21",org.apache.ja + sper.xmlparser;version="7.0.21" +Bundle-Vendor: %bundleProvider +Bundle-ClassPath: . +Bundle-Version: 7.0.21 +Bundle-Localization: plugin +Bundle-Name: %bundleName +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: org.apache.jasper +Import-Package: javax.servlet;version="[2.4.0, 3.0.0]",javax.servlet.h + ttp;version="[2.4.0, 3.0.0]",javax.servlet.jsp;version="[2.0.0, 2.2.0 + ]",javax.servlet.jsp.el;version="[2.0.0, 2.2.0]",javax.servlet.jsp.re + sources;version="[2.0.0, 2.2.0]",javax.servlet.jsp.tagext;version="[2 + .0.0, 2.2.0]",javax.servlet.resources;version="[2.4.0, 3.0.0]",javax. + xml.parsers,org.apache.commons.el;version="[1.0.0,2.0.0)",org.apache. + commons.logging;version="[1.0.0,2.0.0)",org.apache.tools.ant;resoluti + on:=optional,org.apache.tools.ant.taskdefs;resolution:=optional,org.a + pache.tools.ant.types;resolution:=optional,org.apache.tools.ant.util; + resolution:=optional,org.w3c.dom,org.xml.sax,org.xml.sax.ext,org.xml. + sax.helpers,org.apache.tomcat;version="7.0.21",org.apache.juli.loggin + g;version="7.0.21",javax.el;version="2.2.0",org.eclipse.jdt.internal. + compiler,org.eclipse.jdt.internal.compiler.parser,org.eclipse.jdt.int + ernal.compiler.parser.diagnose,org.eclipse.jdt.internal.compiler.flow + ,org.eclipse.jdt.internal.compiler.util,org.eclipse.jdt.internal.comp + iler.impl,org.eclipse.jdt.internal.compiler.lookup,org.eclipse.jdt.in + ternal.compiler.codegen,org.eclipse.jdt.internal.compiler.batch,org.e + clipse.jdt.internal.compiler.classfmt,org.eclipse.jdt.internal.compil + er.ast,org.eclipse.jdt.internal.compiler.problem,org.eclipse.jdt.inte + rnal.compiler.env,org.eclipse.jdt.internal.core.util,org.eclipse.jdt. + core.compiler +Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J + 2SE-1.3 + diff --git a/SOURCES/jasper-el-OSGi-MANIFEST.MF b/SOURCES/jasper-el-OSGi-MANIFEST.MF new file mode 100644 index 0000000..fd2879f --- /dev/null +++ b/SOURCES/jasper-el-OSGi-MANIFEST.MF @@ -0,0 +1,13 @@ +Manifest-Version: 1.0 +Export-Package: org.apache.el;version="7.0.21" +Bundle-Vendor: %bundleProvider +Bundle-ClassPath: . +Bundle-Version: 7.0.21 +Bundle-Name: %bundleName +Bundle-Localization: plugin +Bundle-ManifestVersion: 2 +Import-Package: javax.el;version="2.2" +Bundle-SymbolicName: org.apache.el +Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J + 2SE-1.3 + diff --git a/SOURCES/jsp-api-OSGi-MANIFEST.MF b/SOURCES/jsp-api-OSGi-MANIFEST.MF new file mode 100644 index 0000000..62bc00d --- /dev/null +++ b/SOURCES/jsp-api-OSGi-MANIFEST.MF @@ -0,0 +1,13 @@ +Manifest-Version: 1.0 +Bundle-Vendor: %bundleProvider +Bundle-Localization: plugin +Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0,J2SE-1.3 +Bundle-Name: %bundleName +Bundle-SymbolicName: javax.servlet.jsp +Export-Package: javax.servlet.jsp; version=2.2,javax.servlet.jsp.el; v + ersion=2.2,javax.servlet.jsp.resources; version=2.2,javax.servlet.jsp + .tagext; version=2.2 +Bundle-Version: 2.2.0.v200806031607 +Bundle-ManifestVersion: 2 +Import-Package: javax.servlet; version=3.0,javax.servlet.http; version + =3.0,javax.servlet.resources; version=3.0,javax.el;version="2.2.0" diff --git a/SOURCES/patch.rhbz1602060 b/SOURCES/patch.rhbz1602060 new file mode 100644 index 0000000..e89aadf --- /dev/null +++ b/SOURCES/patch.rhbz1602060 @@ -0,0 +1,82 @@ +--- java/org/apache/tomcat/websocket/WsSession.java.org 2017-03-09 14:51:41.000000000 +0100 ++++ java/org/apache/tomcat/websocket/WsSession.java 2018-07-18 17:37:46.853657200 +0200 +@@ -595,8 +595,8 @@ + localEndpoint.onError(this, e); + } + } +- +- ++ ++ + /** + * Use protected so unit tests can access this method directly. + */ +@@ -635,29 +635,48 @@ + * {@link FutureToSendHandler} completes. + */ + protected void registerFuture(FutureToSendHandler f2sh) { +- boolean fail = false; +- synchronized (stateLock) { +- // If the session has already been closed the any registered futures +- // will have been processed so the failure result for this future +- // needs to be set here. +- if (state == State.OPEN || f2sh.isCloseMessage()) { +- // WebSocket session is open or this is the close message +- futures.put(f2sh, f2sh); +- } else if (f2sh.isDone()) { +- // NO-OP. The future completed before the session closed so no +- // need to register in case the session closes before it +- // completes. +- } else { +- // Construct the exception outside of the sync block +- fail = true; +- } ++ // Ideally, this code should sync on stateLock so that the correct ++ // action is taken based on the current state of the connection. ++ // However, a sync on stateLock can't be used here as it will create the ++ // possibility of a dead-lock. See BZ 61183. ++ // Therefore, a slightly less efficient approach is used. ++ ++ // Always register the future. ++ futures.put(f2sh, f2sh); ++ ++ if (state == State.OPEN || f2sh.isCloseMessage()) { ++ // The session is open. The future has been registered with the open ++ // session. Normal processing continues. ++ return; + } + +- if (fail) { +- IOException ioe = new IOException(sm.getString("wsSession.messageFailed")); +- SendResult sr = new SendResult(ioe); +- f2sh.onResult(sr); ++ // The session is closed. The future may or may not have been registered ++ // in time for it to be processed during session closure. ++ ++ if (f2sh.isDone()) { ++ // The future has completed. It is not known if the future was ++ // completed normally by the I/O layer or in error by doClose(). It ++ // doesn't matter which. There is nothing more to do here. ++ return; + } ++ ++ // The session is closed. The Future had not completed when last checked. ++ // There is a small timing window that means the Future may have been ++ // completed since the last check. There is also the possibility that ++ // the Future was not registered in time to be cleaned up during session ++ // close. ++ // Attempt to complete the Future with an error result as this ensures ++ // that the Future completes and any client code waiting on it does not ++ // hang. It is slightly inefficient since the Future may have been ++ // completed in another thread or another thread may be about to ++ // complete the Future but knowing if this is the case requires the sync ++ // on stateLock (see above). ++ // Note: If multiple attempts are made to complete the Future, the ++ // second and subsequent attempts are ignored. ++ ++ IOException ioe = new IOException(sm.getString("wsSession.messageFailed")); ++ SendResult sr = new SendResult(ioe); ++ f2sh.onResult(sr); + } + + diff --git a/SOURCES/servlet-api-OSGi-MANIFEST.MF b/SOURCES/servlet-api-OSGi-MANIFEST.MF new file mode 100644 index 0000000..3938935 --- /dev/null +++ b/SOURCES/servlet-api-OSGi-MANIFEST.MF @@ -0,0 +1,17 @@ +Manifest-Version: 1.0 +Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1,J2SE-1.4 +Bundle-SymbolicName: javax.servlet +Bundle-ManifestVersion: 2 +Bundle-Name: %bundleName +Bundle-Localization: plugin +Bundle-Version: 3.0.0 +Bundle-Vendor: %bundleProvider +Export-Package: javax.servlet;version="3.0", + javax.servlet;version="2.6", + javax.servlet.http;version="3.0", + javax.servlet.http;version="2.6", + javax.servlet.annotation;version="2.6", + javax.servlet.descriptor;version="3.0", + javax.servlet.descriptor;version="2.6", + javax.servlet.resources;version="3.0", + javax.servlet.resources;version="2.6" diff --git a/SOURCES/tomcat-7.0-bootstrap-MANIFEST.MF.patch b/SOURCES/tomcat-7.0-bootstrap-MANIFEST.MF.patch new file mode 100644 index 0000000..e2ee93e --- /dev/null +++ b/SOURCES/tomcat-7.0-bootstrap-MANIFEST.MF.patch @@ -0,0 +1,9 @@ +--- res/META-INF/bootstrap.jar.manifest.orig 2010-04-06 10:11:09.000000000 -0600 ++++ res/META-INF/bootstrap.jar.manifest 2010-04-06 10:45:56.000000000 -0600 +@@ -1,6 +1,5 @@ + Manifest-Version: 1.0 + Main-Class: org.apache.catalina.startup.Bootstrap +-Class-Path: commons-daemon.jar + Specification-Title: Apache Tomcat Bootstrap + Specification-Version: @VERSION_MAJOR_MINOR@ + Specification-Vendor: Apache Software Foundation diff --git a/SOURCES/tomcat-7.0-catalina-policy.patch b/SOURCES/tomcat-7.0-catalina-policy.patch new file mode 100644 index 0000000..8aaf93e --- /dev/null +++ b/SOURCES/tomcat-7.0-catalina-policy.patch @@ -0,0 +1,39 @@ +--- conf/catalina.policy~ 2016-06-17 10:20:17.649171968 -0400 ++++ conf/catalina.policy 2016-06-17 10:23:35.358309244 -0400 +@@ -50,6 +50,36 @@ grant codeBase "file:${java.home}/lib/ex + permission java.security.AllPermission; + }; + ++// ========== RHEL SPECIFIC CODE PERMISSIONS ======================================= ++ ++// Allowing everything in /usr/share/java allows too many unknowns to be permitted ++// Specifying the individual jars that tomcat needs to function with the security manager ++// is the safest way forward. ++grant codeBase "file:/usr/share/java/tomcat-servlet-3.0-api.jar" { ++ permission java.security.AllPermission; ++}; ++grant codeBase "file:/usr/share/java/omcat-jsp-2.2-api.jar" { ++ permission java.security.AllPermission; ++}; ++grant codeBase "file:/usr/share/java/tomcat-el-2.2-api.jar" { ++ permission java.security.AllPermission; ++}; ++grant codeBase "file:/usr/share/java/log4j.jar" { ++ permission java.security.AllPermission; ++}; ++grant codeBase "file:/usr/share/java/ecj.jar" { ++ permission java.security.AllPermission; ++}; ++grant codeBase "file:/usr/share/java/apache-commons-pool.jar" { ++ permission java.security.AllPermission; ++}; ++grant codeBase "file:/usr/share/java/apache-commons-dbcp.jar" { ++ permission java.security.AllPermission; ++}; ++grant codeBase "file:/usr/share/java/apache-commons-collections.jar" { ++ permission java.security.AllPermission; ++}; ++ + + // ========== CATALINA CODE PERMISSIONS ======================================= + diff --git a/SOURCES/tomcat-7.0-digest.script b/SOURCES/tomcat-7.0-digest.script new file mode 100644 index 0000000..86f05ec --- /dev/null +++ b/SOURCES/tomcat-7.0-digest.script @@ -0,0 +1,45 @@ +#!/bin/sh +# +# tomcat-digest script +# JPackage Project + +# Source functions library +if [ -f /usr/share/java-utils/java-functions ] ; then + . /usr/share/java-utils/java-functions +else + echo "Can't find functions library, aborting" + exit 1 +fi + +# Get the tomcat config (use this for environment specific settings) +if [ -z "${TOMCAT_CFG}" ]; then + TOMCAT_CFG="/etc/tomcat/tomcat.conf" +fi + +if [ -r "$TOMCAT_CFG" ]; then + . $TOMCAT_CFG +fi + +set_javacmd + +# CLASSPATH munging +if [ -n "$JSSE_HOME" ]; then + CLASSPATH="${CLASSPATH}:$(build-classpath jcert jnet jsse 2>/dev/null)" +fi +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/bin/bootstrap.jar" +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/bin/tomcat-juli.jar" +export CLASSPATH + +# Configuration +MAIN_CLASS="org.apache.catalina.startup.Tool" +BASE_FLAGS="-Dcatalina.home=\"$CATALINA_HOME\"" +BASE_OPTIONS="" +BASE_JARS="commons-daemon tomcat/catalina servlet tomcat/tomcat-util tomcat/tomcat-coyote" + +# Set parameters +set_classpath $BASE_JARS +set_flags $BASE_FLAGS +set_options $BASE_OPTIONS + +# Let's start +run -server org.apache.catalina.realm.RealmBase "$@" diff --git a/SOURCES/tomcat-7.0-jsvc.service b/SOURCES/tomcat-7.0-jsvc.service new file mode 100644 index 0000000..f480324 --- /dev/null +++ b/SOURCES/tomcat-7.0-jsvc.service @@ -0,0 +1,22 @@ +# Systemd unit file for tomcat +# +# To create clones of this service: +# 1) By default SERVICE_NAME=tomcat. When cloned, the value must be defined +# before tomcat-sysd is called. +# 2) Create /etc/sysconfig/${SERVICE_NAME} from /etc/sysconfig/tomcat +# to override tomcat defaults + +[Unit] +Description=Apache Tomcat Web Application Container JSVC wrapper +After=syslog.target network.target + +[Service] +Type=simple +EnvironmentFile=/etc/tomcat/tomcat.conf +Environment="NAME=" "USE_JSVC=true" +EnvironmentFile=-/etc/sysconfig/tomcat +ExecStart=/usr/libexec/tomcat/server start +ExecStop=/usr/libexec/tomcat/server stop + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/tomcat-7.0-log4j.properties b/SOURCES/tomcat-7.0-log4j.properties new file mode 100644 index 0000000..1252a64 --- /dev/null +++ b/SOURCES/tomcat-7.0-log4j.properties @@ -0,0 +1,11 @@ +log4j.rootLogger=debug, R +log4j.appender.R=org.apache.log4j.RollingFileAppender +log4j.appender.R.File=${catalina.home}/logs/tomcat.log +log4j.appender.R.MaxFileSize=10MB +log4j.appender.R.MaxBackupIndex=10 +log4j.appender.R.layout=org.apache.log4j.PatternLayout +log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n +log4j.logger.org.apache.catalina=DEBUG, R +log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost]=DEBUG, R +log4j.logger.org.apache.catalina.core=DEBUG, R +log4j.logger.org.apache.catalina.session=DEBUG, R diff --git a/SOURCES/tomcat-7.0-tomcat-users-webapp.patch b/SOURCES/tomcat-7.0-tomcat-users-webapp.patch new file mode 100644 index 0000000..5304bad --- /dev/null +++ b/SOURCES/tomcat-7.0-tomcat-users-webapp.patch @@ -0,0 +1,17 @@ +--- conf/tomcat-users.xml 2016-04-11 04:02:30.000000000 -0400 ++++ conf/tomcat-users.xml 2016-06-06 16:39:12.751217530 -0400 +@@ -38,4 +38,14 @@ + + + --> ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + diff --git a/SOURCES/tomcat-7.0-tool-wrapper.script b/SOURCES/tomcat-7.0-tool-wrapper.script new file mode 100644 index 0000000..2640ce2 --- /dev/null +++ b/SOURCES/tomcat-7.0-tool-wrapper.script @@ -0,0 +1,45 @@ +#!/bin/sh +# +# tomcat-digest script +# JPackage Project + +# Source functions library +if [ -f /usr/share/java-utils/java-functions ] ; then + . /usr/share/java-utils/java-functions +else + echo "Can't find functions library, aborting" + exit 1 +fi + +# Get the tomcat config (use this for environment specific settings) +if [ -z "${TOMCAT_CFG}" ]; then + TOMCAT_CFG="/etc/tomcat/tomcat.conf" +fi + +if [ -r "$TOMCAT_CFG" ]; then + . $TOMCAT_CFG +fi + +set_javacmd + +# CLASSPATH munging +if [ -n "$JSSE_HOME" ]; then + CLASSPATH="${CLASSPATH}:$(build-classpath jcert jnet jsse 2>/dev/null)" +fi +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/bin/bootstrap.jar" +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/bin/tomcat-juli.jar" +export CLASSPATH + +# Configuration +MAIN_CLASS="org.apache.catalina.startup.Tool" +BASE_OPTIONS="" +BASE_FLAGS="-Dcatalina.home=\"$CATALINA_HOME\"" +BASE_JARS="commons-daemon tomcat/catalina servlet" + +# Set parameters +set_classpath $BASE_JARS +set_flags $BASE_FLAGS +set_options $BASE_OPTIONS + +# Let's start +run "$@" diff --git a/SOURCES/tomcat-7.0.54-rebase.patch b/SOURCES/tomcat-7.0.54-rebase.patch new file mode 100644 index 0000000..e7c9590 --- /dev/null +++ b/SOURCES/tomcat-7.0.54-rebase.patch @@ -0,0 +1,28 @@ +--- java/org/apache/jasper/compiler/JDTCompiler.java.orig 2014-09-17 12:18:00.909020000 -0400 ++++ java/org/apache/jasper/compiler/JDTCompiler.java 2014-09-17 12:21:41.007769000 -0400 +@@ -333,9 +333,11 @@ + } else if(opt.equals("1.7")) { + settings.put(CompilerOptions.OPTION_Source, + CompilerOptions.VERSION_1_7); ++ /** + } else if(opt.equals("1.8")) { + settings.put(CompilerOptions.OPTION_Source, + CompilerOptions.VERSION_1_8); ++ **/ + } else { + log.warn("Unknown source VM " + opt + " ignored."); + settings.put(CompilerOptions.OPTION_Source, +@@ -377,11 +379,13 @@ + CompilerOptions.VERSION_1_7); + settings.put(CompilerOptions.OPTION_Compliance, + CompilerOptions.VERSION_1_7); ++ /** + } else if(opt.equals("1.8")) { + settings.put(CompilerOptions.OPTION_TargetPlatform, + CompilerOptions.VERSION_1_8); + settings.put(CompilerOptions.OPTION_Compliance, + CompilerOptions.VERSION_1_8); ++ **/ + } else { + log.warn("Unknown target VM " + opt + " ignored."); + settings.put(CompilerOptions.OPTION_TargetPlatform, diff --git a/SOURCES/tomcat-7.0.76-CVE-2017-12617.patch b/SOURCES/tomcat-7.0.76-CVE-2017-12617.patch new file mode 100644 index 0000000..7cc2048 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2017-12617.patch @@ -0,0 +1,715 @@ +--- java/org/apache/catalina/servlets/DefaultServlet.java.orig 2017-10-13 09:41:05.734302404 -0400 ++++ java/org/apache/catalina/servlets/DefaultServlet.java 2017-10-13 09:42:53.515701311 -0400 +@@ -855,23 +855,6 @@ + return; + } + +- // If the resource is not a collection, and the resource path +- // ends with "/" or "\", return NOT FOUND +- if (cacheEntry.context == null) { +- if (path.endsWith("/") || (path.endsWith("\\"))) { +- // Check if we're included so we can return the appropriate +- // missing resource name in the error +- String requestUri = (String) request.getAttribute( +- RequestDispatcher.INCLUDE_REQUEST_URI); +- if (requestUri == null) { +- requestUri = request.getRequestURI(); +- } +- response.sendError(HttpServletResponse.SC_NOT_FOUND, +- requestUri); +- return; +- } +- } +- + boolean isError = DispatcherType.ERROR == request.getDispatcherType(); + + // Check if the conditions specified in the optional If headers are +--- java/org/apache/naming/resources/FileDirContext.java.orig 2017-10-13 09:41:05.737302387 -0400 ++++ java/org/apache/naming/resources/FileDirContext.java 2017-10-13 09:42:53.516701306 -0400 +@@ -14,8 +14,6 @@ + * See the License for the specific language governing permissions and + * limitations under the License. + */ +- +- + package org.apache.naming.resources; + + import java.io.File; +@@ -75,6 +73,8 @@ + + /** + * Builds a file directory context using the given environment. ++ * ++ * @param env The environment with which to build the context + */ + public FileDirContext(Hashtable env) { + super(env); +@@ -95,6 +95,8 @@ + */ + protected String absoluteBase = null; + ++ private String canonicalBase = null; ++ + + /** + * Allow linking. +@@ -104,7 +106,6 @@ + + // ------------------------------------------------------------- Properties + +- + /** + * Set the document root. + * +@@ -117,32 +118,41 @@ + */ + @Override + public void setDocBase(String docBase) { ++ // Validate the format of the proposed document root ++ if (docBase == null) { ++ throw new IllegalArgumentException(sm.getString("resources.null")); ++ } + +- // Validate the format of the proposed document root +- if (docBase == null) +- throw new IllegalArgumentException +- (sm.getString("resources.null")); +- +- // Calculate a File object referencing this document base directory +- base = new File(docBase); ++ // Calculate a File object referencing this document base directory ++ base = new File(docBase); + try { + base = base.getCanonicalFile(); + } catch (IOException e) { + // Ignore + } + +- // Validate that the document base is an existing directory +- if (!base.exists() || !base.isDirectory() || !base.canRead()) +- throw new IllegalArgumentException +- (sm.getString("fileResources.base", docBase)); +- this.absoluteBase = base.getAbsolutePath(); +- super.setDocBase(docBase); ++ // Validate that the document base is an existing directory ++ if (!base.exists() || !base.isDirectory() || !base.canRead()) { ++ throw new IllegalArgumentException(sm.getString("fileResources.base", docBase)); ++ } + ++ this.absoluteBase = normalize(base.getAbsolutePath()); ++ ++ // absoluteBase also needs to be normalized. Using the canonical path is ++ // the simplest way of doing this. ++ try { ++ this.canonicalBase = base.getCanonicalPath(); ++ } catch (IOException e) { ++ throw new IllegalArgumentException(e); ++ } ++ super.setDocBase(docBase); + } + + + /** + * Set allow linking. ++ * ++ * @param allowLinking The new value for the attribute + */ + public void setAllowLinking(boolean allowLinking) { + this.allowLinking = allowLinking; +@@ -151,6 +161,8 @@ + + /** + * Is linking allowed. ++ * ++ * @return {@code true} is linking is allowed, otherwise {@false} + */ + public boolean getAllowLinking() { + return allowLinking; +@@ -193,7 +205,7 @@ + @Override + protected Object doLookup(String name) { + Object result = null; +- File file = file(name); ++ File file = file(name, true); + + if (file == null) + return null; +@@ -230,7 +242,7 @@ + public void unbind(String name) + throws NamingException { + +- File file = file(name); ++ File file = file(name, true); + + if (file == null) + throw new NameNotFoundException( +@@ -255,22 +267,22 @@ + * @exception NamingException if a naming exception is encountered + */ + @Override +- public void rename(String oldName, String newName) +- throws NamingException { ++ public void rename(String oldName, String newName) throws NamingException { + +- File file = file(oldName); ++ File file = file(oldName, true); + +- if (file == null) +- throw new NameNotFoundException +- (sm.getString("resources.notFound", oldName)); ++ if (file == null) { ++ throw new NameNotFoundException(sm.getString("resources.notFound", oldName)); ++ } + +- File newFile = new File(base, newName); ++ File newFile = file(newName, false); ++ if (newFile == null) { ++ throw new NamingException(sm.getString("resources.renameFail", oldName, newName)); ++ } + + if (!file.renameTo(newFile)) { +- throw new NamingException(sm.getString("resources.renameFail", +- oldName, newName)); ++ throw new NamingException(sm.getString("resources.renameFail", oldName, newName)); + } +- + } + + +@@ -291,11 +303,11 @@ + protected List doListBindings(String name) + throws NamingException { + +- File file = file(name); ++ File file = file(name, true); + + if (file == null) + return null; +- ++ + return list(file); + + } +@@ -395,7 +407,7 @@ + throws NamingException { + + // Building attribute list +- File file = file(name); ++ File file = file(name, true); + + if (file == null) + return null; +@@ -463,12 +475,20 @@ + * @exception NamingException if a naming exception is encountered + */ + @Override +- public void bind(String name, Object obj, Attributes attrs) +- throws NamingException { ++ public void bind(String name, Object obj, Attributes attrs) throws NamingException { + + // Note: No custom attributes allowed + +- File file = new File(base, name); ++ // bind() is meant to create a file so ensure that the path doesn't end ++ // in '/' ++ if (name.endsWith("/")) { ++ throw new NamingException(sm.getString("resources.bindFailed", name)); ++ } ++ ++ File file = file(name, false); ++ if (file == null) { ++ throw new NamingException(sm.getString("resources.bindFailed", name)); ++ } + if (file.exists()) + throw new NameAlreadyBoundException + (sm.getString("resources.alreadyBound", name)); +@@ -503,7 +523,10 @@ + // Note: No custom attributes allowed + // Check obj type + +- File file = new File(base, name); ++ File file = file(name, false); ++ if (file == null) { ++ throw new NamingException(sm.getString("resources.bindFailed", name)); ++ } + + InputStream is = null; + if (obj instanceof Resource) { +@@ -583,13 +606,14 @@ + public DirContext createSubcontext(String name, Attributes attrs) + throws NamingException { + +- File file = new File(base, name); ++ File file = file(name, false); ++ if (file == null) { ++ throw new NamingException(sm.getString("resources.bindFailed", name)); ++ } + if (file.exists()) +- throw new NameAlreadyBoundException +- (sm.getString("resources.alreadyBound", name)); ++ throw new NameAlreadyBoundException(sm.getString("resources.alreadyBound", name)); + if (!file.mkdir()) +- throw new NamingException +- (sm.getString("resources.bindFailed", name)); ++ throw new NamingException(sm.getString("resources.bindFailed", name)); + return (DirContext) lookup(name); + + } +@@ -758,6 +782,7 @@ + + } + ++ + /** + * Return a File object representing the specified normalized + * context-relative path if it exists and is readable. Otherwise, +@@ -766,51 +791,133 @@ + * @param name Normalized context-relative path (with leading '/') + */ + protected File file(String name) { ++ return file(name, true); ++ } ++ ++ ++ /** ++ * Return a File object representing the specified normalized ++ * context-relative path if it exists and is readable. Otherwise, ++ * return null. ++ * ++ * @param name Normalized context-relative path (with leading '/') ++ * @param mustExist Must the specified resource exist? ++ */ ++ protected File file(String name, boolean mustExist) { ++ if (name.equals("/")) { ++ name = ""; ++ } + + File file = new File(base, name); +- if (file.exists() && file.canRead()) { ++ return validate(file, name, mustExist, absoluteBase, canonicalBase); ++ } + +- if (allowLinking) +- return file; +- +- // Check that this file belongs to our root path +- String canPath = null; +- try { +- canPath = file.getCanonicalPath(); +- } catch (IOException e) { +- // Ignore +- } +- if (canPath == null) +- return null; + +- // Check to see if going outside of the web application root +- if (!canPath.startsWith(absoluteBase)) { +- return null; +- } ++ protected File validate(File file, String name, boolean mustExist, String absoluteBase, ++ String canonicalBase) { + +- // Case sensitivity check - this is now always done +- String fileAbsPath = file.getAbsolutePath(); +- if (fileAbsPath.endsWith(".")) +- fileAbsPath = fileAbsPath + "/"; +- String absPath = normalize(fileAbsPath); +- canPath = normalize(canPath); +- if ((absoluteBase.length() < absPath.length()) +- && (absoluteBase.length() < canPath.length())) { +- absPath = absPath.substring(absoluteBase.length() + 1); +- if (absPath.equals("")) +- absPath = "/"; +- canPath = canPath.substring(absoluteBase.length() + 1); +- if (canPath.equals("")) +- canPath = "/"; +- if (!canPath.equals(absPath)) +- return null; +- } ++ // If the requested names ends in '/', the Java File API will return a ++ // matching file if one exists. This isn't what we want as it is not ++ // consistent with the Servlet spec rules for request mapping. ++ if (name.endsWith("/") && file.isFile()) { ++ return null; ++ } + +- } else { ++ // If the file/dir must exist but the identified file/dir can't be read ++ // then signal that the resource was not found ++ if (mustExist && !file.canRead()) { ++ return null; ++ } ++ ++ // If allow linking is enabled, files are not limited to being located ++ // under the fileBase so all further checks are disabled. ++ if (allowLinking) { ++ return file; ++ } ++ ++ // Additional Windows specific checks to handle known problems with ++ // File.getCanonicalPath() ++ if (JrePlatform.IS_WINDOWS && isInvalidWindowsFilename(name)) { ++ return null; ++ } ++ ++ // Check that this file is located under the web application root ++ String canPath = null; ++ try { ++ canPath = file.getCanonicalPath(); ++ } catch (IOException e) { ++ // Ignore ++ } ++ if (canPath == null || !canPath.startsWith(canonicalBase)) { ++ return null; ++ } ++ ++ // Ensure that the file is not outside the fileBase. This should not be ++ // possible for standard requests (the request is normalized early in ++ // the request processing) but might be possible for some access via the ++ // Servlet API (RequestDispatcher etc.) therefore these checks are ++ // retained as an additional safety measure. absoluteBase has been ++ // normalized so absPath needs to be normalized as well. ++ String absPath = normalize(file.getAbsolutePath()); ++ if ((absoluteBase.length() > absPath.length())) { + return null; + } ++ ++ // Remove the fileBase location from the start of the paths since that ++ // was not part of the requested path and the remaining check only ++ // applies to the request path ++ absPath = absPath.substring(absoluteBase.length()); ++ canPath = canPath.substring(canonicalBase.length()); ++ ++ // Case sensitivity check ++ // The normalized requested path should be an exact match the equivalent ++ // canonical path. If it is not, possible reasons include: ++ // - case differences on case insensitive file systems ++ // - Windows removing a trailing ' ' or '.' from the file name ++ // ++ // In all cases, a mis-match here results in the resource not being ++ // found ++ // ++ // absPath is normalized so canPath needs to be normalized as well ++ // Can't normalize canPath earlier as canonicalBase is not normalized ++ if (canPath.length() > 0) { ++ canPath = normalize(canPath); ++ } ++ if (!canPath.equals(absPath)) { ++ return null; ++ } ++ + return file; ++ } ++ + ++ private boolean isInvalidWindowsFilename(String name) { ++ final int len = name.length(); ++ if (len == 0) { ++ return false; ++ } ++ // This consistently ~10 times faster than the equivalent regular ++ // expression irrespective of input length. ++ for (int i = 0; i < len; i++) { ++ char c = name.charAt(i); ++ if (c == '\"' || c == '<' || c == '>') { ++ // These characters are disallowed in Windows file names and ++ // there are known problems for file names with these characters ++ // when using File#getCanonicalPath(). ++ // Note: There are additional characters that are disallowed in ++ // Windows file names but these are not known to cause ++ // problems when using File#getCanonicalPath(). ++ return true; ++ } ++ } ++ // Windows does not allow file names to end in ' ' unless specific low ++ // level APIs are used to create the files that bypass various checks. ++ // File names that end in ' ' are known to cause problems when using ++ // File#getCanonicalPath(). ++ if (name.charAt(len -1) == ' ') { ++ return true; ++ } ++ return false; + } + + +@@ -1054,10 +1161,10 @@ + return super.getResourceType(); + } + +- ++ + /** + * Get canonical path. +- * ++ * + * @return String the file's canonical path + */ + @Override +@@ -1071,10 +1178,6 @@ + } + return canonicalPath; + } +- +- + } +- +- + } + +--- java/org/apache/naming/resources/VirtualDirContext.java.orig 2017-10-13 09:41:05.740302370 -0400 ++++ java/org/apache/naming/resources/VirtualDirContext.java 2017-10-13 09:42:53.517701300 -0400 +@@ -76,7 +76,8 @@ + * be listed twice. + *

+ * +- * @param path ++ * @param path The set of file system paths and virtual paths to map them to ++ * in the required format + */ + public void setExtraResourcePaths(String path) { + extraResourcePaths = path; +@@ -106,13 +107,13 @@ + } + path = resSpec.substring(0, idx); + } +- String dir = resSpec.substring(idx + 1); ++ File dir = new File(resSpec.substring(idx + 1)); + List resourcePaths = mappedResourcePaths.get(path); + if (resourcePaths == null) { + resourcePaths = new ArrayList(); + mappedResourcePaths.put(path, resourcePaths); + } +- resourcePaths.add(dir); ++ resourcePaths.add(dir.getAbsolutePath()); + } + } + if (mappedResourcePaths.isEmpty()) { +@@ -151,15 +152,17 @@ + String resourcesDir = dirList.get(0); + if (name.equals(path)) { + File f = new File(resourcesDir); +- if (f.exists() && f.canRead()) { ++ f = validate(f, name, true, resourcesDir); ++ if (f != null) { + return new FileResourceAttributes(f); + } + } + path += "/"; + if (name.startsWith(path)) { + String res = name.substring(path.length()); +- File f = new File(resourcesDir + "/" + res); +- if (f.exists() && f.canRead()) { ++ File f = new File(resourcesDir, res); ++ f = validate(f, res, true, resourcesDir); ++ if (f != null) { + return new FileResourceAttributes(f); + } + } +@@ -168,9 +171,16 @@ + throw initialException; + } + ++ + @Override + protected File file(String name) { +- File file = super.file(name); ++ return file(name, true); ++ } ++ ++ ++ @Override ++ protected File file(String name, boolean mustExist) { ++ File file = super.file(name, true); + if (file != null || mappedResourcePaths == null) { + return file; + } +@@ -185,7 +195,8 @@ + if (name.equals(path)) { + for (String resourcesDir : dirList) { + file = new File(resourcesDir); +- if (file.exists() && file.canRead()) { ++ file = validate(file, name, true, resourcesDir); ++ if (file != null) { + return file; + } + } +@@ -194,7 +205,8 @@ + String res = name.substring(path.length()); + for (String resourcesDir : dirList) { + file = new File(resourcesDir, res); +- if (file.exists() && file.canRead()) { ++ file = validate(file, res, true, resourcesDir); ++ if (file != null) { + return file; + } + } +@@ -229,7 +241,8 @@ + if (res != null) { + for (String resourcesDir : dirList) { + File f = new File(resourcesDir, res); +- if (f.exists() && f.canRead() && f.isDirectory()) { ++ f = validate(f, res, true, resourcesDir); ++ if (f != null && f.isDirectory()) { + List virtEntries = super.list(f); + for (NamingEntry entry : virtEntries) { + // filter duplicate +@@ -264,7 +277,8 @@ + if (name.equals(path)) { + for (String resourcesDir : dirList) { + File f = new File(resourcesDir); +- if (f.exists() && f.canRead()) { ++ f = validate(f, name, true, resourcesDir); ++ if (f != null) { + if (f.isFile()) { + return new FileResource(f); + } +@@ -279,8 +293,9 @@ + if (name.startsWith(path)) { + String res = name.substring(path.length()); + for (String resourcesDir : dirList) { +- File f = new File(resourcesDir + "/" + res); +- if (f.exists() && f.canRead()) { ++ File f = new File(resourcesDir, res); ++ f = validate(f, res, true, resourcesDir); ++ if (f != null) { + if (f.isFile()) { + return new FileResource(f); + } +@@ -304,4 +319,9 @@ + return null; + } + } ++ ++ ++ protected File validate(File file, String name, boolean mustExist, String absoluteBase) { ++ return validate(file, name, mustExist, normalize(absoluteBase), absoluteBase); ++ } + } +--- webapps/docs/changelog.xml.orig 2017-10-13 09:15:35.996884086 -0400 ++++ webapps/docs/changelog.xml 2017-10-13 09:44:50.895046977 -0400 +@@ -64,6 +64,14 @@ + 61101: CORS filter should set Vary header in response. + Submitted by Rick Riemer. (remm) + ++ ++ Correct regression in 7.0.80 that broke WebDAV. (markt) ++ ++ ++ 61542: Fix CVE-2017-12617 and prevent JSPs from being ++ uploaded via a specially crafted request when HTTP PUT was enabled. ++ (markt) ++ + + + +--- java/org/apache/naming/resources/JrePlatform.java.orig 2017-10-13 09:41:05.745302342 -0400 ++++ java/org/apache/naming/resources/JrePlatform.java 2017-10-13 09:42:53.516701306 -0400 +@@ -0,0 +1,59 @@ ++/* ++ * 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.naming.resources; ++ ++import java.security.AccessController; ++import java.security.PrivilegedAction; ++ ++public class JrePlatform { ++ ++ private static final String OS_NAME_PROPERTY = "os.name"; ++ private static final String OS_NAME_WINDOWS_PREFIX = "Windows"; ++ ++ static { ++ /* ++ * There are a few places where a) the behaviour of the Java API depends ++ * on the underlying platform and b) those behavioural differences have ++ * an impact on Tomcat. ++ * ++ * Tomcat therefore needs to be able to determine the platform it is ++ * running on to account for those differences. ++ * ++ * In an ideal world this code would not exist. ++ */ ++ ++ // This check is derived from the check in Apache Commons Lang ++ String osName; ++ if (System.getSecurityManager() == null) { ++ osName = System.getProperty(OS_NAME_PROPERTY); ++ } else { ++ osName = AccessController.doPrivileged( ++ new PrivilegedAction() { ++ ++ @Override ++ public String run() { ++ return System.getProperty(OS_NAME_PROPERTY); ++ } ++ }); ++ } ++ ++ IS_WINDOWS = osName.startsWith(OS_NAME_WINDOWS_PREFIX); ++ } ++ ++ ++ public static final boolean IS_WINDOWS; ++} +--- test/org/apache/naming/resources/TestFileDirContext.java.orig 2017-10-13 09:45:35.991795584 -0400 ++++ test/org/apache/naming/resources/TestFileDirContext.java 2017-10-13 09:42:53.517701300 -0400 +@@ -0,0 +1,46 @@ ++/* ++ * 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.naming.resources; ++ ++import java.io.File; ++ ++import javax.servlet.http.HttpServletResponse; ++ ++import org.junit.Assert; ++import org.junit.Test; ++ ++import org.apache.catalina.startup.Tomcat; ++import org.apache.catalina.startup.TomcatBaseTest; ++import org.apache.tomcat.util.buf.ByteChunk; ++ ++public class TestFileDirContext extends TomcatBaseTest { ++ ++ @Test ++ public void testLookupResourceWithTrailingSlash() throws Exception { ++ Tomcat tomcat = getTomcatInstance(); ++ ++ File appDir = new File("test/webapp-3.0"); ++ // app dir is relative to server home ++ tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); ++ ++ tomcat.start(); ++ ++ int sc = getUrl("http://localhost:" + getPort() + ++ "/test/index.html/", new ByteChunk(), null); ++ Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, sc); ++ } ++} diff --git a/SOURCES/tomcat-7.0.76-CVE-2017-5647.patch b/SOURCES/tomcat-7.0.76-CVE-2017-5647.patch new file mode 100644 index 0000000..baa284d --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2017-5647.patch @@ -0,0 +1,232 @@ +--- java/org/apache/coyote/AbstractProtocol.java.orig 2017-08-18 09:12:05.149568367 -0400 ++++ java/org/apache/coyote/AbstractProtocol.java 2017-08-18 09:12:55.998699189 -0400 +@@ -693,10 +693,10 @@ + release(wrapper, processor, false, true); + } else if (state == SocketState.SENDFILE) { + // Sendfile in progress. If it fails, the socket will be +- // closed. If it works, the socket will be re-added to the +- // poller +- connections.remove(socket); +- release(wrapper, processor, false, false); ++ // closed. If it works, the socket either be added to the ++ // poller (or equivalent) to await more data or processed ++ // if there are any pipe-lined requests remaining. ++ connections.put(socket, processor); + } else if (state == SocketState.UPGRADED) { + // Need to keep the connection associated with the processor + connections.put(socket, processor); +--- java/org/apache/coyote/http11/Http11AprProcessor.java.orig 2017-06-08 16:23:31.983000742 -0400 ++++ java/org/apache/coyote/http11/Http11AprProcessor.java 2017-06-08 16:23:31.999000805 -0400 +@@ -38,6 +38,7 @@ + import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; + import org.apache.tomcat.util.net.AprEndpoint; + import org.apache.tomcat.util.net.SSLSupport; ++import org.apache.tomcat.util.net.SendfileKeepAliveState; + import org.apache.tomcat.util.net.SocketStatus; + import org.apache.tomcat.util.net.SocketWrapper; + +@@ -211,7 +212,15 @@ + // Do sendfile as needed: add socket to sendfile and end + if (sendfileData != null && !getErrorState().isError()) { + sendfileData.socket = socketWrapper.getSocket().longValue(); +- sendfileData.keepAlive = keepAlive; ++ if (keepAlive) { ++ if (getInputBuffer().available() == 0) { ++ sendfileData.keepAliveState = SendfileKeepAliveState.OPEN; ++ } else { ++ sendfileData.keepAliveState = SendfileKeepAliveState.PIPELINED; ++ } ++ } else { ++ sendfileData.keepAliveState = SendfileKeepAliveState.NONE; ++ } + switch (((AprEndpoint)endpoint).getSendfile().add(sendfileData)) { + case DONE: + return false; +--- java/org/apache/coyote/http11/Http11NioProcessor.java.orig 2017-06-08 16:23:31.984000746 -0400 ++++ java/org/apache/coyote/http11/Http11NioProcessor.java 2017-06-08 16:23:32.000000809 -0400 +@@ -37,6 +37,7 @@ + import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment; + import org.apache.tomcat.util.net.SSLSupport; + import org.apache.tomcat.util.net.SecureNioChannel; ++import org.apache.tomcat.util.net.SendfileKeepAliveState; + import org.apache.tomcat.util.net.SocketStatus; + import org.apache.tomcat.util.net.SocketWrapper; + +@@ -275,7 +276,15 @@ + // Do sendfile as needed: add socket to sendfile and end + if (sendfileData != null && !getErrorState().isError()) { + ((KeyAttachment) socketWrapper).setSendfileData(sendfileData); +- sendfileData.keepAlive = keepAlive; ++ if (keepAlive) { ++ if (getInputBuffer().available() == 0) { ++ sendfileData.keepAliveState = SendfileKeepAliveState.OPEN; ++ } else { ++ sendfileData.keepAliveState = SendfileKeepAliveState.PIPELINED; ++ } ++ } else { ++ sendfileData.keepAliveState = SendfileKeepAliveState.NONE; ++ } + SelectionKey key = socketWrapper.getSocket().getIOChannel().keyFor( + socketWrapper.getSocket().getPoller().getSelector()); + //do the first write on this thread, might as well +--- java/org/apache/tomcat/util/net/AprEndpoint.java.orig 2017-06-08 16:23:31.985000750 -0400 ++++ java/org/apache/tomcat/util/net/AprEndpoint.java 2017-06-08 16:23:32.001000813 -0400 +@@ -2106,7 +2106,7 @@ + // Position + public long pos; + // KeepAlive flag +- public boolean keepAlive; ++ public SendfileKeepAliveState keepAliveState = SendfileKeepAliveState.NONE; + } + + +@@ -2349,20 +2349,33 @@ + state.pos = state.pos + nw; + if (state.pos >= state.end) { + remove(state); +- if (state.keepAlive) { ++ switch (state.keepAliveState) { ++ case NONE: { ++ // Close the socket since this is ++ // the end of the not keep-alive request. ++ closeSocket(state.socket); ++ break; ++ } ++ case PIPELINED: { ++ // Destroy file descriptor pool, which should close the file ++ Pool.destroy(state.fdpool); ++ Socket.timeoutSet(state.socket, getSoTimeout() * 1000); ++ // Process the pipelined request data ++ if (!processSocket(state.socket, SocketStatus.OPEN_READ)) { ++ closeSocket(state.socket); ++ } ++ break; ++ } ++ case OPEN: { + // Destroy file descriptor pool, which should close the file + Pool.destroy(state.fdpool); +- Socket.timeoutSet(state.socket, +- getSoTimeout() * 1000); +- // If all done put the socket back in the +- // poller for processing of further requests +- getPoller().add( +- state.socket, getKeepAliveTimeout(), ++ Socket.timeoutSet(state.socket, getSoTimeout() * 1000); ++ // Put the socket back in the poller for ++ // processing of further requests ++ getPoller().add(state.socket, getKeepAliveTimeout(), + true, false); +- } else { +- // Close the socket since this is +- // the end of not keep-alive request. +- closeSocket(state.socket); ++ break; ++ } + } + } + } +--- java/org/apache/tomcat/util/net/NioEndpoint.java.orig 2017-06-08 16:23:31.987000757 -0400 ++++ java/org/apache/tomcat/util/net/NioEndpoint.java 2017-06-08 16:23:32.002000817 -0400 +@@ -1383,16 +1383,30 @@ + // responsible for registering the socket for the + // appropriate event(s) if sendfile completes. + if (!calledByProcessor) { +- if ( sd.keepAlive ) { +- if (log.isDebugEnabled()) { +- log.debug("Connection is keep alive, registering back for OP_READ"); +- } +- reg(sk,attachment,SelectionKey.OP_READ); +- } else { ++ switch (sd.keepAliveState) { ++ case NONE: { + if (log.isDebugEnabled()) { + log.debug("Send file connection is being closed"); + } + cancelledKey(sk,SocketStatus.STOP,false); ++ break; ++ } ++ case PIPELINED: { ++ if (log.isDebugEnabled()) { ++ log.debug("Connection is keep alive, processing pipe-lined data"); ++ } ++ if (!processSocket(sc, SocketStatus.OPEN_READ, true)) { ++ cancelledKey(sk, SocketStatus.DISCONNECT, false); ++ } ++ break; ++ } ++ case OPEN: { ++ if (log.isDebugEnabled()) { ++ log.debug("Connection is keep alive, registering back for OP_READ"); ++ } ++ reg(sk, attachment, SelectionKey.OP_READ); ++ break; ++ } + } + } + return SendfileState.DONE; +@@ -1836,6 +1850,6 @@ + public volatile long pos; + public volatile long length; + // KeepAlive flag +- public volatile boolean keepAlive; ++ public SendfileKeepAliveState keepAliveState = SendfileKeepAliveState.NONE; + } + } +--- webapps/docs/changelog.xml.orig 2017-06-08 16:23:31.989000765 -0400 ++++ webapps/docs/changelog.xml 2017-06-08 16:25:23.618440723 -0400 +@@ -73,6 +73,13 @@ + + + ++ ++ ++ ++ Improve sendfile handling when requests are pipelined. (markt) ++ ++ ++ + +
+ +--- java/org/apache/tomcat/util/net/SendfileKeepAliveState.java.orig 2017-06-08 16:23:31.992000777 -0400 ++++ java/org/apache/tomcat/util/net/SendfileKeepAliveState.java 2017-06-08 16:23:32.000000809 -0400 +@@ -0,0 +1,39 @@ ++/* ++ * 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.net; ++ ++public enum SendfileKeepAliveState { ++ ++ /** ++ * Keep-alive is not in use. The socket can be closed when the response has ++ * been written. ++ */ ++ NONE, ++ ++ /** ++ * Keep-alive is in use and there is pipelined data in the input buffer to ++ * be read as soon as the current response has been written. ++ */ ++ PIPELINED, ++ ++ /** ++ * Keep-alive is in use. The socket should be added to the poller (or ++ * equivalent) to await more data as soon as the current response has been ++ * written. ++ */ ++ OPEN ++} diff --git a/SOURCES/tomcat-7.0.76-CVE-2017-5664.patch b/SOURCES/tomcat-7.0.76-CVE-2017-5664.patch new file mode 100644 index 0000000..f1cb009 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2017-5664.patch @@ -0,0 +1,125 @@ +--- java/org/apache/catalina/servlets/DefaultServlet.java.orig 2017-06-08 16:12:18.426412964 -0400 ++++ java/org/apache/catalina/servlets/DefaultServlet.java 2017-06-08 16:12:18.436413002 -0400 +@@ -245,7 +245,7 @@ + urlEncoder.addSafeCharacter('.'); + urlEncoder.addSafeCharacter('*'); + urlEncoder.addSafeCharacter('/'); +- ++ + if (Globals.IS_SECURITY_ENABLED) { + factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); +@@ -423,6 +423,18 @@ + } + + ++ @Override ++ protected void service(HttpServletRequest req, HttpServletResponse resp) ++ throws ServletException, IOException { ++ ++ if (req.getDispatcherType() == DispatcherType.ERROR) { ++ doGet(req, resp); ++ } else { ++ super.service(req, resp); ++ } ++ } ++ ++ + /** + * Process a GET request for the specified resource. + * +@@ -860,8 +872,7 @@ + } + } + +- boolean isError = +- response.getStatus() >= HttpServletResponse.SC_BAD_REQUEST; ++ boolean isError = DispatcherType.ERROR == request.getDispatcherType(); + + // Check if the conditions specified in the optional If headers are + // satisfied. +@@ -1326,7 +1337,7 @@ + + } + +- ++ + /** + * Return an InputStream to an HTML representation of the contents + * of this directory. +@@ -1767,15 +1778,15 @@ + + + private File validateGlobalXsltFile() { +- ++ + File result = null; + String base = System.getProperty(Globals.CATALINA_BASE_PROP); +- ++ + if (base != null) { + File baseConf = new File(base, "conf"); + result = validateGlobalXsltFile(baseConf); + } +- ++ + if (result == null) { + String home = System.getProperty(Globals.CATALINA_HOME_PROP); + if (home != null && !home.equals(base)) { +@@ -2364,6 +2375,8 @@ + + /** + * Validate range. ++ * ++ * @return true if the range is valid, otherwise false + */ + public boolean validate() { + if (end >= length) +--- java/org/apache/catalina/servlets/WebdavServlet.java.orig 2017-06-08 16:12:18.427412968 -0400 ++++ java/org/apache/catalina/servlets/WebdavServlet.java 2017-06-08 16:12:18.436413002 -0400 +@@ -40,6 +40,7 @@ + import javax.naming.NamingEnumeration; + import javax.naming.NamingException; + import javax.naming.directory.DirContext; ++import javax.servlet.DispatcherType; + import javax.servlet.RequestDispatcher; + import javax.servlet.ServletContext; + import javax.servlet.ServletException; +@@ -354,6 +355,11 @@ + return; + } + ++ if (req.getDispatcherType() == DispatcherType.ERROR) { ++ doGet(req, resp); ++ return; ++ } ++ + final String method = req.getMethod(); + + if (debug > 0) { +--- webapps/docs/changelog.xml.orig 2017-06-08 16:12:18.429412975 -0400 ++++ webapps/docs/changelog.xml 2017-06-08 16:13:17.452638065 -0400 +@@ -57,6 +57,23 @@ + They eventually become mixed with the numbered issues. (I.e., numbered + issues do not "pop up" wrt. others). + --> ++
++ ++ ++ ++ Use a more reliable mechanism for the DefaultServlet when ++ determining if the current request is for custom error page or not. ++ (markt) ++ ++ ++ Ensure that when the Default or WebDAV servlets process an error ++ dispatch that the error resource is processed via the ++ doGet() method irrespective of the method used for the ++ original request that triggered the error. (markt) ++ ++ ++ ++
+
+ + diff --git a/SOURCES/tomcat-7.0.76-CVE-2017-7674.patch b/SOURCES/tomcat-7.0.76-CVE-2017-7674.patch new file mode 100644 index 0000000..efb3cf9 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2017-7674.patch @@ -0,0 +1,46 @@ +--- java/org/apache/catalina/filters/CorsFilter.java.orig 2017-10-12 16:48:47.426952298 -0400 ++++ java/org/apache/catalina/filters/CorsFilter.java 2017-10-12 16:48:47.431952269 -0400 +@@ -297,6 +297,10 @@ + exposedHeadersString); + } + ++ // Indicate the response depends on the origin ++ response.addHeader(CorsFilter.REQUEST_HEADER_VARY, ++ CorsFilter.REQUEST_HEADER_ORIGIN); ++ + // Forward the request down the filter chain. + filterChain.doFilter(request, response); + } +@@ -998,6 +1002,13 @@ + "Access-Control-Allow-Headers"; + + // -------------------------------------------------- CORS Request Headers ++ ++ /** ++ * The Vary header indicates allows disabling proxy caching by indicating ++ * the the response depends on the origin. ++ */ ++ public static final String REQUEST_HEADER_VARY = "Vary"; ++ + /** + * The Origin header indicates where the cross-origin request or preflight + * request originates from. +--- webapps/docs/changelog.xml.orig 2017-10-12 16:48:47.428952287 -0400 ++++ webapps/docs/changelog.xml 2017-10-12 16:50:08.718477877 -0400 +@@ -57,6 +57,16 @@ + They eventually become mixed with the numbered issues. (I.e., numbered + issues do not "pop up" wrt. others). + --> ++
++ ++ ++ ++ 61101: CORS filter should set Vary header in response. ++ Submitted by Rick Riemer. (remm) ++ ++ ++ ++
+
+ + diff --git a/SOURCES/tomcat-7.0.76-CVE-2018-11784.patch b/SOURCES/tomcat-7.0.76-CVE-2018-11784.patch new file mode 100644 index 0000000..fedf214 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2018-11784.patch @@ -0,0 +1,34 @@ +diff -up java/org/apache/catalina/servlets/DefaultServlet.java.orig java/org/apache/catalina/servlets/DefaultServlet.java +--- java/org/apache/catalina/servlets/DefaultServlet.java.orig 2019-02-12 09:16:19.144563964 -0500 ++++ java/org/apache/catalina/servlets/DefaultServlet.java 2019-02-12 09:16:52.516485998 -0500 +@@ -1103,6 +1103,10 @@ public class DefaultServlet + location.append('?'); + location.append(request.getQueryString()); + } ++ // Avoid protocol relative redirects ++ while (location.length() > 1 && location.charAt(1) == '/') { ++ location.deleteCharAt(0); ++ } + response.sendRedirect(response.encodeRedirectURL(location.toString())); + } + +diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml +--- webapps/docs/changelog.xml.orig 2019-02-12 09:18:01.155325629 -0500 ++++ webapps/docs/changelog.xml 2019-02-12 09:18:36.354243382 -0500 +@@ -57,6 +57,16 @@ + They eventually become mixed with the numbered issues. (I.e., numbered + issues do not "pop up" wrt. others). + --> ++
++ ++ ++ ++ When generating a redirect to a directory in the Default Servlet, avoid ++ generating a protocol relative redirect. (markt) ++ ++ ++ ++
+
+ + 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-1336.patch b/SOURCES/tomcat-7.0.76-CVE-2018-1336.patch new file mode 100644 index 0000000..fdfce82 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2018-1336.patch @@ -0,0 +1,33 @@ +--- java/org/apache/tomcat/util/buf/Utf8Decoder.java.orig 2018-10-01 11:41:20.080777790 -0400 ++++ java/org/apache/tomcat/util/buf/Utf8Decoder.java 2018-10-01 11:42:07.421663409 -0400 +@@ -277,6 +277,11 @@ public class Utf8Decoder extends Charset + outRemaining--; + } else { + if (outRemaining < 2) { ++ // Encoded with 4 bytes. inIndex currently points ++ // to the final byte. Move it back to first byte. ++ inIndex -= 3; ++ in.position(inIndex - in.arrayOffset()); ++ out.position(outIndex - out.arrayOffset()); + return CoderResult.OVERFLOW; + } + cArr[outIndex++] = (char) ((jchar >> 0xA) + 0xD7C0); +--- webapps/docs/changelog.xml.orig 2018-10-01 11:47:17.700912507 -0400 ++++ webapps/docs/changelog.xml 2018-10-01 11:47:40.889856277 -0400 +@@ -57,6 +57,16 @@ + They eventually become mixed with the numbered issues. (I.e., numbered + issues do not "pop up" wrt. others). + --> ++
++ ++ ++ ++ Improve handing of overflow in the UTF-8 decoder with supplementary ++ characters. (markt) ++ ++ ++ ++
+
+ + 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 new file mode 100644 index 0000000..07a1cf2 --- /dev/null +++ b/SOURCES/tomcat-7.0.conf @@ -0,0 +1,48 @@ +# System-wide configuration file for tomcat services +# This will be loaded by systemd as an environment file, +# so please keep the syntax. For shell expansion support +# place your custom files as /etc/tomcat/conf.d/*.conf +# +# There are 2 "classes" of startup behavior in this package. +# The old one, the default service named tomcat.service. +# The new named instances are called tomcat@instance.service. +# +# Use this file to change default values for all services. +# Change the service specific ones to affect only one service. +# For tomcat.service it's /etc/sysconfig/tomcat, for +# tomcat@instance it's /etc/sysconfig/tomcat@instance. + +# This variable is used to figure out if config is loaded or not. +TOMCAT_CFG_LOADED="1" + +# In new-style instances, if CATALINA_BASE isn't specified, it will +# be constructed by joining TOMCATS_BASE and NAME. +TOMCATS_BASE="/var/lib/tomcats/" + +# Where your java installation lives +JAVA_HOME="/usr/lib/jvm/jre" + +# Where your tomcat installation lives +CATALINA_HOME="@@@TCHOME@@@" + +# System-wide tmp +CATALINA_TMPDIR="/var/cache/tomcat/temp" + +# You can pass some parameters to java here if you wish to +#JAVA_OPTS="-Xminf0.1 -Xmaxf0.3" + +# Use JAVA_OPTS to set java.library.path for libtcnative.so +#JAVA_OPTS="-Djava.library.path=/usr/lib" + +# You can change your tomcat locale here +#LANG="en_US" + +# Run tomcat under the Java Security Manager +SECURITY_MANAGER="false" + +# 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 +# (i.e. LD_LIBRARY_PATH for some jdbc drivers) diff --git a/SOURCES/tomcat-7.0.logrotate b/SOURCES/tomcat-7.0.logrotate new file mode 100644 index 0000000..a87b4c0 --- /dev/null +++ b/SOURCES/tomcat-7.0.logrotate @@ -0,0 +1,8 @@ +@@@TCLOG@@@/catalina.out { + copytruncate + weekly + rotate 52 + compress + missingok + create 0644 tomcat tomcat +} diff --git a/SOURCES/tomcat-7.0.service b/SOURCES/tomcat-7.0.service new file mode 100644 index 0000000..832e7c6 --- /dev/null +++ b/SOURCES/tomcat-7.0.service @@ -0,0 +1,20 @@ +# Systemd unit file for default tomcat +# +# To create clones of this service: +# DO NOTHING, use tomcat@.service instead. + +[Unit] +Description=Apache Tomcat Web Application Container +After=syslog.target network.target + +[Service] +Type=simple +EnvironmentFile=/etc/tomcat/tomcat.conf +Environment="NAME=" +EnvironmentFile=-/etc/sysconfig/tomcat +ExecStart=/usr/libexec/tomcat/server start +SuccessExitStatus=143 +User=tomcat + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/tomcat-7.0.sysconfig b/SOURCES/tomcat-7.0.sysconfig new file mode 100644 index 0000000..9167dd7 --- /dev/null +++ b/SOURCES/tomcat-7.0.sysconfig @@ -0,0 +1,43 @@ +# Service-specific configuration file for tomcat. This will be sourced by +# the SysV init script after the global configuration file +# /etc/tomcat/tomcat.conf, thus allowing values to be overridden in +# a per-service manner. +# +# NEVER change the init script itself. To change values for all services make +# your changes in /etc/tomcat/tomcat.conf +# +# To change values for a specific service make your edits here. +# To create a new service create a link from /etc/init.d/ to +# /etc/init.d/tomcat (do not copy the init script) and make a copy of the +# /etc/sysconfig/tomcat file to /etc/sysconfig/ and change +# the property values so the two services won't conflict. Register the new +# service in the system as usual (see chkconfig and similars). +# + +# Where your java installation lives +#JAVA_HOME="/usr/lib/jvm/java" + +# Where your tomcat installation lives +#CATALINA_BASE="@@@TCHOME@@@" +#CATALINA_HOME="@@@TCHOME@@@" +#JASPER_HOME="@@@TCHOME@@@" +#CATALINA_TMPDIR="@@@TCTEMP@@@" + +# You can pass some parameters to java here if you wish to +#JAVA_OPTS="-Xminf0.1 -Xmaxf0.3" + +# Use JAVA_OPTS to set java.library.path for libtcnative.so +#JAVA_OPTS="-Djava.library.path=@@@LIBDIR@@@" + +# You can change your tomcat locale here +#LANG="en_US" + +# Run tomcat under the Java Security Manager +#SECURITY_MANAGER="false" + +# 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 +# (i.e. LD_LIBRARY_PATH for some jdbc drivers) diff --git a/SOURCES/tomcat-7.0.wrapper b/SOURCES/tomcat-7.0.wrapper new file mode 100644 index 0000000..c97cb85 --- /dev/null +++ b/SOURCES/tomcat-7.0.wrapper @@ -0,0 +1,24 @@ +#!/bin/bash + +if [ "$1" = "version" ]; then + . /usr/libexec/tomcat/preamble + exec ${JAVACMD} -classpath ${CATALINA_HOME}/lib/catalina.jar \ + org.apache.catalina.util.ServerInfo +fi + +SRV="tomcat" +if [ -n "$2" ]; then + SRV="tomcat@$2" +fi + +if [ "$1" = "start" ]; then + systemctl start ${SRV}.service +elif [ "$1" = "stop" ]; then + systemctl stop ${SRV}.service +elif [ "$1" = "version" ]; then + ${JAVACMD} -classpath ${CATALINA_HOME}/lib/catalina.jar \ + org.apache.catalina.util.ServerInfo +else + echo "Usage: $0 {start|stop|version} [server-id]" + exit 1 +fi diff --git a/SOURCES/tomcat-api-OSGi-MANIFEST.MF b/SOURCES/tomcat-api-OSGi-MANIFEST.MF new file mode 100644 index 0000000..3522c72 --- /dev/null +++ b/SOURCES/tomcat-api-OSGi-MANIFEST.MF @@ -0,0 +1,12 @@ +Manifest-Version: 1.0 +Export-Package: org.apache.tomcat;version="7.0.21" +Bundle-Vendor: %bundleProvider +Bundle-ClassPath: . +Bundle-Version: 7.0.21 +Bundle-Name: %bundleName +Bundle-Localization: plugin +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: org.apache.tomcat +Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J + 2SE-1.3 + diff --git a/SOURCES/tomcat-functions b/SOURCES/tomcat-functions new file mode 100644 index 0000000..5afc9fe --- /dev/null +++ b/SOURCES/tomcat-functions @@ -0,0 +1,41 @@ +#!/bin/bash + +if [ -r /usr/share/java-utils/java-functions ]; then + . /usr/share/java-utils/java-functions +else + echo "Can't read Java functions library, aborting" + exit 1 +fi + +_save_function() { + local ORIG_FUNC=$(declare -f $1) + local NEWNAME_FUNC="$2${ORIG_FUNC#$1}" + eval "$NEWNAME_FUNC" +} + +run_jsvc(){ + if [ -x /usr/bin/jsvc ]; then + TOMCAT_USER="${TOMCAT_USER:-tomcat}" + JSVC="/usr/bin/jsvc" + + JSVC_OPTS="-nodetach -pidfile /var/run/jsvc-tomcat${NAME}.pid -user ${TOMCAT_USER} -outfile ${CATALINA_BASE}/logs/catalina.out -errfile ${CATALINA_BASE}/logs/catalina.out" + if [ "$1" = "stop" ]; then + JSVC_OPTS="${JSVC_OPTS} -stop" + fi + + exec "${JSVC}" ${JSVC_OPTS} ${FLAGS} -classpath "${CLASSPATH}" ${OPTIONS} "${MAIN_CLASS}" "${@}" + else + echo "Can't find /usr/bin/jsvc executable" + fi +} + +_save_function run run_java + +run() { + if [ "${USE_JSVC}" = "true" ] ; then + run_jsvc $@ + else + run_java $@ + fi +} + diff --git a/SOURCES/tomcat-juli-OSGi-MANIFEST.MF b/SOURCES/tomcat-juli-OSGi-MANIFEST.MF new file mode 100644 index 0000000..cbb5674 --- /dev/null +++ b/SOURCES/tomcat-juli-OSGi-MANIFEST.MF @@ -0,0 +1,13 @@ +Manifest-Version: 1.0 +Export-Package: org.apache.juli;version="7.0.21",org.apache.juli.loggi + ng;version="7.0.21" +Bundle-Vendor: %bundleProvider +Bundle-ClassPath: . +Bundle-Version: 7.0.21 +Bundle-Name: %bundleName +Bundle-Localization: plugin +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: org.apache.juli +Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J + 2SE-1.3 + diff --git a/SOURCES/tomcat-named.service b/SOURCES/tomcat-named.service new file mode 100644 index 0000000..2998060 --- /dev/null +++ b/SOURCES/tomcat-named.service @@ -0,0 +1,24 @@ +# Systemd unit file for tomcat instances. +# +# To create clones of this service: +# 0. systemctl enable tomcat@name.service +# 1. create catalina.base directory structure in +# /var/lib/tomcats/name +# 2. profit. + +[Unit] +Description=Apache Tomcat Web Application Container +After=syslog.target network.target + +[Service] +Type=simple +EnvironmentFile=/etc/tomcat/tomcat.conf +Environment="NAME=%I" +EnvironmentFile=-/etc/sysconfig/tomcat@%I +ExecStart=/usr/libexec/tomcat/server start +ExecStop=/usr/libexec/tomcat/server stop +SuccessExitStatus=143 +User=tomcat + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/tomcat-preamble b/SOURCES/tomcat-preamble new file mode 100644 index 0000000..1271dca --- /dev/null +++ b/SOURCES/tomcat-preamble @@ -0,0 +1,52 @@ +#!/bin/bash + +. /usr/libexec/tomcat/functions + +# Get the tomcat config (use this for environment specific settings) + +if [ -z "${TOMCAT_CFG_LOADED}" ]; then + if [ -z "${TOMCAT_CFG}" ]; then + TOMCAT_CFG="/etc/tomcat/tomcat.conf" + fi + . $TOMCAT_CFG +fi + +if [ -d "${TOMCAT_CONFD=/etc/tomcat/conf.d}" ]; then + for file in ${TOMCAT_CONFD}/*.conf ; do + if [ -f "$file" ] ; then + . "$file" + fi + done +fi + +if [ -z "$CATALINA_BASE" ]; then + if [ -n "$NAME" ]; then + if [ -z "$TOMCATS_BASE" ]; then + TOMCATS_BASE="/var/lib/tomcats/" + fi + CATALINA_BASE="${TOMCATS_BASE}${NAME}" + else + CATALINA_BASE="${CATALINA_HOME}" + fi +fi +VERBOSE=1 +set_javacmd +cd ${CATALINA_HOME} +# CLASSPATH munging +if [ ! -z "$CLASSPATH" ] ; then + CLASSPATH="$CLASSPATH": +fi + +if [ -n "$JSSE_HOME" ]; then + CLASSPATH="${CLASSPATH}$(build-classpath jcert jnet jsse 2>/dev/null):" +fi +CLASSPATH="${CLASSPATH}${CATALINA_HOME}/bin/bootstrap.jar" +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/bin/tomcat-juli.jar" +CLASSPATH="${CLASSPATH}:$(build-classpath commons-daemon 2>/dev/null)" + +if [ -z "$LOGGING_PROPERTIES" ] ; then + LOGGING_PROPERTIES="${CATALINA_BASE}/conf/logging.properties" + if [ ! -f "${LOGGING_PROPERTIES}" ] ; then + LOGGING_PROPERTIES="${CATALINA_HOME}/conf/logging.properties" + fi +fi diff --git a/SOURCES/tomcat-server b/SOURCES/tomcat-server new file mode 100644 index 0000000..17ae385 --- /dev/null +++ b/SOURCES/tomcat-server @@ -0,0 +1,25 @@ +#!/bin/bash + +. /usr/libexec/tomcat/preamble + +MAIN_CLASS=org.apache.catalina.startup.Bootstrap + +FLAGS="$JAVA_OPTS" +OPTIONS="-Dcatalina.base=$CATALINA_BASE \ +-Dcatalina.home=$CATALINA_HOME \ +-Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS \ +-Djava.io.tmpdir=$CATALINA_TMPDIR \ +-Djava.util.logging.config.file=${LOGGING_PROPERTIES} \ +-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager" + +if [ "$1" = "start" ] ; then + FLAGS="${FLAGS} $CATALINA_OPTS" + if [ "${SECURITY_MANAGER}" = "true" ] ; then + OPTIONS="${OPTIONS} \ + -Djava.security.manager \ + -Djava.security.policy==${CATALINA_BASE}/conf/catalina.policy" + fi + run start +elif [ "$1" = "stop" ] ; then + run stop +fi diff --git a/SPECS/tomcat.spec b/SPECS/tomcat.spec new file mode 100644 index 0000000..a4e5ad6 --- /dev/null +++ b/SPECS/tomcat.spec @@ -0,0 +1,1013 @@ +# Copyright (c) 2000-2008, JPackage Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# 3. Neither the name of the JPackage Project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +%global jspspec 2.2 +%global major_version 7 +%global minor_version 0 +%global micro_version 76 +%global packdname apache-tomcat-%{version}-src +%global servletspec 3.0 +%global elspec 2.2 +%global tcuid 53 + +# FHS 2.3 compliant tree structure - http://www.pathname.com/fhs/2.3/ +%global basedir %{_var}/lib/%{name} +%global appdir %{basedir}/webapps +%global homedir %{_datadir}/%{name} +%global bindir %{homedir}/bin +%global confdir %{_sysconfdir}/%{name} +%global libdir %{_javadir}/%{name} +%global logdir %{_var}/log/%{name} +%global cachedir %{_var}/cache/%{name} +%global tempdir %{cachedir}/temp +%global workdir %{cachedir}/work +%global _initrddir %{_sysconfdir}/init.d +%global _systemddir /lib/systemd/system + +Name: tomcat +Epoch: 0 +Version: %{major_version}.%{minor_version}.%{micro_version} +Release: 10%{?dist} +Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API + +Group: System Environment/Daemons +License: ASL 2.0 +URL: http://tomcat.apache.org/ +Source0: http://www.apache.org/dist/tomcat/tomcat-%{major_version}/v%{version}/src/%{packdname}.tar.gz +Source1: %{name}-%{major_version}.%{minor_version}.conf +Source3: %{name}-%{major_version}.%{minor_version}.sysconfig +Source4: %{name}-%{major_version}.%{minor_version}.wrapper +Source5: %{name}-%{major_version}.%{minor_version}.logrotate +Source6: %{name}-%{major_version}.%{minor_version}-digest.script +Source7: %{name}-%{major_version}.%{minor_version}-tool-wrapper.script +Source8: servlet-api-OSGi-MANIFEST.MF +Source9: jsp-api-OSGi-MANIFEST.MF +Source10: %{name}-%{major_version}.%{minor_version}-log4j.properties +Source11: %{name}-%{major_version}.%{minor_version}.service +Source12: el-api-OSGi-MANIFEST.MF +Source13: jasper-el-OSGi-MANIFEST.MF +Source14: jasper-OSGi-MANIFEST.MF +Source15: tomcat-api-OSGi-MANIFEST.MF +Source16: tomcat-juli-OSGi-MANIFEST.MF +Source20: %{name}-%{major_version}.%{minor_version}-jsvc.service +Source21: tomcat-functions +Source22: tomcat-preamble +Source23: tomcat-server +Source24: tomcat-named.service +Source25: tomcat-juli-adapters.jar +Source26: tomcat-juli.jar + +Patch0: %{name}-%{major_version}.%{minor_version}-bootstrap-MANIFEST.MF.patch +Patch1: %{name}-%{major_version}.%{minor_version}-tomcat-users-webapp.patch +Patch2: %{name}-7.0.54-rebase.patch +Patch3: %{name}-7.0-catalina-policy.patch +Patch4: %{name}-7.0.76-CVE-2017-5664.patch +Patch5: %{name}-7.0.76-CVE-2017-5647.patch +Patch6: %{name}-7.0.76-CVE-2017-7674.patch +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 + +BuildRequires: ant +#BuildRequires: ant-nodeps +BuildRequires: ecj >= 1:4.2.1 +BuildRequires: findutils +BuildRequires: apache-commons-collections +BuildRequires: apache-commons-daemon +BuildRequires: apache-commons-dbcp +BuildRequires: apache-commons-pool +BuildRequires: jakarta-taglibs-standard +BuildRequires: java-devel >= 1:1.6.0 +BuildRequires: jpackage-utils >= 0:1.7.0 +BuildRequires: junit +BuildRequires: log4j +BuildRequires: geronimo-jaxrpc +BuildRequires: wsdl4j +BuildRequires: systemd-units +Requires: apache-commons-daemon +Requires: apache-commons-logging +Requires: apache-commons-collections +Requires: apache-commons-dbcp +Requires: apache-commons-pool +Requires: java >= 1:1.6.0 +Requires: procps +Requires: %{name}-lib = %{epoch}:%{version}-%{release} +Requires(pre): shadow-utils +Requires(post): chkconfig +Requires(preun): chkconfig +Requires(post): systemd-units +Requires(preun): systemd-units +Requires(postun): systemd-units + +%description +Tomcat is the servlet container that is used in the official Reference +Implementation for the Java Servlet and JavaServer Pages technologies. +The Java Servlet and JavaServer Pages specifications are developed by +Sun under the Java Community Process. + +Tomcat is developed in an open and participatory environment and +released under the Apache Software License version 2.0. Tomcat is intended +to be a collaboration of the best-of-breed developers from around the world. + +%package admin-webapps +Group: Applications/System +Summary: The host-manager and manager web applications for Apache Tomcat +Requires: %{name} = %{epoch}:%{version}-%{release} + +%description admin-webapps +The host-manager and manager web applications for Apache Tomcat. + +%package docs-webapp +Group: Applications/Text +Summary: The docs web application for Apache Tomcat +Requires: %{name} = %{epoch}:%{version}-%{release} + +%description docs-webapp +The docs web application for Apache Tomcat. + +%package javadoc +Group: Documentation +Summary: Javadoc generated documentation for Apache Tomcat +Requires: jpackage-utils + +%description javadoc +Javadoc generated documentation for Apache Tomcat. + +%package jsvc +Group: System Environment/Daemons +Summary: Apache jsvc wrapper for Apache Tomcat as separate service +Requires: %{name} = %{epoch}:%{version}-%{release} +Requires: apache-commons-daemon-jsvc + +%description jsvc +Systemd service and wrapper scripts to start tomcat with jsvc, +which allows tomcat to perform some privileged operations +(e.g. bind to a port < 1024) and then switch identity to a non-privileged user. + +%package jsp-%{jspspec}-api +Group: Development/Libraries +Summary: Apache Tomcat JSP API implementation classes +Provides: jsp = %{jspspec} +Provides: jsp22 +Requires: %{name}-servlet-%{servletspec}-api = %{epoch}:%{version}-%{release} +Requires(post): chkconfig +Requires(postun): chkconfig + +%description jsp-%{jspspec}-api +Apache Tomcat JSP API implementation classes. + +%package lib +Group: Development/Libraries +Summary: Libraries needed to run the Tomcat Web container +Requires: %{name}-jsp-%{jspspec}-api = %{epoch}:%{version}-%{release} +Requires: %{name}-servlet-%{servletspec}-api = %{epoch}:%{version}-%{release} +Requires: %{name}-el-%{elspec}-api = %{epoch}:%{version}-%{release} +Requires: ecj >= 1:4.2.1 +Requires: apache-commons-collections +Requires: apache-commons-dbcp +Requires: apache-commons-pool +Requires(preun): coreutils + +%description lib +Libraries needed to run the Tomcat Web container. + +%package servlet-%{servletspec}-api +Group: Development/Libraries +Summary: Apache Tomcat Servlet API implementation classes +Provides: servlet = %{servletspec} +Provides: servlet6 +Provides: servlet3 +Requires(post): chkconfig +Requires(postun): chkconfig + +%description servlet-%{servletspec}-api +Apache Tomcat Servlet API implementation classes. + +%package el-%{elspec}-api +Group: Development/Libraries +Summary: Expression Language v%{elspec} API +Provides: el_1_0_api = %{epoch}:%{version}-%{release} +Provides: el_api = %{elspec} +Requires(post): chkconfig +Requires(postun): chkconfig + +%description el-%{elspec}-api +Expression Language %{elspec}. + +%package webapps +Group: Applications/Internet +Summary: The ROOT and examples web applications for Apache Tomcat +Requires: %{name} = %{epoch}:%{version}-%{release} +Requires: jakarta-taglibs-standard >= 0:1.1 + +%description webapps +The ROOT and examples web applications for Apache Tomcat. + +%prep +%setup -q -n %{packdname} +# remove pre-built binaries and windows files +find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name "*.gz" -o \ + -name "*.jar" -o -name "*.war" -o -name "*.zip" \) -delete + +%patch0 -p0 +%patch1 -p0 +%patch2 -p0 +%patch3 -p0 +%patch4 -p0 +%patch5 -p0 +%patch6 -p0 +%patch7 -p0 +%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 + +%build +export OPT_JAR_LIST="xalan-j2-serializer" + + # we don't care about the tarballs and we're going to replace + # tomcat-dbcp.jar with apache-commons-{collections,dbcp,pool}-tomcat5.jar + # so just create a dummy file for later removal + touch HACK + %{__mkdir_p} HACKDIR + touch HACKDIR/build.xml + # who needs a build.properties file anyway + %{ant} -Dbase.path="." \ + -Dbuild.compiler="modern" \ + -Dcommons-collections.jar="$(build-classpath apache-commons-collections)" \ + -Dcommons-daemon.jar="$(build-classpath apache-commons-daemon)" \ + -Dcommons-daemon.native.src.tgz="HACK" \ + -Djasper-jdt.jar="$(build-classpath ecj)" \ + -Djdt.jar="$(build-classpath ecj)" \ + -Dtomcat-dbcp.jar="$(build-classpath apache-commons-dbcp)" \ + -Dtomcat-native.tar.gz="HACK" \ + -Dtomcat-native.home="." \ + -Dcommons-daemon.native.win.mgr.exe="HACK" \ + -Dnsis.exe="HACK" \ + -Djaxrpc-lib.jar="$(build-classpath jaxrpc)" \ + -Dwsdl4j-lib.jar="$(build-classpath wsdl4j)" \ + -Dcommons-pool.home="HACKDIR" \ + -Dcommons-dbcp.home="HACKDIR" \ + -Dno.build.dbcp=true \ + -Dversion="%{version}" \ + -Dversion.build="%{micro_version}" \ + -Djava.7.home=%{java_home} \ + deploy dist-prepare dist-source javadoc + + # remove some jars that we'll replace with symlinks later + %{__rm} output/build/bin/commons-daemon.jar \ + output/build/lib/ecj.jar \ + output/build/lib/apache-commons-dbcp.jar + + # remove the cruft we created + %{__rm} output/build/bin/tomcat-native.tar.gz +pushd output/dist/src/webapps/docs/appdev/sample/src +%{__mkdir_p} ../web/WEB-INF/classes +%{javac} -cp ../../../../../../../../output/build/lib/servlet-api.jar -d ../web/WEB-INF/classes mypackage/Hello.java +pushd ../web +%{jar} cf ../../../../../../../../output/build/webapps/docs/appdev/sample/sample.war * +popd +popd + +# inject OSGi manifests +mkdir -p META-INF +cp -p %{SOURCE8} META-INF/MANIFEST.MF +touch META-INF/MANIFEST.MF +zip -u output/build/lib/servlet-api.jar META-INF/MANIFEST.MF +cp -p %{SOURCE9} META-INF/MANIFEST.MF +touch META-INF/MANIFEST.MF +zip -u output/build/lib/jsp-api.jar META-INF/MANIFEST.MF +cp -p %{SOURCE12} META-INF/MANIFEST.MF +touch META-INF/MANIFEST.MF +zip -u output/build/lib/el-api.jar META-INF/MANIFEST.MF +cp -p %{SOURCE13} META-INF/MANIFEST.MF +touch META-INF/MANIFEST.MF +zip -u output/build/lib/jasper-el.jar META-INF/MANIFEST.MF +cp -p %{SOURCE14} META-INF/MANIFEST.MF +touch META-INF/MANIFEST.MF +zip -u output/build/lib/jasper.jar META-INF/MANIFEST.MF +cp -p %{SOURCE15} META-INF/MANIFEST.MF +touch META-INF/MANIFEST.MF +zip -u output/build/lib/tomcat-api.jar META-INF/MANIFEST.MF +cp -p %{SOURCE16} META-INF/MANIFEST.MF +touch META-INF/MANIFEST.MF +zip -u output/build/bin/tomcat-juli.jar META-INF/MANIFEST.MF + +%install +# build initial path structure +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_bindir} +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_sbindir} +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_javadocdir}/%{name} +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_initrddir} +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_systemddir} +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{appdir} +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{bindir} +%{__install} -d -m 0775 ${RPM_BUILD_ROOT}%{confdir} +%{__install} -d -m 0775 ${RPM_BUILD_ROOT}%{confdir}/Catalina/localhost +%{__install} -d -m 0775 ${RPM_BUILD_ROOT}%{confdir}/conf.d +/bin/echo "Place your custom *.conf files here. Shell expansion is supported." > ${RPM_BUILD_ROOT}%{confdir}/conf.d/README +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{libdir} +%{__install} -d -m 0775 ${RPM_BUILD_ROOT}%{logdir} +/bin/touch ${RPM_BUILD_ROOT}%{logdir}/catalina.out +%{__install} -d -m 0775 ${RPM_BUILD_ROOT}%{_localstatedir}/lib/tomcats +/bin/echo "%{name}-%{major_version}.%{minor_version}.%{micro_version} RPM installed" >> ${RPM_BUILD_ROOT}%{logdir}/catalina.out +%{__install} -d -m 0775 ${RPM_BUILD_ROOT}%{homedir} +%{__install} -d -m 0775 ${RPM_BUILD_ROOT}%{tempdir} +%{__install} -d -m 0775 ${RPM_BUILD_ROOT}%{workdir} +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_unitdir} +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_libexecdir}/%{name} + +# move things into place +# First copy supporting libs to tomcat lib +pushd output/build + %{__cp} -a bin/*.{jar,xml} ${RPM_BUILD_ROOT}%{bindir} + %{__cp} %{SOURCE10} conf/log4j.properties + %{__cp} -a conf/*.{policy,properties,xml} ${RPM_BUILD_ROOT}%{confdir} + %{__cp} -a lib/*.jar ${RPM_BUILD_ROOT}%{libdir} + %{__cp} -a webapps/* ${RPM_BUILD_ROOT}%{appdir} +popd +# javadoc +%{__cp} -a output/dist/webapps/docs/api/* ${RPM_BUILD_ROOT}%{_javadocdir}/%{name} + +%{__sed} -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \ + -e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \ + -e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE1} \ + > ${RPM_BUILD_ROOT}%{confdir}/%{name}.conf +%{__sed} -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \ + -e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \ + -e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE3} \ + > ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/%{name} +%{__install} -m 0644 %{SOURCE4} \ + ${RPM_BUILD_ROOT}%{_sbindir}/%{name} +%{__install} -m 0644 %{SOURCE11} \ + ${RPM_BUILD_ROOT}%{_unitdir}/%{name}.service +%{__install} -m 0644 %{SOURCE20} \ + ${RPM_BUILD_ROOT}%{_unitdir}/%{name}-jsvc.service +# %{__ln_s} %{name} ${RPM_BUILD_ROOT}%{_sbindir}/d%{name} +%{__sed} -e "s|\@\@\@TCLOG\@\@\@|%{logdir}|g" %{SOURCE5} \ + > ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name} +%{__sed} -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \ + -e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \ + -e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE6} \ + > ${RPM_BUILD_ROOT}%{_bindir}/%{name}-digest +%{__sed} -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \ + -e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \ + -e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE7} \ + > ${RPM_BUILD_ROOT}%{_bindir}/%{name}-tool-wrapper + +%{__install} -m 0644 %{SOURCE21} \ + ${RPM_BUILD_ROOT}%{_libexecdir}/%{name}/functions +%{__install} -m 0755 %{SOURCE22} \ + ${RPM_BUILD_ROOT}%{_libexecdir}/%{name}/preamble +%{__install} -m 0755 %{SOURCE23} \ + ${RPM_BUILD_ROOT}%{_libexecdir}/%{name}/server +%{__install} -m 0644 %{SOURCE24} \ + ${RPM_BUILD_ROOT}%{_unitdir}/%{name}@.service + +# create jsp and servlet API symlinks +pushd ${RPM_BUILD_ROOT}%{_javadir} + %{__mv} %{name}/jsp-api.jar %{name}-jsp-%{jspspec}-api.jar + %{__ln_s} %{name}-jsp-%{jspspec}-api.jar %{name}-jsp-api.jar + %{__mv} %{name}/servlet-api.jar %{name}-servlet-%{servletspec}-api.jar + %{__ln_s} %{name}-servlet-%{servletspec}-api.jar %{name}-servlet-api.jar + %{__mv} %{name}/el-api.jar %{name}-el-%{elspec}-api.jar + %{__ln_s} %{name}-el-%{elspec}-api.jar %{name}-el-api.jar +popd + +pushd output/build + %{_bindir}/build-jar-repository lib apache-commons-collections \ + apache-commons-dbcp apache-commons-pool ecj 2>&1 + # need to use -p here with b-j-r otherwise the examples webapp fails to + # load with a java.io.IOException + %{_bindir}/build-jar-repository -p webapps/examples/WEB-INF/lib \ + taglibs-core.jar taglibs-standard.jar 2>&1 +popd + +pushd ${RPM_BUILD_ROOT}%{libdir} + # symlink JSP and servlet API jars + %{__ln_s} ../%{name}-jsp-%{jspspec}-api.jar . + %{__ln_s} ../%{name}-servlet-%{servletspec}-api.jar . + %{__ln_s} ../%{name}-el-%{elspec}-api.jar . + %{__ln_s} $(build-classpath apache-commons-collections) commons-collections.jar + %{__ln_s} $(build-classpath apache-commons-dbcp) commons-dbcp.jar + %{__ln_s} $(build-classpath apache-commons-pool) commons-pool.jar + %{__ln_s} $(build-classpath log4j) log4j.jar + %{__ln_s} $(build-classpath ecj) jasper-jdt.jar + + # Temporary copy the juli jar here from /usr/share/java/tomcat (for maven depmap) + %{__cp} -a ${RPM_BUILD_ROOT}%{bindir}/tomcat-juli.jar ./ + + # Add extras JULI jars + %{__mkdir} extras + pushd extras + %{__cp} -p %{SOURCE25} . + %{__cp} -p %{SOURCE26} . + popd +popd + +# symlink to the FHS locations where we've installed things +pushd ${RPM_BUILD_ROOT}%{homedir} + %{__ln_s} %{appdir} webapps + %{__ln_s} %{confdir} conf + %{__ln_s} %{libdir} lib + %{__ln_s} %{logdir} logs + %{__ln_s} %{tempdir} temp + %{__ln_s} %{workdir} work +popd + +# install sample webapp +%{__mkdir_p} ${RPM_BUILD_ROOT}%{appdir}/sample +pushd ${RPM_BUILD_ROOT}%{appdir}/sample +%{jar} xf ${RPM_BUILD_ROOT}%{appdir}/docs/appdev/sample/sample.war +popd +%{__rm} ${RPM_BUILD_ROOT}%{appdir}/docs/appdev/sample/sample.war + +# Allow linking for example webapp +%{__mkdir_p} ${RPM_BUILD_ROOT}%{appdir}/examples/META-INF +pushd ${RPM_BUILD_ROOT}%{appdir}/examples/META-INF +echo ''>context.xml +echo ''>>context.xml +popd + +pushd ${RPM_BUILD_ROOT}%{appdir}/examples/WEB-INF/lib +%{__ln_s} -f $(build-classpath jakarta-taglibs-core) jstl.jar +%{__ln_s} -f $(build-classpath jakarta-taglibs-standard) standard.jar +popd + + +# Install the maven metadata +%{__install} -d -m 0755 ${RPM_BUILD_ROOT}%{_mavenpomdir} +pushd output/dist/src/res/maven +for pom in *.pom; do + # fix-up version in all pom files + sed -i 's/@MAVEN.DEPLOY.VERSION@/%{version}/g' $pom +done + +# we won't install dbcp, juli-adapters and juli-extras pom files +for libname in annotations-api catalina jasper-el jasper catalina-ha; do + %{__cp} -a %{name}-$libname.pom ${RPM_BUILD_ROOT}%{_mavenpomdir}/JPP.%{name}-$libname.pom + %add_maven_depmap JPP.%{name}-$libname.pom %{name}/$libname.jar +done + +# servlet-api jsp-api and el-api are not in tomcat subdir, since they are widely re-used elsewhere +%{__cp} -a tomcat-jsp-api.pom ${RPM_BUILD_ROOT}%{_mavenpomdir}/JPP-tomcat-jsp-api.pom +%add_maven_depmap JPP-tomcat-jsp-api.pom tomcat-jsp-api.jar -f "tomcat-jsp-api" -a "javax.servlet.jsp:javax.servlet.jsp-api,javax.servlet:jsp-api,org.eclipse.jetty.orbit:javax.servlet.jsp" + +%{__cp} -a tomcat-el-api.pom ${RPM_BUILD_ROOT}%{_mavenpomdir}/JPP-tomcat-el-api.pom +%add_maven_depmap JPP-tomcat-el-api.pom tomcat-el-api.jar -f "tomcat-el-api" -a "javax.el:javax.el-api,javax.el:el-api,org.eclipse.jetty.orbit:javax.el" + +%{__cp} -a tomcat-servlet-api.pom ${RPM_BUILD_ROOT}%{_mavenpomdir}/JPP-tomcat-servlet-api.pom +# Generate a depmap fragment javax.servlet:servlet-api pointing to +# tomcat-servlet-3.0-api for backwards compatibility +# also provide jetty depmap (originally in jetty package, but it's cleaner to have it here +%add_maven_depmap JPP-tomcat-servlet-api.pom tomcat-servlet-api.jar -f "tomcat-servlet-api" -a "javax.servlet:servlet-api,javax.servlet:javax.servlet-api,org.mortbay.jetty:servlet-api,org.eclipse.jetty.orbit:javax.servlet" + +# two special pom where jar files have different names +%{__cp} -a tomcat-tribes.pom ${RPM_BUILD_ROOT}%{_mavenpomdir}/JPP.%{name}-catalina-tribes.pom +%add_maven_depmap JPP.%{name}-catalina-tribes.pom %{name}/catalina-tribes.jar + +%{__cp} -a tomcat-coyote.pom ${RPM_BUILD_ROOT}%{_mavenpomdir}/JPP.%{name}-tomcat-coyote.pom +%add_maven_depmap JPP.%{name}-tomcat-coyote.pom %{name}/tomcat-coyote.jar + +%{__cp} -a tomcat-juli.pom ${RPM_BUILD_ROOT}%{_mavenpomdir}/JPP.%{name}-tomcat-juli.pom +%add_maven_depmap JPP.%{name}-tomcat-juli.pom %{name}/tomcat-juli.jar + +%{__cp} -a tomcat-api.pom ${RPM_BUILD_ROOT}%{_mavenpomdir}/JPP.%{name}-tomcat-api.pom +%add_maven_depmap JPP.%{name}-tomcat-api.pom %{name}/tomcat-api.jar + +%{__cp} -a tomcat-util.pom ${RPM_BUILD_ROOT}%{_mavenpomdir}/JPP.%{name}-tomcat-util.pom +%add_maven_depmap JPP.%{name}-tomcat-util.pom %{name}/tomcat-util.jar + +# replace temporary copy with link +%{__ln_s} -f %{bindir}/tomcat-juli.jar ${RPM_BUILD_ROOT}%{libdir}/ + + +%pre +# add the tomcat user and group +getent group tomcat >/dev/null || %{_sbindir}/groupadd -f -g %{tcuid} -r tomcat +if ! getent passwd tomcat >/dev/null ; then + if ! getent passwd %{tcuid} >/dev/null ; then + %{_sbindir}/useradd -r -u %{tcuid} -g tomcat -d %{homedir} -s /sbin/nologin -c "Apache Tomcat" tomcat + # Tomcat uses a reserved ID, so there should never be an else + fi +fi +exit 0 + +%post +# install but don't activate +%systemd_post %{name}.service + +%post jsp-%{jspspec}-api +%{_sbindir}/update-alternatives --install %{_javadir}/jsp.jar jsp \ + %{_javadir}/%{name}-jsp-%{jspspec}-api.jar 20200 + +%post servlet-%{servletspec}-api +%{_sbindir}/update-alternatives --install %{_javadir}/servlet.jar servlet \ + %{_javadir}/%{name}-servlet-%{servletspec}-api.jar 30000 + +%post el-%{elspec}-api +%{_sbindir}/update-alternatives --install %{_javadir}/elspec.jar elspec \ + %{_javadir}/%{name}-el-%{elspec}-api.jar 20300 + +%preun +# clean tempdir and workdir on removal or upgrade +%{__rm} -rf %{workdir}/* %{tempdir}/* +%systemd_preun %{name}.service + +%postun +%systemd_postun_with_restart %{name}.service + +%postun jsp-%{jspspec}-api +if [ "$1" = "0" ]; then + %{_sbindir}/update-alternatives --remove jsp \ + %{_javadir}/%{name}-jsp-%{jspspec}-api.jar +fi + +%postun servlet-%{servletspec}-api +if [ "$1" = "0" ]; then + %{_sbindir}/update-alternatives --remove servlet \ + %{_javadir}/%{name}-servlet-%{servletspec}-api.jar +fi + +%postun el-%{elspec}-api +if [ "$1" = "0" ]; then + %{_sbindir}/update-alternatives --remove elspec \ + %{_javadir}/%{name}-el-%{elspec}-api.jar +fi + +%triggerun -- tomcat < 0:7.0.22-2 +/usr/bin/systemd-sysv-convert -- save tomcat > /dev/null 2>&1 || : +# Run these becasue the SysV package being removed won't do them +/sbin/chkconfig --del tomcat > /dev/null 2>&1 || : +/bin/systemctl try-restart tomcat.service > /dev/null 2>&1 || : + +%files +%defattr(0664,root,tomcat,0755) +%doc {LICENSE,NOTICE,RELEASE*} +%attr(0755,root,root) %{_bindir}/%{name}-digest +%attr(0755,root,root) %{_bindir}/%{name}-tool-wrapper +%attr(0755,root,root) %{_sbindir}/%{name} +%attr(0644,root,root) %{_unitdir}/%{name}.service +%attr(0644,root,root) %{_unitdir}/%{name}@.service +%attr(0755,root,root) %dir %{_libexecdir}/%{name} +%attr(0755,root,root) %dir %{_localstatedir}/lib/tomcats +%attr(0644,root,root) %{_libexecdir}/%{name}/functions +%attr(0755,root,root) %{_libexecdir}/%{name}/preamble +%attr(0755,root,root) %{_libexecdir}/%{name}/server +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/logrotate.d/%{name} +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/sysconfig/%{name} +%attr(0755,root,tomcat) %dir %{basedir} +%attr(0755,root,tomcat) %dir %{confdir} + +%defattr(0664,tomcat,root,0770) +%attr(0770,tomcat,root) %dir %{logdir} + +%defattr(0664,root,tomcat,0770) +%attr(0660,tomcat,tomcat) %verify(not size md5 mtime) %{logdir}/catalina.out +%attr(0770,root,tomcat) %dir %{cachedir} +%attr(0770,root,tomcat) %dir %{tempdir} +%attr(0770,root,tomcat) %dir %{workdir} + +%defattr(0644,root,tomcat,0775) +%attr(0775,root,tomcat) %dir %{appdir} +%attr(0775,root,tomcat) %dir %{confdir}/Catalina +%attr(0775,root,tomcat) %dir %{confdir}/Catalina/localhost +%attr(0755,root,tomcat) %dir %{confdir}/conf.d +%{confdir}/conf.d/README +%config(noreplace) %{confdir}/%{name}.conf +%config(noreplace) %{confdir}/*.policy +%config(noreplace) %{confdir}/*.properties +%config(noreplace) %{confdir}/context.xml +%config(noreplace) %{confdir}/server.xml +%attr(0640,root,tomcat) %config(noreplace) %{confdir}/tomcat-users.xml +%config(noreplace) %{confdir}/web.xml +%dir %{homedir} +%{bindir}/bootstrap.jar +%{bindir}/catalina-tasks.xml +%{homedir}/lib +%{homedir}/temp +%{homedir}/webapps +%{homedir}/work +%{homedir}/logs +%{homedir}/conf + +%files admin-webapps +%defattr(0664,root,tomcat,0755) +%{appdir}/host-manager +%{appdir}/manager +%config(noreplace) %{appdir}/manager/WEB-INF/web.xml +%config(noreplace) %{appdir}/host-manager/WEB-INF/web.xml + +%files docs-webapp +%defattr(-,root,root,-) +%{appdir}/docs + +%files javadoc +%defattr(-,root,root,-) +%{_javadocdir}/%{name} + +%files jsp-%{jspspec}-api +%defattr(-,root,root,-) +%{_javadir}/%{name}-jsp-%{jspspec}*.jar +%{_javadir}/%{name}-jsp-api.jar +%{_mavenpomdir}/JPP-%{name}-jsp-api.pom +%{_mavendepmapfragdir}/%{name}-tomcat-jsp-api + +%files lib +%defattr(-,root,root,-) +%{libdir} +%{bindir}/tomcat-juli.jar +%{_mavendepmapfragdir}/%{name} +%{_mavenpomdir}/JPP.%{name}-annotations-api.pom +%{_mavenpomdir}/JPP.%{name}-catalina-ha.pom +%{_mavenpomdir}/JPP.%{name}-catalina-tribes.pom +%{_mavenpomdir}/JPP.%{name}-catalina.pom +%{_mavenpomdir}/JPP.%{name}-jasper-el.pom +%{_mavenpomdir}/JPP.%{name}-jasper.pom +%{_mavenpomdir}/JPP.%{name}-tomcat-api.pom +%{_mavenpomdir}/JPP.%{name}-tomcat-juli.pom +%{_mavenpomdir}/JPP.%{name}-tomcat-coyote.pom +%{_mavenpomdir}/JPP.%{name}-tomcat-util.pom + +%exclude %{libdir}/%{name}-el-%{elspec}-api.jar + +%files servlet-%{servletspec}-api +%defattr(-,root,root,-) +%doc LICENSE +%{_javadir}/%{name}-servlet-%{servletspec}*.jar +%{_javadir}/%{name}-servlet-api.jar +%{_mavendepmapfragdir}/%{name}-tomcat-servlet-api +%{_mavenpomdir}/JPP-%{name}-servlet-api.pom + +%files el-%{elspec}-api +%defattr(-,root,root,-) +%doc LICENSE +%{_javadir}/%{name}-el-%{elspec}-api.jar +%{_javadir}/%{name}-el-api.jar +%{libdir}/%{name}-el-%{elspec}-api.jar +%{_mavenpomdir}/JPP-%{name}-el-api.pom +%{_mavendepmapfragdir}/%{name}-tomcat-el-api + + +%files webapps +%defattr(0644,tomcat,tomcat,0755) +%{appdir}/ROOT +%{appdir}/examples +%{appdir}/sample + +%files jsvc +%defattr(755,root,root,0755) +%attr(0644,root,root) %{_unitdir}/%{name}-jsvc.service + +%changelog +* Tue Sep 03 2019 Coty Sutherland 0:7.0.76-10 +- Resolves: rhbz#1748541 Bump tomcat release number + +* 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 + +* Tue Jul 24 2018 Jean-Frederic Clere 0:7.0.76-7 +- Resolves: rhbz#1602060 Deadlock occurs while sending to a closing session + +* Wed Nov 08 2017 Coty Sutherland 0:7.0.76-6 +- Related: rhbz#1505762 Remove erroneous useradd + +* Tue Nov 07 2017 Coty Sutherland 0:7.0.76-5 +- Resolves: rhbz#1485453 man page uid and gid mismatch for service accounts +- Resolves: rhbz#1505762 Problem to start tomcat with a user whose group has a name different to the user + +* Mon Nov 06 2017 Coty Sutherland 0:7.0.76-3 +- Resolves: rhbz#1498343 CVE-2017-12615 CVE-2017-12617 tomcat: various flaws +- Resolves: rhbz#1495655 CVE-2017-7674 tomcat: Vary header not added by CORS filter leading to cache poisoning +- Resolves: rhbz#1470597 CVE-2017-5647 Add follow up revision + +* Thu Jun 08 2017 Coty Sutherland 0:7.0.76-2 +- Resolves: rhbz#1459747 CVE-2017-5664 tomcat: Security constrained bypass in error page mechanism +- Resolves: rhbz#1441481 CVE-2017-5647 tomcat: Incorrect handling of pipelined requests when send file was used + +* Wed Mar 29 2017 Coty Sutherland - 0:7.0.76-1 +- Resolves: rhbz#1414895 Rebase tomcat to the current release + +* Thu Aug 25 2016 Coty Sutherland - 0:7.0.69-10 +- Related: rhbz#1368122 + +* Tue Aug 23 2016 Coty Sutherland - 0:7.0.69-9 +- Resolves: rhbz#1362213 Tomcat: CGI sets environmental variable based on user supplied Proxy request header +- Resolves: rhbz#1368122 + +* Wed Aug 03 2016 Coty Sutherland - 0:7.0.69-7 +- Resolves: rhbz#1362545 + +* Fri Jul 08 2016 Coty Sutherland - 0:7.0.69-6 +- Related: rhbz#1201409 Added /etc/sysconfig/tomcat to the systemd unit for tomcat-jsvc.service + +* Fri Jul 01 2016 Coty Sutherland - 0:7.0.69-5 +- Resolves: rhbz#1347860 The systemd service unit does not allow tomcat to shut down gracefully + +* Mon Jun 27 2016 Coty Sutherland - 0:7.0.69-4 +- Resolves: rhbz#1350438 CVE-2016-3092 tomcat: Usage of vulnerable FileUpload package can result in denial of service + +* Fri Jun 17 2016 Coty Sutherland - 0:7.0.69-3 +- Resolves: rhbz#1347774 The security manager doesn't work correctly (JSPs cannot be compiled) + +* Tue Jun 07 2016 Coty Sutherland - 0:7.0.69-2 +- Rebase Resolves: rhbz#1311622 Getting NoSuchElementException while handling attributes with empty string value in tomcat +- Rebase Resolves: rhbz#1320853 Add HSTS support +- Rebase Resolves: rhbz#1293292 CVE-2014-7810 tomcat: Tomcat/JBossWeb: security manager bypass via EL expressions +- Rebase Resolves: rhbz#1347144 CVE-2016-0706 tomcat: security manager bypass via StatusManagerServlet +- Rebase Resolves: rhbz#1347139 CVE-2015-5346 tomcat: Session fixation +- Rebase Resolves: rhbz#1347136 CVE-2015-5345 tomcat: directory disclosure +- Rebase Resolves: rhbz#1347129 CVE-2015-5174 tomcat: URL Normalization issue +- Rebase Resolves: rhbz#1347146 CVE-2016-0763 tomcat: security manager bypass via setGlobalContext() +- Rebase Resolves: rhbz#1347142 CVE-2016-0714 tomcat: Security Manager bypass via persistence mechanisms +- Rebase Resolves: rhbz#1347133 CVE-2015-5351 tomcat: CSRF token leak + +* Mon Jun 06 2016 Coty Sutherland - 0:7.0.69-1 +- Resolves: rhbz#1287928 Rebase to tomcat 7.0.69 +- Resolves: rhbz#1327326 rpm -V tomcat fails on /var/log/tomcat/catalina.out +- Resolves: rhbz#1277197 tomcat user has non-existing default shell set +- Resolves: rhbz#1240279 The command tomcat-digest doesn't work with RHEL 7 +- Resolves: rhbz#1229476 Tomcat startup ONLY options +- Resolves: rhbz#1133070 Need to include full implementation of tomcat-juli.jar and tomcat-juli-adapters.jar +- Resolves: rhbz#1201409 Fix the broken tomcat-jsvc service unit +- Resolves: rhbz#1221896 tomcat.service loads /etc/sysconfig/tomcat without shell expansion +- Resolves: rhbz#1208402 Mark web.xml in tomcat-admin-webapps as config file + +* Tue Mar 24 2015 David Knox - 0:7.0.54-2 +- Resolves: CVE-2014-0227 + +* Wed Sep 17 2014 David Knox - 0:7.0.54-1 +- Resolves: rhbz#1141372 - Remove systemv artifacts. Add new systemd +- artifacts. Rebase on 7.0.54. + +* Wed Jun 18 2014 David Knox - 0:7.0.43-6 +- Resolves: CVE-2014-0099 +- Resolves: CVE-2014-0096 +- Resolves: CVE-2014-0075 + +* Wed Apr 16 2014 David Knox - 0:7.0.42-5 +- Related: CVE-2013-4286 +- Related: CVE-2013-4322 +- Related: CVE-2014-0050 +- revisit patches for above. + +* Thu Mar 20 2014 David Knox - 0:7.0.42-4 +- Related: rhbz#1056696 correct packaging for sbin tomcat + +* Thu Mar 20 2014 David Knox - 0:7.0.42-3 +- Related: CVE-2013-4286. increment build number. missed doing +- it. +- Resolves: rhbz#1038183 remove BR for ant-nodeps. it's +- no long used. + +* Wed Jan 22 2014 David Knox - 0:7.0.42-2 +- Resolves: rhbz#1056673 Invocation of useradd with shell +- other than sbin nologin +- Resolves: rhbz#1056677 preun systemv scriptlet unconditionally +- stops service +- Resolves: rhbz#1056696 init.d tomcat does not conform to RHEL7 +- systemd rules. systemv subpackage is removed. +- Resolves: CVE-2013-4286 +- Resolves: CVE-2013-4322 +- Resolves: CVE-2014-0050 +- Built for rhel-7 RC + +* Tue Jan 21 2014 David Knox - 0:7.0.42-1 +- Resolves: rhbz#1051657 update to 7.0.42. Ant-nodeps is +- deprecated. + +* Fri Dec 27 2013 Daniel Mach - 07.0.40-3 +- Mass rebuild 2013-12-27 + +* Sat May 11 2013 Ivan Afonichev 0:7.0.40-1 +- Updated to 7.0.40 +- Resolves: rhbz 956569 added missing commons-pool link + +* Mon Mar 4 2013 Mikolaj Izdebski - 0:7.0.37-2 +- Add depmaps for org.eclipse.jetty.orbit +- Resolves: rhbz#917626 + +* Wed Feb 20 2013 Ivan Afonichev 0:7.0.39-1 +- Updated to 7.0.39 + +* Wed Feb 20 2013 Ivan Afonichev 0:7.0.37-1 +- Updated to 7.0.37 + +* Mon Feb 4 2013 Ivan Afonichev 0:7.0.35-1 +- Updated to 7.0.35 +- systemd SuccessExitStatus=143 for proper stop exit code processing + +* Mon Dec 24 2012 Ivan Afonichev 0:7.0.34-1 +- Updated to 7.0.34 +- ecj >= 4.2.1 now required +- Resolves: rhbz 889395 concat classpath correctly; chdir to $CATALINA_HOME + +* Fri Dec 7 2012 Ivan Afonichev 0:7.0.33-2 +- Resolves: rhbz 883806 refix logdir ownership + +* Sun Dec 2 2012 Ivan Afonichev 0:7.0.33-1 +- Updated to 7.0.33 +- Resolves: rhbz 873620 need chkconfig for update-alternatives + +* Wed Oct 17 2012 Ivan Afonichev 0:7.0.32-1 +- Updated to 7.0.32 +- Resolves: rhbz 842620 symlinks to taglibs + +* Fri Aug 24 2012 Ivan Afonichev 0:7.0.29-1 +- Updated to 7.0.29 +- Add pidfile as tmpfile +- Use systemd for running as unprivileged user +- Resolves: rhbz 847751 upgrade path was broken +- Resolves: rhbz 850343 use new systemd-rpm macros + +* Sat Jul 21 2012 Fedora Release Engineering - 0:7.0.28-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Mon Jul 2 2012 Ivan Afonichev 0:7.0.28-1 +- Updated to 7.0.28 +- Resolves: rhbz 820119 Remove bundled apache-commons-dbcp +- Resolves: rhbz 814900 Added tomcat-coyote POM +- Resolves: rhbz 810775 Remove systemv stuff from %post scriptlet +- Remove redhat-lsb R + +* Mon Apr 9 2012 Ivan Afonichev 0:7.0.27-2 +- Fixed native download hack + +* Sat Apr 7 2012 Ivan Afonichev 0:7.0.27-1 +- Updated to 7.0.27 +- Fixed jakarta-taglibs-standard BR and R + +* Wed Mar 21 2012 Stanislav Ochotnicky - 0:7.0.26-2 +- Add more depmaps to J2EE apis to help jetty/glassfish updates + +* Wed Mar 14 2012 Juan Hernandez 0:7.0.26-2 +- Added the POM files for tomcat-api and tomcat-util (#803495) + +* Wed Feb 22 2012 Ivan Afonichev 0:7.0.26-1 +- Updated to 7.0.26 +- Bug 790334: Change ownership of logdir for logrotate + +* Thu Feb 16 2012 Krzysztof Daniel 0:7.0.25-4 +- Bug 790694: Priorities of jsp, servlet and el packages updated. + +* Wed Feb 8 2012 Krzysztof Daniel 0:7.0.25-3 +- Dropped indirect dependecy to tomcat 5 + +* Sun Jan 22 2012 Ivan Afonichev 0:7.0.25-2 +- Added hack for maven depmap of tomcat-juli absolute link [ -f ] pass correctly + +* Sat Jan 21 2012 Ivan Afonichev 0:7.0.25-1 +- Updated to 7.0.25 +- Removed EntityResolver patch (changes already in upstream sources) +- Place poms and depmaps in the same package as jars +- Added javax.servlet.descriptor to export-package of servlet-api +- Move several chkconfig actions and reqs to systemv subpackage +- New maven depmaps generation method +- Add patch to support java7. (patch sent upstream). +- Require java >= 1:1.6.0 + +* Fri Jan 13 2012 Krzysztof Daniel 0:7.0.23-5 +- Exported javax.servlet.* packages in version 3.0 as 2.6 to make + servlet-api compatible with Eclipse. + +* Thu Jan 12 2012 Ivan Afonichev 0:7.0.23-4 +- Move jsvc support to subpackage + +* Wed Jan 11 2012 Alexander Kurtakov 0:7.0.23-2 +- Add EntityResolver setter patch to jasper for jetty's need. (patch sent upstream). + +* Mon Dec 12 2011 Joseph D. Wagner 0:7.0.23-3 +- Added support to /usr/sbin/tomcat-sysd and /usr/sbin/tomcat for + starting tomcat with jsvc, which allows tomcat to perform some + privileged operations (e.g. bind to a port < 1024) and then switch + identity to a non-privileged user. Must add USE_JSVC="true" to + /etc/tomcat/tomcat.conf or /etc/sysconfig/tomcat. + +* Mon Nov 28 2011 Ivan Afonichev 0:7.0.23-1 +- Updated to 7.0.23 + +* Fri Nov 11 2011 Ivan Afonichev 0:7.0.22-2 +- Move tomcat-juli.jar to lib package +- Drop %%update_maven_depmap as in tomcat6 +- Provide native systemd unit file ported from tomcat6 + +* Thu Oct 6 2011 Ivan Afonichev 0:7.0.22-1 +- Updated to 7.0.22 + +* Mon Oct 03 2011 Rex Dieter - 0:7.0.21-3.1 +- rebuild (java), rel-eng#4932 + +* Mon Sep 26 2011 Ivan Afonichev 0:7.0.21-3 +- Fix basedir mode + +* Tue Sep 20 2011 Roland Grunberg 0:7.0.21-2 +- Add manifests for el-api, jasper-el, jasper, tomcat, and tomcat-juli. + +* Thu Sep 8 2011 Ivan Afonichev 0:7.0.21-1 +- Updated to 7.0.21 + +* Mon Aug 15 2011 Ivan Afonichev 0:7.0.20-3 +- Require java = 1:1.6.0 + +* Mon Aug 15 2011 Ivan Afonichev 0:7.0.20-2 +- Require java < 1.7.0 + +* Mon Aug 15 2011 Ivan Afonichev 0:7.0.20-1 +- Updated to 7.0.20 + +* Tue Jul 26 2011 Ivan Afonichev 0:7.0.19-1 +- Updated to 7.0.19 + +* Tue Jun 21 2011 Ivan Afonichev 0:7.0.16-1 +- Updated to 7.0.16 + +* Mon Jun 6 2011 Ivan Afonichev 0:7.0.14-3 +- Added initial systemd service +- Fix some paths + +* Sat May 21 2011 Ivan Afonichev 0:7.0.14-2 +- Fixed http source link +- Securify some permissions +- Added licenses for el-api and servlet-api +- Added dependency on jpackage-utils for the javadoc subpackage + +* Sat May 14 2011 Ivan Afonichev 0:7.0.14-1 +- Updated to 7.0.14 + +* Thu May 5 2011 Ivan Afonichev 0:7.0.12-4 +- Provided local paths for libs +- Fixed dependencies +- Fixed update temp/work cleanup + +* Mon May 2 2011 Ivan Afonichev 0:7.0.12-3 +- Fixed package groups +- Fixed some permissions +- Fixed some links +- Removed old tomcat6 crap + +* Thu Apr 28 2011 Ivan Afonichev 0:7.0.12-2 +- Package now named just tomcat instead of tomcat7 +- Removed Provides: %{name}-log4j +- Switched to apache-commons-* names instead of jakarta-commons-* . +- Remove the old changelog +- BR/R java >= 1:1.6.0 , same for java-devel +- Removed old tomcat6 crap + +* Wed Apr 27 2011 Ivan Afonichev 0:7.0.12-1 +- Tomcat7