Blob Blame History Raw
--- java/org/apache/catalina/security/SecurityClassLoad.java.orig	2014-07-24 18:29:38.830023000 -0400
+++ java/org/apache/catalina/security/SecurityClassLoad.java	2014-07-24 18:31:18.655356000 -0400
@@ -25,9 +25,7 @@
  *
  * @author Glenn L. Nielsen
  * @author Jean-Francois Arcand
- * @version $Id: SecurityClassLoad.java 1410548 2012-11-16 19:38:51Z markt $
  */
-
 public final class SecurityClassLoad {
 
     public static void securityClassLoad(ClassLoader loader)
@@ -41,6 +39,7 @@
         loadCoyotePackage(loader);
         loadLoaderPackage(loader);
         loadRealmPackage(loader);
+        loadServletsPackage(loader);
         loadSessionPackage(loader);
         loadUtilPackage(loader);
         loadValvesPackage(loader);
@@ -76,12 +75,6 @@
             "AsyncContextImpl$1");
         loader.loadClass
             (basePackage +
-            "AsyncContextImpl$PrivilegedGetTccl");
-        loader.loadClass
-            (basePackage +
-            "AsyncContextImpl$PrivilegedSetTccl");
-        loader.loadClass
-            (basePackage +
             "AsyncListenerWrapper");
         loader.loadClass
             (basePackage +
@@ -124,14 +117,24 @@
     }
 
 
+    private static final void loadServletsPackage(ClassLoader loader)
+            throws Exception {
+        final String basePackage = "org.apache.catalina.servlets.";
+        // Avoid a possible memory leak in the DefaultServlet when running with
+        // a security manager. The DefaultServlet needs to load an XML parser
+        // when running under a security manager. We want this to be loaded by
+        // the container rather than a web application to prevent a memory leak
+        // via web application class loader.
+        loader.loadClass(basePackage + "DefaultServlet");
+    }
+
+
     private static final void loadSessionPackage(ClassLoader loader)
         throws Exception {
         final String basePackage = "org.apache.catalina.session.";
         loader.loadClass
             (basePackage + "StandardSession");
         loader.loadClass
-            (basePackage + "StandardSession$PrivilegedSetTccl");
-        loader.loadClass
             (basePackage + "StandardSession$1");
         loader.loadClass
             (basePackage + "StandardManager$PrivilegedDoUnload");
@@ -280,10 +283,9 @@
         loader.loadClass(basePackage +
                 "util.net.NioBlockingSelector$BlockPoller$3");
         loader.loadClass(basePackage + "util.net.SSLSupport$CipherData");
-        loader.loadClass
-            (basePackage + "util.net.JIoEndpoint$PrivilegedSetTccl");
-        loader.loadClass
-            (basePackage + "util.net.AprEndpoint$PrivilegedSetTccl");
+        // security
+        loader.loadClass(basePackage + "util.security.PrivilegedGetTccl");
+        loader.loadClass(basePackage + "util.security.PrivilegedSetTccl");
     }
 }
 
--- java/org/apache/catalina/servlets/DefaultServlet.java.orig	2014-07-24 18:29:38.875020000 -0400
+++ java/org/apache/catalina/servlets/DefaultServlet.java	2014-07-24 18:31:18.695352000 -0400
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
 package org.apache.catalina.servlets;
 
 
@@ -34,6 +32,7 @@
 import java.io.Reader;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.security.AccessController;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Locale;
@@ -76,11 +75,14 @@
 import org.apache.naming.resources.Resource;
 import org.apache.naming.resources.ResourceAttributes;
 import org.apache.tomcat.util.res.StringManager;
-
+import org.apache.tomcat.util.security.PrivilegedGetTccl;
+import org.apache.tomcat.util.security.PrivilegedSetTccl;
 import org.w3c.dom.Document;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 import org.xml.sax.ext.EntityResolver2;
+
+
 /**
  * <p>The default resource-serving servlet for most web applications,
  * used to serve static resources such as HTML pages and images.
@@ -122,9 +124,7 @@
  * </p>
  * @author Craig R. McClanahan
  * @author Remy Maucherat
- * @version $Id: DefaultServlet.java 1301255 2012-03-15 22:47:40Z markt $
  */
-
 public class DefaultServlet
     extends HttpServlet {
 
@@ -132,10 +132,10 @@
 
     private static final DocumentBuilderFactory factory;
 
-    private static final SecureEntityResolver secureEntityResolver =
-            new SecureEntityResolver();
-    // ----------------------------------------------------- Instance Variables
+    private static final SecureEntityResolver secureEntityResolver;
+
 
+    // ----------------------------------------------------- Instance Variables
 
     /**
      * The debugging detail level for this servlet.
@@ -225,6 +225,11 @@
      */
     protected static final ArrayList<Range> FULL = new ArrayList<Range>();
 
+    /**
+     * Flag to determine if server information is presented.
+     */
+    protected boolean showServerInfo = true;
+
 
     // ----------------------------------------------------- Static Initializer
 
@@ -239,9 +244,16 @@
         urlEncoder.addSafeCharacter('.');
         urlEncoder.addSafeCharacter('*');
         urlEncoder.addSafeCharacter('/');
-        factory = DocumentBuilderFactory.newInstance();
-        factory.setNamespaceAware(true);
-        factory.setValidating(false);
+        
+        if (Globals.IS_SECURITY_ENABLED) {
+            factory = DocumentBuilderFactory.newInstance();
+            factory.setNamespaceAware(true);
+            factory.setValidating(false);
+            secureEntityResolver = new SecureEntityResolver();
+        } else {
+            factory = null;
+            secureEntityResolver = null;
+        }
     }
 
 
@@ -270,6 +282,7 @@
     protected static final int BUFFER_SIZE = 4096;
 
 
+
     // --------------------------------------------------------- Public Methods
 
 
@@ -345,6 +358,9 @@
             throw new UnavailableException("No resources");
         }
 
+        if (getServletConfig().getInitParameter("showServerInfo") != null) {
+            showServerInfo = Boolean.parseBoolean(getServletConfig().getInitParameter("showServerInfo"));
+        }
     }
 
 
@@ -1251,7 +1267,6 @@
     }
 
 
-
     /**
      *  Decide which way to render. HTML or XML.
      */
@@ -1259,13 +1274,15 @@
         throws IOException, ServletException {
 
         Source xsltSource = findXsltInputStream(cacheEntry.context);
-        if (xsltSource ==null) {
+
+        if (xsltSource == null) {
             return renderHtml(contextPath, cacheEntry);
         }
         return renderXml(contextPath, cacheEntry, xsltSource);
 
     }
 
+    
     /**
      * Return an InputStream to an HTML representation of the contents
      * of this directory.
@@ -1362,11 +1379,27 @@
             sb.append("]]></readme>");
         }
 
-
         sb.append("</listing>");
 
-
+        // Prevent possible memory leak. Ensure Transformer and
+        // TransformerFactory are not loaded from the web application.
+        ClassLoader original;
+        if (Globals.IS_SECURITY_ENABLED) {
+            PrivilegedGetTccl pa = new PrivilegedGetTccl();
+            original = AccessController.doPrivileged(pa);
+        } else {
+            original = Thread.currentThread().getContextClassLoader();
+        }
         try {
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedSetTccl pa =
+                        new PrivilegedSetTccl(DefaultServlet.class.getClassLoader());
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(
+                        DefaultServlet.class.getClassLoader());
+            }
+
             TransformerFactory tFactory = TransformerFactory.newInstance();
             Source xmlSource = new StreamSource(new StringReader(sb.toString()));
             Transformer transformer = tFactory.newTransformer(xsltSource);
@@ -1379,6 +1412,13 @@
             return (new ByteArrayInputStream(stream.toByteArray()));
         } catch (TransformerException e) {
             throw new ServletException("XSL transformer error", e);
+        } finally {
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedSetTccl pa = new PrivilegedSetTccl(original);
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(original);
+            }
         }
     }
 
@@ -1530,7 +1570,9 @@
             sb.append("<HR size=\"1\" noshade=\"noshade\">");
         }
 
-        sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
+        if (showServerInfo) {
+            sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
+        }
         sb.append("</body>\r\n");
         sb.append("</html>\r\n");
 
@@ -1588,7 +1630,7 @@
 
 
     /**
-     * Return the xsl template inputstream (if possible)
+     * Return a Source for the xsl template (if possible)
      */
     protected Source findXsltInputStream(DirContext directory)
         throws IOException {
@@ -1630,31 +1672,32 @@
         /*  Open and read in file in one fell swoop to reduce chance
          *  chance of leaving handle open.
          */
-       if (globalXsltFile != null) {
-          File f = validateGlobalXsltFile();
-          if (f != null ){
-              FileInputStream fis = null;
-              try {
-                 fis = new FileInputStream(f);
-                 byte b[] = new byte[(int)f.length()]; /* danger! */
-                 fis.read(b);
-                 return new StreamSource(new ByteArrayInputStream(b));
-              } finally {
-                  if (fis != null) {
-                      try {
-                          fis.close();
-                      } catch(IOException ioe) {
-                          // ignore
-                      }
-                  }
-              }
-           }
+        if (globalXsltFile != null) {
+            File f = validateGlobalXsltFile();
+            if (f != null){
+                FileInputStream fis = null;
+                try {
+                    fis = new FileInputStream(f);
+                    byte b[] = new byte[(int)f.length()]; /* danger! */
+                    fis.read(b);
+                    return new StreamSource(new ByteArrayInputStream(b));
+                } finally {
+                    if (fis != null) {
+                        try {
+                            fis.close();
+                        } catch (IOException ioe) {
+                            // Ignore
+                        }
+                    }
+                }
+            }
         }
 
         return null;
 
     }
 
+
     private File validateGlobalXsltFile() {
         
         File result = null;
@@ -1705,6 +1748,7 @@
         return candidate;
     }
 
+
     private Source secureXslt(InputStream is) {
         // Need to filter out any external entities
         Source result = null;
@@ -1740,7 +1784,6 @@
 
     // -------------------------------------------------------- protected Methods
 
-
     /**
      * Check if sendfile can be used.
      */
@@ -2240,9 +2283,6 @@
     }
 
 
-    // ------------------------------------------------------ Range Inner Class
-
-
     protected static class Range {
 
         public long start;
@@ -2259,6 +2299,7 @@
         }
     }
 
+
     /**
      * This is secure in the sense that any attempt to use an external entity
      * will trigger an exception.
@@ -2288,4 +2329,3 @@
         }
     }
 }
-
--- webapps/docs/changelog.xml.orig	2014-07-24 18:29:38.917021000 -0400
+++ webapps/docs/changelog.xml	2014-07-24 18:36:52.526481000 -0400
@@ -158,6 +158,15 @@
         <bug>55176</bug>: Correctly handle regular expressions within SSI
         expressions that contain an equals character. (markt)
       </fix>
+      <fix>
+        Ensure that a TLD parser obtained from the cache has the correct value
+        of <code>blockExternal</code>. (markt)
+      </fix>
+      <add>
+        Extend XML factory, parser etc. memory leak protection to cover some
+        additional locations where, theoretically, a memory leak could occur.
+        (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Coyote">
@@ -352,6 +361,10 @@
         request if the CRLF terminating the request line was split across
         multiple packets. Patch by Konstantin Preißer. (markt)
       </fix>
+      <fix>
+        Only create XML parsing objects if required and fix associated potential
+        memory leak in the default Servlet. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Jasper">
--- java/org/apache/jasper/xmlparser/ParserUtils.java.orig	2014-07-24 18:29:38.952016000 -0400
+++ java/org/apache/jasper/xmlparser/ParserUtils.java	2014-07-24 18:31:18.746354000 -0400
@@ -18,6 +18,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.security.AccessController;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -29,6 +30,8 @@
 import org.apache.tomcat.util.descriptor.DigesterFactory;
 import org.apache.tomcat.util.descriptor.LocalResolver;
 import org.apache.tomcat.util.descriptor.XmlErrorHandler;
+import org.apache.tomcat.util.security.PrivilegedGetTccl;
+import org.apache.tomcat.util.security.PrivilegedSetTccl;
 import org.w3c.dom.Comment;
 import org.w3c.dom.Document;
 import org.w3c.dom.NamedNodeMap;
@@ -36,7 +39,6 @@
 import org.w3c.dom.NodeList;
 import org.w3c.dom.Text;
 import org.xml.sax.EntityResolver;
-import org.xml.sax.ErrorHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
@@ -48,16 +50,10 @@
  * use a separate class loader for the parser to be used.
  *
  * @author Craig R. McClanahan
- * @version $Id: ParserUtils.java 1549529 2013-12-09 10:05:56Z markt $
  */
 public class ParserUtils {
 
     /**
-     * An error handler for use when parsing XML documents.
-     */
-    static ErrorHandler errorHandler = new XmlErrorHandler();
-
-    /**
      * An entity resolver for use when parsing XML documents.
      */
     static EntityResolver entityResolver;
@@ -99,15 +95,46 @@
         Document document = null;
 
         // Perform an XML parse of this document, via JAXP
+        ClassLoader original;
+        if (Constants.IS_SECURITY_ENABLED) {
+            PrivilegedGetTccl pa = new PrivilegedGetTccl();
+            original = AccessController.doPrivileged(pa);
+        } else {
+            original = Thread.currentThread().getContextClassLoader();
+        }
         try {
+            if (Constants.IS_SECURITY_ENABLED) {
+                PrivilegedSetTccl pa =
+                        new PrivilegedSetTccl(ParserUtils.class.getClassLoader());
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(
+                        ParserUtils.class.getClassLoader());
+            }
+            
             DocumentBuilderFactory factory =
                 DocumentBuilderFactory.newInstance();
             factory.setNamespaceAware(true);
             factory.setValidating(validating);
+            if (validating) {
+                // Enable DTD validation
+                factory.setFeature(
+                        "http://xml.org/sax/features/validation",
+                        true);
+                // Enable schema validation
+                factory.setFeature(
+                        "http://apache.org/xml/features/validation/schema",
+                        true);
+            }
             DocumentBuilder builder = factory.newDocumentBuilder();
             builder.setEntityResolver(entityResolverInstance);
-            builder.setErrorHandler(errorHandler);
+            XmlErrorHandler handler = new XmlErrorHandler();
+            builder.setErrorHandler(handler);
             document = builder.parse(is);
+            if (!handler.getErrors().isEmpty()) {
+                // throw the first to indicate there was a error during processing
+                throw handler.getErrors().iterator().next();
+            }
         } catch (ParserConfigurationException ex) {
             throw new JasperException
                 (Localizer.getMessage("jsp.error.parse.xml", location), ex);
@@ -124,6 +151,13 @@
         } catch (IOException io) {
             throw new JasperException
                 (Localizer.getMessage("jsp.error.parse.xml", location), io);
+        } finally {
+            if (Constants.IS_SECURITY_ENABLED) {
+                PrivilegedSetTccl pa = new PrivilegedSetTccl(original);
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(original);
+            }
         }
 
         // Convert the resulting document to a graph of TreeNodes
--- java/org/apache/jasper/compiler/JspDocumentParser.java.orig	2014-07-24 18:29:38.974016000 -0400
+++ java/org/apache/jasper/compiler/JspDocumentParser.java	2014-07-24 18:31:18.774352000 -0400
@@ -20,6 +20,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.security.AccessController;
 import java.util.Iterator;
 import java.util.List;
 import java.util.jar.JarFile;
@@ -35,6 +36,8 @@
 import org.apache.jasper.JspCompilationContext;
 import org.apache.tomcat.util.descriptor.DigesterFactory;
 import org.apache.tomcat.util.descriptor.LocalResolver;
+import org.apache.tomcat.util.security.PrivilegedGetTccl;
+import org.apache.tomcat.util.security.PrivilegedSetTccl;
 import org.xml.sax.Attributes;
 import org.xml.sax.InputSource;
 import org.xml.sax.Locator;
@@ -129,7 +132,7 @@
                 Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
         boolean blockExternal;
         if (blockExternalString == null) {
-            blockExternal = Constants.IS_SECURITY_ENABLED;
+            blockExternal = true;
         } else {
             blockExternal = Boolean.parseBoolean(blockExternalString);
         }
@@ -1464,33 +1467,58 @@
         JspDocumentParser jspDocParser)
         throws Exception {
 
-        SAXParserFactory factory = SAXParserFactory.newInstance();
-
-        factory.setNamespaceAware(true);
-        // Preserve xmlns attributes
-        factory.setFeature(
-            "http://xml.org/sax/features/namespace-prefixes",
-            true);
-
-        factory.setValidating(validating);
-        if (validating) {
-            // Enable DTD validation
-            factory.setFeature(
-                    "http://xml.org/sax/features/validation",
-                    true);
-            // Enable schema validation
-            factory.setFeature(
-                    "http://apache.org/xml/features/validation/schema",
-                    true);
+        ClassLoader original;
+        if (Constants.IS_SECURITY_ENABLED) {
+            PrivilegedGetTccl pa = new PrivilegedGetTccl();
+            original = AccessController.doPrivileged(pa);
+        } else {
+            original = Thread.currentThread().getContextClassLoader();
         }
+        try {
+            if (Constants.IS_SECURITY_ENABLED) {
+                PrivilegedSetTccl pa =
+                        new PrivilegedSetTccl(JspDocumentParser.class.getClassLoader());
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(
+                        JspDocumentParser.class.getClassLoader());
+            }
+
+            SAXParserFactory factory = SAXParserFactory.newInstance();
 
-        // Configure the parser
-        SAXParser saxParser = factory.newSAXParser();
-        XMLReader xmlReader = saxParser.getXMLReader();
-        xmlReader.setProperty(LEXICAL_HANDLER_PROPERTY, jspDocParser);
-        xmlReader.setErrorHandler(jspDocParser);
+            factory.setNamespaceAware(true);
+            // Preserve xmlns attributes
+            factory.setFeature(
+                "http://xml.org/sax/features/namespace-prefixes",
+                true);
 
-        return saxParser;
+            factory.setValidating(validating);
+            if (validating) {
+                // Enable DTD validation
+                factory.setFeature(
+                        "http://xml.org/sax/features/validation",
+                        true);
+                // Enable schema validation
+                factory.setFeature(
+                        "http://apache.org/xml/features/validation/schema",
+                        true);
+            }
+
+            // Configure the parser
+            SAXParser saxParser = factory.newSAXParser();
+            XMLReader xmlReader = saxParser.getXMLReader();
+            xmlReader.setProperty(LEXICAL_HANDLER_PROPERTY, jspDocParser);
+            xmlReader.setErrorHandler(jspDocParser);
+
+            return saxParser;
+        } finally {
+            if (Constants.IS_SECURITY_ENABLED) {
+                PrivilegedSetTccl pa = new PrivilegedSetTccl(original);
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(original);
+            }
+        }
     }
 
     /*
--- java/org/apache/tomcat/util/security/PrivilegedGetTccl.java.orig	2014-07-24 18:59:42.923103000 -0400
+++ java/org/apache/tomcat/util/security/PrivilegedGetTccl.java	2014-07-24 18:56:17.729411000 -0400
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.security;
+
+import java.security.PrivilegedAction;
+
+public class PrivilegedGetTccl implements PrivilegedAction<ClassLoader> {
+    @Override
+    public ClassLoader run() {
+        return Thread.currentThread().getContextClassLoader();
+    }
+}
+
+
--- java/org/apache/tomcat/util/security/PrivilegedSetTccl.java.orig	2014-07-24 18:59:42.928107000 -0400
+++ java/org/apache/tomcat/util/security/PrivilegedSetTccl.java	2014-07-24 18:56:17.736421000 -0400
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.security;
+
+import java.security.PrivilegedAction;
+
+public class PrivilegedSetTccl implements PrivilegedAction<Void> {
+
+    private ClassLoader cl;
+
+    public PrivilegedSetTccl(ClassLoader cl) {
+        this.cl = cl;
+    }
+
+    @Override
+    public Void run() {
+        Thread.currentThread().setContextClassLoader(cl);
+        return null;
+    }
+}
\ No newline at end of file