Blame SOURCES/tomcat-7.0.42-CVE-2014-0096.patch

79b4cc
--- conf/web.xml.orig	2014-06-16 14:44:40.800412000 -0400
79b4cc
+++ conf/web.xml	2014-06-16 15:16:19.075817000 -0400
79b4cc
@@ -88,10 +88,10 @@
79b4cc
   
79b4cc
   
79b4cc
   
79b4cc
-  
79b4cc
-  
79b4cc
-  
79b4cc
-  
79b4cc
+  
79b4cc
+  
79b4cc
+  
79b4cc
+  
79b4cc
 
79b4cc
     <servlet>
79b4cc
         <servlet-name>default</servlet-name>
79b4cc
--- java/org/apache/catalina/servlets/DefaultServlet.java.orig	2014-06-16 14:44:40.820409000 -0400
79b4cc
+++ java/org/apache/catalina/servlets/DefaultServlet.java	2014-06-16 17:31:56.392767000 -0400
79b4cc
@@ -36,6 +36,7 @@
79b4cc
 import java.io.StringWriter;
79b4cc
 import java.util.ArrayList;
79b4cc
 import java.util.Iterator;
79b4cc
+import java.util.Locale;
79b4cc
 import java.util.StringTokenizer;
79b4cc
 
79b4cc
 import javax.naming.InitialContext;
79b4cc
@@ -53,10 +54,14 @@
79b4cc
 import javax.servlet.http.HttpServlet;
79b4cc
 import javax.servlet.http.HttpServletRequest;
79b4cc
 import javax.servlet.http.HttpServletResponse;
79b4cc
+import javax.xml.parsers.DocumentBuilder;
79b4cc
+import javax.xml.parsers.DocumentBuilderFactory;
79b4cc
+import javax.xml.parsers.ParserConfigurationException;
79b4cc
 import javax.xml.transform.Source;
79b4cc
 import javax.xml.transform.Transformer;
79b4cc
 import javax.xml.transform.TransformerException;
79b4cc
 import javax.xml.transform.TransformerFactory;
79b4cc
+import javax.xml.transform.dom.DOMSource;
79b4cc
 import javax.xml.transform.stream.StreamResult;
79b4cc
 import javax.xml.transform.stream.StreamSource;
79b4cc
 
79b4cc
@@ -72,7 +77,10 @@
79b4cc
 import org.apache.naming.resources.ResourceAttributes;
79b4cc
 import org.apache.tomcat.util.res.StringManager;
79b4cc
 
79b4cc
-
79b4cc
+import org.w3c.dom.Document;
79b4cc
+import org.xml.sax.InputSource;
79b4cc
+import org.xml.sax.SAXException;
79b4cc
+import org.xml.sax.ext.EntityResolver2;
79b4cc
 /**
79b4cc
  * 

The default resource-serving servlet for most web applications,

79b4cc
  * used to serve static resources such as HTML pages and images.
79b4cc
@@ -122,6 +130,10 @@
79b4cc
 
79b4cc
     private static final long serialVersionUID = 1L;
79b4cc
 
79b4cc
+    private static final DocumentBuilderFactory factory;
79b4cc
+
79b4cc
+    private static final SecureEntityResolver secureEntityResolver =
79b4cc
+            new SecureEntityResolver();
79b4cc
     // ----------------------------------------------------- Instance Variables
79b4cc
 
79b4cc
 
79b4cc
@@ -227,6 +239,9 @@
79b4cc
         urlEncoder.addSafeCharacter('.');
79b4cc
         urlEncoder.addSafeCharacter('*');
79b4cc
         urlEncoder.addSafeCharacter('/');
79b4cc
+        factory = DocumentBuilderFactory.newInstance();
79b4cc
+        factory.setNamespaceAware(true);
79b4cc
+        factory.setValidating(false);
79b4cc
     }
79b4cc
 
79b4cc
 
79b4cc
@@ -1243,13 +1258,11 @@
79b4cc
     protected InputStream render(String contextPath, CacheEntry cacheEntry)
79b4cc
         throws IOException, ServletException {
79b4cc
 
79b4cc
-        InputStream xsltInputStream =
79b4cc
-            findXsltInputStream(cacheEntry.context);
79b4cc
-
79b4cc
-        if (xsltInputStream==null) {
79b4cc
+        Source xsltSource = findXsltInputStream(cacheEntry.context);
79b4cc
+        if (xsltSource ==null) {
79b4cc
             return renderHtml(contextPath, cacheEntry);
79b4cc
         }
79b4cc
-        return renderXml(contextPath, cacheEntry, xsltInputStream);
79b4cc
+        return renderXml(contextPath, cacheEntry, xsltSource);
79b4cc
 
79b4cc
     }
79b4cc
 
79b4cc
@@ -1262,7 +1275,7 @@
79b4cc
      */
79b4cc
     protected InputStream renderXml(String contextPath,
79b4cc
                                     CacheEntry cacheEntry,
79b4cc
-                                    InputStream xsltInputStream)
79b4cc
+                                    Source xsltSource)
79b4cc
         throws IOException, ServletException {
79b4cc
 
79b4cc
         StringBuilder sb = new StringBuilder();
79b4cc
@@ -1356,8 +1369,7 @@
79b4cc
         try {
79b4cc
             TransformerFactory tFactory = TransformerFactory.newInstance();
79b4cc
             Source xmlSource = new StreamSource(new StringReader(sb.toString()));
79b4cc
-            Source xslSource = new StreamSource(xsltInputStream);
79b4cc
-            Transformer transformer = tFactory.newTransformer(xslSource);
79b4cc
+            Transformer transformer = tFactory.newTransformer(xsltSource);
79b4cc
 
79b4cc
             ByteArrayOutputStream stream = new ByteArrayOutputStream();
79b4cc
             OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
79b4cc
@@ -1578,7 +1590,7 @@
79b4cc
     /**
79b4cc
      * Return the xsl template inputstream (if possible)
79b4cc
      */
79b4cc
-    protected InputStream findXsltInputStream(DirContext directory)
79b4cc
+    protected Source findXsltInputStream(DirContext directory)
79b4cc
         throws IOException {
79b4cc
 
79b4cc
         if (localXsltFile != null) {
79b4cc
@@ -1586,8 +1598,13 @@
79b4cc
                 Object obj = directory.lookup(localXsltFile);
79b4cc
                 if ((obj != null) && (obj instanceof Resource)) {
79b4cc
                     InputStream is = ((Resource) obj).streamContent();
79b4cc
-                    if (is != null)
79b4cc
-                        return is;
79b4cc
+                    if (is != null) {
79b4cc
+                        if (Globals.IS_SECURITY_ENABLED) {
79b4cc
+                            return secureXslt(is);
79b4cc
+                        } else {
79b4cc
+                            return new StreamSource(is);
79b4cc
+                        }
79b4cc
+                    }
79b4cc
                 }
79b4cc
             } catch (NamingException e) {
79b4cc
                 if (debug > 10)
79b4cc
@@ -1598,8 +1615,13 @@
79b4cc
         if (contextXsltFile != null) {
79b4cc
             InputStream is =
79b4cc
                 getServletContext().getResourceAsStream(contextXsltFile);
79b4cc
-            if (is != null)
79b4cc
-                return is;
79b4cc
+            if (is != null) {
79b4cc
+                if (Globals.IS_SECURITY_ENABLED) {
79b4cc
+                    return secureXslt(is);
79b4cc
+                } else {
79b4cc
+                    return new StreamSource(is);
79b4cc
+                }
79b4cc
+            }
79b4cc
 
79b4cc
             if (debug > 10)
79b4cc
                 log("contextXsltFile '" + contextXsltFile + "' not found");
79b4cc
@@ -1608,25 +1630,111 @@
79b4cc
         /*  Open and read in file in one fell swoop to reduce chance
79b4cc
          *  chance of leaving handle open.
79b4cc
          */
79b4cc
-        if (globalXsltFile!=null) {
79b4cc
-            FileInputStream fis = null;
79b4cc
+       if (globalXsltFile != null) {
79b4cc
+          File f = validateGlobalXsltFile();
79b4cc
+          if (f != null ){
79b4cc
+              FileInputStream fis = null;
79b4cc
+              try {
79b4cc
+                 fis = new FileInputStream(f);
79b4cc
+                 byte b[] = new byte[(int)f.length()]; /* danger! */
79b4cc
+                 fis.read(b);
79b4cc
+                 return new StreamSource(new ByteArrayInputStream(b));
79b4cc
+              } finally {
79b4cc
+                  if (fis != null) {
79b4cc
+                      try {
79b4cc
+                          fis.close();
79b4cc
+                      } catch(IOException ioe) {
79b4cc
+                          // ignore
79b4cc
+                      }
79b4cc
+                  }
79b4cc
+              }
79b4cc
+           }
79b4cc
+        }
79b4cc
 
79b4cc
-            try {
79b4cc
-                File f = new File(globalXsltFile);
79b4cc
-                if (f.exists()){
79b4cc
-                    fis =new FileInputStream(f);
79b4cc
-                    byte b[] = new byte[(int)f.length()]; /* danger! */
79b4cc
-                    fis.read(b);
79b4cc
-                    return new ByteArrayInputStream(b);
79b4cc
-                }
79b4cc
-            } finally {
79b4cc
-                if (fis!=null)
79b4cc
-                    fis.close();
79b4cc
+        return null;
79b4cc
+
79b4cc
+    }
79b4cc
+
79b4cc
+    private File validateGlobalXsltFile() {
79b4cc
+        
79b4cc
+        File result = null;
79b4cc
+        String base = System.getProperty(Globals.CATALINA_BASE_PROP);
79b4cc
+        
79b4cc
+        if (base != null) {
79b4cc
+            File baseConf = new File(base, "conf");
79b4cc
+            result = validateGlobalXsltFile(baseConf);
79b4cc
+        }
79b4cc
+        
79b4cc
+        if (result == null) {
79b4cc
+            String home = System.getProperty(Globals.CATALINA_HOME_PROP);
79b4cc
+            if (home != null && !home.equals(base)) {
79b4cc
+                File homeConf = new File(home, "conf");
79b4cc
+                result = validateGlobalXsltFile(homeConf);
79b4cc
             }
79b4cc
         }
79b4cc
 
79b4cc
-        return null;
79b4cc
+        return result;
79b4cc
+    }
79b4cc
+
79b4cc
 
79b4cc
+    private File validateGlobalXsltFile(File base) {
79b4cc
+        File candidate = new File(globalXsltFile);
79b4cc
+        if (!candidate.isAbsolute()) {
79b4cc
+            candidate = new File(base, globalXsltFile);
79b4cc
+        }
79b4cc
+
79b4cc
+        if (!candidate.isFile()) {
79b4cc
+            return null;
79b4cc
+        }
79b4cc
+
79b4cc
+        // First check that the resulting path is under the provided base
79b4cc
+        try {
79b4cc
+            if (!candidate.getCanonicalPath().startsWith(base.getCanonicalPath())) {
79b4cc
+                return null;
79b4cc
+            }
79b4cc
+        } catch (IOException ioe) {
79b4cc
+            return null;
79b4cc
+        }
79b4cc
+
79b4cc
+        // Next check that an .xsl or .xslt file has been specified
79b4cc
+        String nameLower = candidate.getName().toLowerCase(Locale.ENGLISH);
79b4cc
+        if (!nameLower.endsWith(".xslt") && !nameLower.endsWith(".xsl")) {
79b4cc
+            return null;
79b4cc
+        }
79b4cc
+
79b4cc
+        return candidate;
79b4cc
+    }
79b4cc
+
79b4cc
+    private Source secureXslt(InputStream is) {
79b4cc
+        // Need to filter out any external entities
79b4cc
+        Source result = null;
79b4cc
+        try {
79b4cc
+            DocumentBuilder builder = factory.newDocumentBuilder();
79b4cc
+            builder.setEntityResolver(secureEntityResolver);
79b4cc
+            Document document = builder.parse(is);
79b4cc
+            result = new DOMSource(document);
79b4cc
+        } catch (ParserConfigurationException e) {
79b4cc
+            if (debug > 0) {
79b4cc
+                log(e.getMessage(), e);
79b4cc
+            }
79b4cc
+        } catch (SAXException e) {
79b4cc
+            if (debug > 0) {
79b4cc
+                log(e.getMessage(), e);
79b4cc
+            }
79b4cc
+        } catch (IOException e) {
79b4cc
+            if (debug > 0) {
79b4cc
+                log(e.getMessage(), e);
79b4cc
+            }
79b4cc
+        } finally {
79b4cc
+            if (is != null) {
79b4cc
+                try {
79b4cc
+                    is.close();
79b4cc
+                } catch (IOException e) {
79b4cc
+                    // Ignore
79b4cc
+                }
79b4cc
+            }
79b4cc
+        }
79b4cc
+        return result;
79b4cc
     }
79b4cc
 
79b4cc
 
79b4cc
@@ -2150,4 +2258,34 @@
79b4cc
             return (start >= 0) && (end >= 0) && (start <= end) && (length > 0);
79b4cc
         }
79b4cc
     }
79b4cc
+
79b4cc
+    /**
79b4cc
+     * This is secure in the sense that any attempt to use an external entity
79b4cc
+     * will trigger an exception.
79b4cc
+     */
79b4cc
+    private static class SecureEntityResolver implements EntityResolver2  {
79b4cc
+
79b4cc
+        @Override
79b4cc
+        public InputSource resolveEntity(String publicId, String systemId)
79b4cc
+                throws SAXException, IOException {
79b4cc
+            throw new SAXException(sm.getString("defaultServlet.blockExternalEntity",
79b4cc
+                    publicId, systemId));
79b4cc
+        }
79b4cc
+
79b4cc
+        @Override
79b4cc
+        public InputSource getExternalSubset(String name, String baseURI)
79b4cc
+                throws SAXException, IOException {
79b4cc
+            throw new SAXException(sm.getString("defaultServlet.blockExternalSubset",
79b4cc
+                    name, baseURI));
79b4cc
+        }
79b4cc
+
79b4cc
+        @Override
79b4cc
+        public InputSource resolveEntity(String name, String publicId,
79b4cc
+                String baseURI, String systemId) throws SAXException,
79b4cc
+                IOException {
79b4cc
+            throw new SAXException(sm.getString("defaultServlet.blockExternalEntity2",
79b4cc
+                    name, publicId, baseURI, systemId));
79b4cc
+        }
79b4cc
+    }
79b4cc
 }
79b4cc
+
79b4cc
--- java/org/apache/catalina/servlets/LocalStrings.properties.orig	2014-06-16 14:44:40.830411000 -0400
79b4cc
+++ java/org/apache/catalina/servlets/LocalStrings.properties	2014-06-16 16:15:08.577726000 -0400
79b4cc
@@ -13,6 +13,10 @@
79b4cc
 # See the License for the specific language governing permissions and
79b4cc
 # limitations under the License.
79b4cc
 
79b4cc
+
79b4cc
+defaultServlet.blockExternalEntity=Blocked access to external entity with publicId [{0}] and systemId [{0}]
79b4cc
+defaultServlet.blockExternalEntity2=Blocked access to external entity with name [{0}], publicId [{1}], baseURI [{2}] and systemId [{3}]
79b4cc
+defaultServlet.blockExternalSubset=Blocked access to external subset with name [{0}] and baseURI [{1}]
79b4cc
 defaultServlet.missingResource=The requested resource ({0}) is not available
79b4cc
 defaultservlet.directorylistingfor=Directory Listing for:
79b4cc
 defaultservlet.upto=Up to:
79b4cc
--- webapps/docs/default-servlet.xml.orig	2014-06-16 14:44:40.836413000 -0400
79b4cc
+++ webapps/docs/default-servlet.xml	2014-06-16 16:17:41.419241000 -0400
79b4cc
@@ -110,22 +110,24 @@
79b4cc
     globalXsltFile
79b4cc
     
79b4cc
         If you wish to customize your directory listing, you
79b4cc
-        can use an XSL transformation. This value is an absolute
79b4cc
-        file name which be used for all directory listings.
79b4cc
-        This can be overridden per context and/or per directory. See
79b4cc
-        contextXsltFile and localXsltFile
79b4cc
-        below. The format of the xml is shown below.
79b4cc
+        can use an XSL transformation. This value is a relative file name (to
79b4cc
+         either $CATALINA_BASE/conf/ or $CATALINA_HOME/conf/) which will be used
79b4cc
+         for all directory listings. This can be overridden per context and/or
79b4cc
+         per directory. See contextXsltFile and
79b4cc
+         localXsltFile below. The format of the xml is shown
79b4cc
+         below.
79b4cc
     
79b4cc
   
79b4cc
   
79b4cc
     contextXsltFile
79b4cc
     
79b4cc
         You may also customize your directory listing by context by
79b4cc
-        configuring contextXsltFile. This should be a context
79b4cc
-        relative path (e.g.: /path/to/context.xslt). This
79b4cc
-        overrides globalXsltFile. If this value is present but a
79b4cc
-        file does not exist, then globalXsltFile will be used. If
79b4cc
         globalXsltFile does not exist, then the default
79b4cc
+        configuring contextXsltFile. This must be a context
79b4cc
+        relative path (e.g.: /path/to/context.xslt) to a file with
79b4cc
+        a .xsl or .xslt extension. This overrides
79b4cc
+        globalXsltFile. If this value is present but a file does
79b4cc
+        not exist, then globalXsltFile will be used. If
79b4cc
         directory listing will be shown.
79b4cc
     
79b4cc
   
79b4cc
@@ -133,11 +135,12 @@
79b4cc
     localXsltFile
79b4cc
     
79b4cc
         You may also customize your directory listing by directory by
79b4cc
-        configuring localXsltFile. This should be a relative
79b4cc
-        file name in the directory where the listing will take place.
79b4cc
-        This overrides globalXsltFile and
79b4cc
-        contextXsltFile. If this value is present but a file
79b4cc
-        does not exist, then contextXsltFile will be used. If
79b4cc
+        configuring localXsltFile. This must be a file in the
79b4cc
+        directory where the listing will take place to with a
79b4cc
+        .xsl or .xslt extension. This overrides
79b4cc
+        globalXsltFile and contextXsltFile. If this
79b4cc
+        value is present but a file does not exist, then
79b4cc
+        contextXsltFile will be used. If
79b4cc
         contextXsltFile does not exist, then
79b4cc
         globalXsltFile will be used. If
79b4cc
         globalXsltFile does not exist, then the default