JBossWeb SVN: r1967 - in trunk: test/java/org/apache/el/lang and 1 other directories.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2012-02-16 10:39:39 -0500 (Thu, 16 Feb 2012)
New Revision: 1967
Modified:
trunk/java/org/apache/el/lang/ELSupport.java
trunk/test/java/org/apache/el/lang/TestELSupport.java
trunk/webapps/docs/changelog.xml
Log:
Port 52666: Correct coercion order in EL for A {==,!=,eq,ne} B.
Modified: trunk/java/org/apache/el/lang/ELSupport.java
===================================================================
--- trunk/java/org/apache/el/lang/ELSupport.java …
[View More]2012-02-15 15:57:47 UTC (rev 1966)
+++ trunk/java/org/apache/el/lang/ELSupport.java 2012-02-16 15:39:39 UTC (rev 1967)
@@ -122,40 +122,36 @@
* @throws ELException
*/
public static final boolean equals(final Object obj0, final Object obj1)
- throws ELException {
+ throws ELException {
if (obj0 == obj1) {
return true;
} else if (obj0 == null || obj1 == null) {
return false;
- } else if (obj0 instanceof Boolean || obj1 instanceof Boolean) {
- return coerceToBoolean(obj0).equals(coerceToBoolean(obj1));
- } else if (obj0.getClass().isEnum()) {
- return obj0.equals(coerceToEnum(obj1, obj0.getClass()));
- } else if (obj1.getClass().isEnum()) {
- return obj1.equals(coerceToEnum(obj0, obj1.getClass()));
- } else if (obj0 instanceof String || obj1 instanceof String) {
- int lexCompare = coerceToString(obj0).compareTo(coerceToString(obj1));
- return (lexCompare == 0) ? true : false;
- }
- if (isBigDecimalOp(obj0, obj1)) {
+ } else if (isBigDecimalOp(obj0, obj1)) {
BigDecimal bd0 = (BigDecimal) coerceToNumber(obj0, BigDecimal.class);
BigDecimal bd1 = (BigDecimal) coerceToNumber(obj1, BigDecimal.class);
return bd0.equals(bd1);
- }
- if (isDoubleOp(obj0, obj1)) {
+ } else if (isDoubleOp(obj0, obj1)) {
Double d0 = (Double) coerceToNumber(obj0, Double.class);
Double d1 = (Double) coerceToNumber(obj1, Double.class);
return d0.equals(d1);
- }
- if (isBigIntegerOp(obj0, obj1)) {
+ } else if (isBigIntegerOp(obj0, obj1)) {
BigInteger bi0 = (BigInteger) coerceToNumber(obj0, BigInteger.class);
BigInteger bi1 = (BigInteger) coerceToNumber(obj1, BigInteger.class);
return bi0.equals(bi1);
- }
- if (isLongOp(obj0, obj1)) {
+ } else if (isLongOp(obj0, obj1)) {
Long l0 = (Long) coerceToNumber(obj0, Long.class);
Long l1 = (Long) coerceToNumber(obj1, Long.class);
return l0.equals(l1);
+ } else if (obj0 instanceof Boolean || obj1 instanceof Boolean) {
+ return coerceToBoolean(obj0).equals(coerceToBoolean(obj1));
+ } else if (obj0.getClass().isEnum()) {
+ return obj0.equals(coerceToEnum(obj1, obj0.getClass()));
+ } else if (obj1.getClass().isEnum()) {
+ return obj1.equals(coerceToEnum(obj0, obj1.getClass()));
+ } else if (obj0 instanceof String || obj1 instanceof String) {
+ int lexCompare = coerceToString(obj0).compareTo(coerceToString(obj1));
+ return (lexCompare == 0) ? true : false;
} else {
return obj0.equals(obj1);
}
Modified: trunk/test/java/org/apache/el/lang/TestELSupport.java
===================================================================
--- trunk/test/java/org/apache/el/lang/TestELSupport.java 2012-02-15 15:57:47 UTC (rev 1966)
+++ trunk/test/java/org/apache/el/lang/TestELSupport.java 2012-02-16 15:39:39 UTC (rev 1967)
@@ -28,6 +28,11 @@
public class TestELSupport {
@Test
+ public void testEquals() {
+ assertTrue(ELSupport.equals("01", Long.valueOf(1)));
+ }
+
+ @Test
public void testBigDecimal() {
testIsSame(new BigDecimal(
"0.123456789012345678901234567890123456789012345678901234567890123456789"));
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2012-02-15 15:57:47 UTC (rev 1966)
+++ trunk/webapps/docs/changelog.xml 2012-02-16 15:39:39 UTC (rev 1967)
@@ -42,6 +42,13 @@
</fix>
</changelog>
</subsection>
+ <subsection name="Jasper">
+ <changelog>
+ <fix>
+ <bug>52666</bug>: Correct coercion order in EL for A {==,!=,eq,ne} B. (markt)
+ </fix>
+ </changelog>
+ </subsection>
</section>
<section name="JBoss Web 7.0.10.Final (remm)">
[View Less]
12 years, 10 months
JBossWeb SVN: r1965 - trunk/java/org/apache/catalina/core.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2012-02-15 04:14:38 -0500 (Wed, 15 Feb 2012)
New Revision: 1965
Modified:
trunk/java/org/apache/catalina/core/ApplicationDispatcher.java
Log:
Fix requestDestroyed event.
Modified: trunk/java/org/apache/catalina/core/ApplicationDispatcher.java
===================================================================
--- trunk/java/org/apache/catalina/core/ApplicationDispatcher.java 2012-02-14 12:40:49 UTC (rev 1964)
+++ trunk/java/org/apache/catalina/core/…
[View More]ApplicationDispatcher.java 2012-02-15 09:14:38 UTC (rev 1965)
@@ -758,12 +758,12 @@
ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
ClassLoader contextClassLoader = context.getLoader().getClassLoader();
+ ServletRequestEvent event = null;
+ Object instances[] = context.getApplicationEventListeners();
if (oldCCL != contextClassLoader) {
// Enter application scope
Thread.currentThread().setContextClassLoader(contextClassLoader);
context.getThreadBindingListener().bind();
- Object instances[] = context.getApplicationEventListeners();
- ServletRequestEvent event = null;
if (instances != null && (instances.length > 0)) {
event = new ServletRequestEvent(context.getServletContext(), request);
// create pre-service event
@@ -895,8 +895,6 @@
// Reset the old context class loader
if (oldCCL != null) {
// Exit application scope
- Object instances[] = context.getApplicationEventListeners();
- ServletRequestEvent event = null;
if (instances != null && (instances.length > 0)) {
// create post-service event
for (int i = instances.length - 1; i >= 0; i--) {
[View Less]
12 years, 11 months
JBossWeb SVN: r1964 - trunk/java/org/apache/catalina/core.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2012-02-14 07:40:49 -0500 (Tue, 14 Feb 2012)
New Revision: 1964
Modified:
trunk/java/org/apache/catalina/core/ApplicationDispatcher.java
Log:
Public for easier usage.
Modified: trunk/java/org/apache/catalina/core/ApplicationDispatcher.java
===================================================================
--- trunk/java/org/apache/catalina/core/ApplicationDispatcher.java 2012-02-14 12:22:21 UTC (rev 1963)
+++ trunk/java/org/apache/catalina/core/…
[View More]ApplicationDispatcher.java 2012-02-14 12:40:49 UTC (rev 1964)
@@ -65,7 +65,7 @@
* @version $Revision$ $Date$
*/
-final class ApplicationDispatcher
+public final class ApplicationDispatcher
implements RequestDispatcher {
[View Less]
12 years, 11 months
JBossWeb SVN: r1963 - in trunk: webapps/docs and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2012-02-14 07:22:21 -0500 (Tue, 14 Feb 2012)
New Revision: 1963
Modified:
trunk/java/org/apache/catalina/core/ApplicationDispatcher.java
trunk/webapps/docs/changelog.xml
Log:
Experiment with direct invocation in the request dispatcher.
Modified: trunk/java/org/apache/catalina/core/ApplicationDispatcher.java
===================================================================
--- trunk/java/org/apache/catalina/core/ApplicationDispatcher.java 2012-…
[View More]02-13 23:08:49 UTC (rev 1962)
+++ trunk/java/org/apache/catalina/core/ApplicationDispatcher.java 2012-02-14 12:22:21 UTC (rev 1963)
@@ -30,6 +30,8 @@
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
@@ -99,7 +101,22 @@
}
}
-
+ protected class PrivilegedInvoke implements PrivilegedExceptionAction {
+ private ServletRequest request;
+ private ServletResponse response;
+
+ PrivilegedInvoke(ServletRequest request, ServletResponse response)
+ {
+ this.request = request;
+ this.response = response;
+ }
+
+ public Object run() throws ServletException, IOException {
+ doInvoke(request,response);
+ return null;
+ }
+ }
+
protected class PrivilegedAsync implements PrivilegedExceptionAction {
private ServletRequest request;
private ServletResponse response;
@@ -300,6 +317,70 @@
/**
+ * Batch forward this request and response to another resource for processing.
+ * Any runtime exception, IOException, or ServletException thrown by the
+ * called servlet will be propogated to the caller. This acts like a straight
+ * invacation, using the REQUEST mapping.
+ *
+ * @param request The servlet request to be forwarded
+ * @param response The servlet response to be forwarded
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception ServletException if a servlet exception occurs
+ */
+ public void invoke(ServletRequest request, ServletResponse response)
+ throws ServletException, IOException
+ {
+ if (Globals.IS_SECURITY_ENABLED) {
+ try {
+ AccessController.doPrivileged(new PrivilegedInvoke(request, response));
+ } catch (PrivilegedActionException pe) {
+ Exception e = pe.getException();
+ if (e instanceof ServletException)
+ throw (ServletException) e;
+ throw (IOException) e;
+ }
+ } else {
+ doInvoke(request,response);
+ }
+ }
+
+ private void doInvoke(ServletRequest request, ServletResponse response)
+ throws ServletException, IOException
+ {
+
+ // Set up to handle the specified request and response
+ State state = new State(request, response, false);
+
+ if (Globals.STRICT_SERVLET_COMPLIANCE) {
+ // Check SRV.8.2 / SRV.14.2.5.1 compliance
+ checkSameObjects(request, response);
+ }
+
+ wrapResponse(state);
+ ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state);
+ String contextPath = context.getPath();
+ wrequest.setContextPath(contextPath);
+ wrequest.setRequestURI(requestURI);
+ wrequest.setServletPath(servletPath);
+ wrequest.setPathInfo(pathInfo);
+ if (queryString != null) {
+ wrequest.setQueryString(queryString);
+ wrequest.setQueryParams(queryString);
+ }
+
+ state.outerRequest.setAttribute
+ (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
+ requestPath);
+ state.outerRequest.setAttribute
+ (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+ ApplicationFilterFactory.REQUEST_INTEGER);
+ invoke(state.outerRequest, response, state);
+
+ }
+
+
+ /**
* Async forward this request and response to another resource for processing.
* Any runtime exception, IOException, or ServletException thrown by the
* called servlet will be propogated to the caller.
@@ -663,6 +744,14 @@
private void invoke(ServletRequest request, ServletResponse response,
State state) throws IOException, ServletException {
+ // Initialize local variables we may need
+ HttpServletResponse hresponse = state.hresponse;
+ Servlet servlet = null;
+ IOException ioException = null;
+ ServletException servletException = null;
+ RuntimeException runtimeException = null;
+ boolean unavailable = false;
+
// Checking to see if the context classloader is the current context
// classloader. If it's not, we're saving it, and setting the context
// classloader to the Context classloader
@@ -670,19 +759,36 @@
ClassLoader contextClassLoader = context.getLoader().getClassLoader();
if (oldCCL != contextClassLoader) {
+ // Enter application scope
Thread.currentThread().setContextClassLoader(contextClassLoader);
+ context.getThreadBindingListener().bind();
+ Object instances[] = context.getApplicationEventListeners();
+ ServletRequestEvent event = null;
+ if (instances != null && (instances.length > 0)) {
+ event = new ServletRequestEvent(context.getServletContext(), request);
+ // create pre-service event
+ for (int i = 0; i < instances.length; i++) {
+ if (instances[i] == null)
+ continue;
+ if (!(instances[i] instanceof ServletRequestListener))
+ continue;
+ ServletRequestListener listener = (ServletRequestListener) instances[i];
+ try {
+ listener.requestInitialized(event);
+ } catch (Throwable t) {
+ context.getLogger().error(sm.getString("requestListenerValve.requestInit",
+ instances[i].getClass().getName()), t);
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
+ servletException = new ServletException
+ (sm.getString("requestListenerValve.requestInit",
+ wrapper.getName()), t);
+ }
+ }
+ }
} else {
oldCCL = null;
}
- // Initialize local variables we may need
- HttpServletResponse hresponse = state.hresponse;
- Servlet servlet = null;
- IOException ioException = null;
- ServletException servletException = null;
- RuntimeException runtimeException = null;
- boolean unavailable = false;
-
// Check for the servlet being marked unavailable
if (wrapper.isUnavailable()) {
wrapper.getLogger().warn(
@@ -765,14 +871,8 @@
}
// Release the filter chain (if any) for this request
- try {
- if (filterChain != null)
- filterChain.release();
- } catch (Throwable e) {
- wrapper.getLogger().error(sm.getString("standardWrapper.releaseFilters",
- wrapper.getName()), e);
- // FIXME: Exception handling needs to be simpiler to what is in the StandardWrapperValue
- }
+ if (filterChain != null)
+ filterChain.release();
// Deallocate the allocated servlet instance
try {
@@ -791,10 +891,36 @@
wrapper.getName()), e);
}
+
// Reset the old context class loader
- if (oldCCL != null)
+ if (oldCCL != null) {
+ // Exit application scope
+ Object instances[] = context.getApplicationEventListeners();
+ ServletRequestEvent event = null;
+ if (instances != null && (instances.length > 0)) {
+ // create post-service event
+ for (int i = instances.length - 1; i >= 0; i--) {
+ if (instances[i] == null)
+ continue;
+ if (!(instances[i] instanceof ServletRequestListener))
+ continue;
+ ServletRequestListener listener = (ServletRequestListener) instances[i];
+ try {
+ listener.requestDestroyed(event);
+ } catch (Throwable t) {
+ context.getLogger().error(sm.getString("requestListenerValve.requestDestroy",
+ instances[i].getClass().getName()), t);
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
+ servletException = new ServletException
+ (sm.getString("requestListenerValve.requestDestroy",
+ wrapper.getName()), t);
+ }
+ }
+ }
+ context.getThreadBindingListener().unbind();
Thread.currentThread().setContextClassLoader(oldCCL);
-
+ }
+
// Unwrap request/response if needed
// See Bugzilla 30949
unwrapRequest(state);
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2012-02-13 23:08:49 UTC (rev 1962)
+++ trunk/webapps/docs/changelog.xml 2012-02-14 12:22:21 UTC (rev 1963)
@@ -22,6 +22,13 @@
<fix>
Sync memory leak hacks listener. (markt)
</fix>
+ <add>
+ Update the request dispatcher with a direct invoke, for servlet batch processing. (remm)
+ </add>
+ <fix>
+ If coming from outside of the application scope, make the request dispatcher
+ set all necessary bindings and send events. (remm)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
[View Less]
12 years, 11 months
JBossWeb SVN: r1962 - in trunk: webapps/docs and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2012-02-13 18:08:49 -0500 (Mon, 13 Feb 2012)
New Revision: 1962
Modified:
trunk/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java
trunk/webapps/docs/changelog.xml
Log:
Sync memory leaks listener, just in case.
Modified: trunk/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java
===================================================================
--- trunk/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.…
[View More]java 2012-02-13 18:25:41 UTC (rev 1961)
+++ trunk/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java 2012-02-13 23:08:49 UTC (rev 1962)
@@ -18,9 +18,13 @@
package org.apache.catalina.core;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
+import java.sql.DriverManager;
+import java.util.StringTokenizer;
import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -43,7 +47,7 @@
* <p>
* Locked files usually occur when a resource inside a JAR is accessed without
* first disabling Jar URL connection caching. The workaround is to disable this
- * caching by default.
+ * caching by default.
*/
public class JreMemoryLeakPreventionListener implements LifecycleListener {
@@ -57,19 +61,101 @@
* <code>sun.awt.AppContext.getAppContext()</code> is triggered by a web
* application. Defaults to <code>true</code>.
*/
- protected boolean appContextProtection = true;
+ private boolean appContextProtection = true;
public boolean isAppContextProtection() { return appContextProtection; }
public void setAppContextProtection(boolean appContextProtection) {
this.appContextProtection = appContextProtection;
}
/**
+ * Protect against the memory leak caused when the first call to
+ * <code>java.awt.Toolkit.getDefaultToolkit()</code> is triggered
+ * by a web application. Defaults to <code>false</code> because a new
+ * Thread is launched.
+ */
+ private boolean awtThreadProtection = false;
+ public boolean isAWTThreadProtection() { return awtThreadProtection; }
+ public void setAWTThreadProtection(boolean awtThreadProtection) {
+ this.awtThreadProtection = awtThreadProtection;
+ }
+
+ /**
+ * Protect against the memory leak caused when the
+ * <code>sun.java2d.Disposer</code> class is loaded by a web application.
+ * Defaults to <code>false</code> because a new Thread is launched.
+ */
+ private boolean java2dDisposerProtection = false;
+ public boolean isJava2DDisposerProtection() {
+ return java2dDisposerProtection;
+ }
+ public void setJava2DDisposerProtection(boolean java2dDisposerProtection) {
+ this.java2dDisposerProtection = java2dDisposerProtection;
+ }
+
+ /**
+ * Protect against the memory leak caused when the first call to
+ * <code>sun.misc.GC.requestLatency(long)</code> is triggered by a web
+ * application. This first call will start a GC Daemon thread with the
+ * thread's context class loader configured to be the web application class
+ * loader. Defaults to <code>true</code>.
+ */
+ private boolean gcDaemonProtection = true;
+ public boolean isGcDaemonProtection() { return gcDaemonProtection; }
+ public void setGcDaemonProtection(boolean gcDaemonProtection) {
+ this.gcDaemonProtection = gcDaemonProtection;
+ }
+
+ /**
+ * Protect against the memory leak caused when the first call to
+ * <code>javax.security.auth.Policy</code> is triggered by a web
+ * application. This first call populate a static variable with a reference
+ * to the context class loader. Defaults to <code>true</code>.
+ */
+ private boolean securityPolicyProtection = true;
+ public boolean isSecurityPolicyProtection() {
+ return securityPolicyProtection;
+ }
+ public void setSecurityPolicyProtection(boolean securityPolicyProtection) {
+ this.securityPolicyProtection = securityPolicyProtection;
+ }
+
+ /**
+ * Protects against the memory leak caused when the first call to
+ * <code>javax.security.auth.login.Configuration</code> is triggered by a
+ * web application. This first call populate a static variable with a
+ * reference to the context class loader. Defaults to <code>true</code>.
+ */
+ private boolean securityLoginConfigurationProtection = true;
+ public boolean isSecurityLoginConfigurationProtection() {
+ return securityLoginConfigurationProtection;
+ }
+ public void setSecurityLoginConfigurationProtection(
+ boolean securityLoginConfigurationProtection) {
+ this.securityLoginConfigurationProtection = securityLoginConfigurationProtection;
+ }
+
+ /**
+ * Protect against the memory leak, when the initialization of the
+ * Java Cryptography Architecture is triggered by initializing
+ * a MessageDigest during web application deployment.
+ * This will occasionally start a Token Poller thread with the thread's
+ * context class loader equal to the web application class loader.
+ * Instead we initialize JCA early.
+ * Defaults to <code>true</code>.
+ */
+ private boolean tokenPollerProtection = true;
+ public boolean isTokenPollerProtection() { return tokenPollerProtection; }
+ public void setTokenPollerProtection(boolean tokenPollerProtection) {
+ this.tokenPollerProtection = tokenPollerProtection;
+ }
+
+ /**
* Protect against resources being read for JAR files and, as a side-effect,
* the JAR file becoming locked. Note this disables caching for all
* {@link URLConnection}s, regardless of type. Defaults to
* <code>true</code>.
*/
- protected boolean urlCacheProtection = true;
+ private boolean urlCacheProtection = true;
public boolean isUrlCacheProtection() { return urlCacheProtection; }
public void setUrlCacheProtection(boolean urlCacheProtection) {
this.urlCacheProtection = urlCacheProtection;
@@ -77,82 +163,295 @@
/**
* XML parsing can pin a web application class loader in memory. This is
- * particularly nasty as profilers (at least YourKit and Eclispe MAT) don't
- * idenitfy any GC roots related to this.
+ * particularly nasty as profilers (at least YourKit and Eclipse MAT) don't
+ * identify any GC roots related to this.
+ * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6916498">
+ * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6916498</a>
*/
- protected boolean xmlParsingProtection = true;
+ private boolean xmlParsingProtection = true;
public boolean isXmlParsingProtection() { return xmlParsingProtection; }
public void setXmlParsingProtection(boolean xmlParsingProtection) {
this.xmlParsingProtection = xmlParsingProtection;
}
-
+
+ /**
+ * <code>com.sun.jndi.ldap.LdapPoolManager</code> class spawns a thread when
+ * it is initialized if the system property
+ * <code>com.sun.jndi.ldap.connect.pool.timeout</code> is greater than 0.
+ * That thread inherits the context class loader of the current thread, so
+ * that there may be a web application class loader leak if the web app
+ * is the first to use <code>LdapPoolManager</code>.
+ */
+ private boolean ldapPoolProtection = true;
+ public boolean isLdapPoolProtection() { return ldapPoolProtection; }
+ public void setLdapPoolProtection(boolean ldapPoolProtection) {
+ this.ldapPoolProtection = ldapPoolProtection;
+ }
+
+ /**
+ * The first access to {@link DriverManager} will trigger the loading of
+ * all {@link java.sql.Driver}s in the the current class loader. The web
+ * application level memory leak protection can take care of this in most
+ * cases but triggering the loading here has fewer side-effects.
+ */
+ private boolean driverManagerProtection = true;
+ public boolean isDriverManagerProtection() {
+ return driverManagerProtection;
+ }
+ public void setDriverManagerProtection(boolean driverManagerProtection) {
+ this.driverManagerProtection = driverManagerProtection;
+ }
+
+ /**
+ * List of comma-separated fully qualified class names to load and initialize during
+ * the startup of this Listener. This allows to pre-load classes that are known to
+ * provoke classloader leaks if they are loaded during a request processing.
+ */
+ private String classesToInitialize = null;
+ public String getClassesToInitialize() {
+ return classesToInitialize;
+ }
+ public void setClassesToInitialize(String classesToInitialize) {
+ this.classesToInitialize = classesToInitialize;
+ }
+
+
@Override
public void lifecycleEvent(LifecycleEvent event) {
// Initialise these classes when Tomcat starts
if (Lifecycle.INIT_EVENT.equals(event.getType())) {
- /*
- * Several components end up calling:
- * sun.awt.AppContext.getAppContext()
- *
- * Those libraries / components known to trigger memory leaks due to
- * eventual calls to getAppContext() are:
- * - Google Web Toolkit via its use of javax.imageio
- * - Tomcat via its use of java.beans.Introspector.flushCaches() in
- * 1.6.0_15 onwards
- * - others TBD
- */
-
- // Trigger a call to sun.awt.AppContext.getAppContext(). This will
- // pin the common class loader in memory but that shouldn't be an
- // issue.
- if (appContextProtection) {
- ImageIO.getCacheDirectory();
- }
-
- /*
- * Several components end up opening JarURLConnections without first
- * disabling caching. This effectively locks the file. Whilst more
- * noticeable and harder to ignore on Windows, it affects all
- * operating systems.
- *
- * Those libraries/components known to trigger this issue include:
- * - log4j versions 1.2.15 and earlier
- * - javax.xml.bind.JAXBContext.newInstance()
- */
-
- // Set the default URL caching policy to not to cache
- if (urlCacheProtection) {
- try {
- // Doesn't matter that this JAR doesn't exist - just as long as
- // the URL is well-formed
- URL url = new URL("jar:file://dummy.jar!/");
- URLConnection uConn = url.openConnection();
- uConn.setDefaultUseCaches(false);
- } catch (MalformedURLException e) {
- log.error(sm.getString(
- "jreLeakListener.jarUrlConnCacheFail"), e);
- } catch (IOException e) {
- log.error(sm.getString(
- "jreLeakListener.jarUrlConnCacheFail"), e);
+
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+
+ try
+ {
+ // Use the system classloader as the victim for all this
+ // ClassLoader pinning we're about to do.
+ Thread.currentThread().setContextClassLoader(
+ ClassLoader.getSystemClassLoader());
+
+ /*
+ * First call to this loads all drivers in the current class
+ * loader
+ */
+ if (driverManagerProtection) {
+ DriverManager.getDrivers();
}
- }
-
- /*
- * Haven't got to the root of what is going on with this leak but if
- * a web app is the first to make the calls below the web
- * application class loader will be pinned in memory.
- */
- if (xmlParsingProtection) {
- DocumentBuilderFactory factory =
- DocumentBuilderFactory.newInstance();
- try {
- factory.newDocumentBuilder();
- } catch (ParserConfigurationException e) {
- log.error(sm.getString(
- "jreLeakListener.xmlParseFail"), e);
+
+ /*
+ * Several components end up calling:
+ * sun.awt.AppContext.getAppContext()
+ *
+ * Those libraries / components known to trigger memory leaks
+ * due to eventual calls to getAppContext() are:
+ * - Google Web Toolkit via its use of javax.imageio
+ * - Tomcat via its use of java.beans.Introspector.flushCaches()
+ * in 1.6.0_15 onwards
+ * - others TBD
+ */
+
+ // Trigger a call to sun.awt.AppContext.getAppContext(). This
+ // will pin the system class loader in memory but that shouldn't
+ // be an issue.
+ if (appContextProtection) {
+ ImageIO.getCacheDirectory();
}
+
+ // Trigger the creation of the AWT (AWT-Windows, AWT-XAWT,
+ // etc.) thread
+ if (awtThreadProtection) {
+ java.awt.Toolkit.getDefaultToolkit();
+ }
+
+ // Trigger the creation of the "Java2D Disposer" thread.
+ // See https://issues.apache.org/bugzilla/show_bug.cgi?id=51687
+ if(java2dDisposerProtection) {
+ try {
+ Class.forName("sun.java2d.Disposer");
+ }
+ catch (ClassNotFoundException cnfe) {
+ // Ignore this case: we must be running on a
+ // non-Sun-based JRE.
+ }
+ }
+
+ /*
+ * Several components end up calling:
+ * sun.misc.GC.requestLatency(long)
+ *
+ * Those libraries / components known to trigger memory leaks
+ * due to eventual calls to requestLatency(long) are:
+ * - javax.management.remote.rmi.RMIConnectorServer.start()
+ */
+ if (gcDaemonProtection) {
+ try {
+ Class<?> clazz = Class.forName("sun.misc.GC");
+ Method method = clazz.getDeclaredMethod(
+ "requestLatency",
+ new Class[] {long.class});
+ method.invoke(null, Long.valueOf(3600000));
+ } catch (ClassNotFoundException e) {
+ if (System.getProperty("java.vendor").startsWith(
+ "Sun")) {
+ log.error(sm.getString(
+ "jreLeakListener.gcDaemonFail"), e);
+ } else {
+ log.debug(sm.getString(
+ "jreLeakListener.gcDaemonFail"), e);
+ }
+ } catch (SecurityException e) {
+ log.error(sm.getString("jreLeakListener.gcDaemonFail"),
+ e);
+ } catch (NoSuchMethodException e) {
+ log.error(sm.getString("jreLeakListener.gcDaemonFail"),
+ e);
+ } catch (IllegalArgumentException e) {
+ log.error(sm.getString("jreLeakListener.gcDaemonFail"),
+ e);
+ } catch (IllegalAccessException e) {
+ log.error(sm.getString("jreLeakListener.gcDaemonFail"),
+ e);
+ } catch (InvocationTargetException e) {
+ log.error(sm.getString("jreLeakListener.gcDaemonFail"),
+ e);
+ }
+ }
+
+ /*
+ * Calling getPolicy retains a static reference to the context
+ * class loader.
+ */
+ if (securityPolicyProtection) {
+ try {
+ // Policy.getPolicy();
+ Class<?> policyClass = Class
+ .forName("javax.security.auth.Policy");
+ Method method = policyClass.getMethod("getPolicy");
+ method.invoke(null);
+ } catch(ClassNotFoundException e) {
+ // Ignore. The class is deprecated.
+ } catch(SecurityException e) {
+ // Ignore. Don't need call to getPolicy() to be
+ // successful, just need to trigger static initializer.
+ } catch (NoSuchMethodException e) {
+ log.warn(sm.getString("jreLeakListener.authPolicyFail"),
+ e);
+ } catch (IllegalArgumentException e) {
+ log.warn(sm.getString("jreLeakListener.authPolicyFail"),
+ e);
+ } catch (IllegalAccessException e) {
+ log.warn(sm.getString("jreLeakListener.authPolicyFail"),
+ e);
+ } catch (InvocationTargetException e) {
+ log.warn(sm.getString("jreLeakListener.authPolicyFail"),
+ e);
+ }
+ }
+
+
+ /*
+ * Initializing javax.security.auth.login.Configuration retains a static reference to the context
+ * class loader.
+ */
+ if (securityLoginConfigurationProtection) {
+ try {
+ Class.forName("javax.security.auth.login.Configuration", true, ClassLoader.getSystemClassLoader());
+ } catch(ClassNotFoundException e) {
+ // Ignore
+ }
+ }
+
+ /*
+ * Creating a MessageDigest during web application startup
+ * initializes the Java Cryptography Architecture. Under certain
+ * conditions this starts a Token poller thread with TCCL equal
+ * to the web application class loader.
+ *
+ * Instead we initialize JCA right now.
+ */
+ if (tokenPollerProtection) {
+ java.security.Security.getProviders();
+ }
+
+ /*
+ * Several components end up opening JarURLConnections without
+ * first disabling caching. This effectively locks the file.
+ * Whilst more noticeable and harder to ignore on Windows, it
+ * affects all operating systems.
+ *
+ * Those libraries/components known to trigger this issue
+ * include:
+ * - log4j versions 1.2.15 and earlier
+ * - javax.xml.bind.JAXBContext.newInstance()
+ */
+
+ // Set the default URL caching policy to not to cache
+ if (urlCacheProtection) {
+ try {
+ // Doesn't matter that this JAR doesn't exist - just as
+ // long as the URL is well-formed
+ URL url = new URL("jar:file://dummy.jar!/");
+ URLConnection uConn = url.openConnection();
+ uConn.setDefaultUseCaches(false);
+ } catch (MalformedURLException e) {
+ log.error(sm.getString(
+ "jreLeakListener.jarUrlConnCacheFail"), e);
+ } catch (IOException e) {
+ log.error(sm.getString(
+ "jreLeakListener.jarUrlConnCacheFail"), e);
+ }
+ }
+
+ /*
+ * Haven't got to the root of what is going on with this leak
+ * but if a web app is the first to make the calls below the web
+ * application class loader will be pinned in memory.
+ */
+ if (xmlParsingProtection) {
+ DocumentBuilderFactory factory =
+ DocumentBuilderFactory.newInstance();
+ try {
+ factory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ log.error(sm.getString("jreLeakListener.xmlParseFail"),
+ e);
+ }
+ }
+
+ if (ldapPoolProtection) {
+ try {
+ Class.forName("com.sun.jndi.ldap.LdapPoolManager");
+ } catch (ClassNotFoundException e) {
+ if (System.getProperty("java.vendor").startsWith(
+ "Sun")) {
+ log.error(sm.getString(
+ "jreLeakListener.ldapPoolManagerFail"), e);
+ } else {
+ log.debug(sm.getString(
+ "jreLeakListener.ldapPoolManagerFail"), e);
+ }
+ }
+ }
+
+ if (classesToInitialize != null) {
+ StringTokenizer strTok =
+ new StringTokenizer(classesToInitialize, ", \r\n\t");
+ while (strTok.hasMoreTokens()) {
+ String classNameToLoad = strTok.nextToken();
+ try {
+ Class.forName(classNameToLoad);
+ } catch (ClassNotFoundException e) {
+ log.error(
+ sm.getString("jreLeakListener.classToInitializeFail",
+ classNameToLoad), e);
+ // continue with next class to load
+ }
+ }
+ }
+
+ } finally {
+ Thread.currentThread().setContextClassLoader(loader);
}
}
}
-
}
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2012-02-13 18:25:41 UTC (rev 1961)
+++ trunk/webapps/docs/changelog.xml 2012-02-13 23:08:49 UTC (rev 1962)
@@ -17,6 +17,13 @@
<body>
<section name="JBoss Web 7.0.11.Final (remm)">
+ <subsection name="Catalina">
+ <changelog>
+ <fix>
+ Sync memory leak hacks listener. (markt)
+ </fix>
+ </changelog>
+ </subsection>
<subsection name="Coyote">
<changelog>
<fix>
[View Less]
12 years, 11 months
JBossWeb SVN: r1961 - branches.
by jbossweb-commits@lists.jboss.org
Author: bmaxwell
Date: 2012-02-13 13:25:41 -0500 (Mon, 13 Feb 2012)
New Revision: 1961
Added:
branches/JBOSSWEB_2_1_12_GA_JBPAPP-8108/
Log:
[JBPAPP-8108] create one off patch branch from 2.1.12 patch03
12 years, 11 months
JBossWeb SVN: r1960 - branches.
by jbossweb-commits@lists.jboss.org
Author: bmaxwell
Date: 2012-02-13 13:24:47 -0500 (Mon, 13 Feb 2012)
New Revision: 1960
Removed:
branches/JBOSSWEB_2_1_12_GA_JBPAPP-8108/
Log:
[JBPAPP-8108] removing one off patch branch to redo against patch03
12 years, 11 months
JBossWeb SVN: r1959 - in tags/JBOSSWEB_2_1_12_GA_patch03: java/org/apache/catalina/connector and 5 other directories.
by jbossweb-commits@lists.jboss.org
Author: bmaxwell
Date: 2012-02-13 13:19:46 -0500 (Mon, 13 Feb 2012)
New Revision: 1959
Modified:
tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/AuthenticatorBase.java
tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/DigestAuthenticator.java
tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/LocalStrings.properties
tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/mbeans-descriptors.xml
tags/…
[View More]JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/connector/OutputBuffer.java
tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/realm/RealmBase.java
tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/jasper/xmlparser/ParserUtils.java
tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/http/MimeHeaders.java
tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/http/Parameters.java
tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/net/AprEndpoint.java
tags/JBOSSWEB_2_1_12_GA_patch03/webapps/docs/changelog.xml
Log:
[JBPAPP-8110] applied patch03 => jbossweb-CVE-1184.patch jbossweb-CVE-2011-4610.patch jbossweb-CVE-2526.patch jbossweb-CVE-4858.patch jbossweb-JBPAPP-7267-2.patch
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/AuthenticatorBase.java
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2012-02-13 18:19:46 UTC (rev 1959)
@@ -89,8 +89,17 @@
*/
protected static final String DEFAULT_ALGORITHM = "MD5";
+ /**
+ * Authentication header
+ */
+ protected static final String AUTH_HEADER_NAME = "WWW-Authenticate";
/**
+ * Default authentication realm name.
+ */
+ protected static final String REALM_NAME = "Authentication required";
+
+ /**
* The number of random bytes to include when generating a
* session identifier.
*/
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/DigestAuthenticator.java
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/DigestAuthenticator.java 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/DigestAuthenticator.java 2012-02-13 18:19:46 UTC (rev 1959)
@@ -22,12 +22,18 @@
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Random;
import java.security.Principal;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletResponse;
-
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.LifecycleException;
import org.apache.catalina.Realm;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
@@ -67,6 +73,11 @@
"org.apache.catalina.authenticator.DigestAuthenticator/1.0";
+ /**
+ * Tomcat's DIGEST implementation only supports auth quality of protection.
+ */
+ protected static final String QOP = "auth";
+
// ----------------------------------------------------------- Constructors
@@ -92,17 +103,49 @@
/**
+ * List of client nonce values currently being tracked
+ */
+ protected Map<String,NonceInfo> cnonces;
+
+
+ /**
+ * Maximum number of client nonces to keep in the cache. If not specified,
+ * the default value of 1000 is used.
+ */
+ protected int cnonceCacheSize = 1000;
+
+
+ /**
* Private key.
*/
- protected String key = "Catalina";
+ protected String key = null;
- // ------------------------------------------------------------- Properties
+ /**
+ * How long server nonces are valid for in milliseconds. Defaults to 5
+ * minutes.
+ */
+ protected long nonceValidity = 5 * 60 * 1000;
/**
+ * Opaque string.
+ */
+ protected String opaque;
+
+
+ /**
+ * Should the URI be validated as required by RFC2617? Can be disabled in
+ * reverse proxies where the proxy has modified the URI.
+ */
+ protected boolean validateUri = true;
+
+ // ------------------------------------------------------------- Properties
+
+ /**
* Return descriptive information about this Valve implementation.
*/
+ @Override
public String getInfo() {
return (info);
@@ -110,9 +153,58 @@
}
+ public int getCnonceCacheSize() {
+ return cnonceCacheSize;
+ }
+
+
+ public void setCnonceCacheSize(int cnonceCacheSize) {
+ this.cnonceCacheSize = cnonceCacheSize;
+ }
+
+
+ public String getKey() {
+ return key;
+ }
+
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+
+ public long getNonceValidity() {
+ return nonceValidity;
+ }
+
+
+ public void setNonceValidity(long nonceValidity) {
+ this.nonceValidity = nonceValidity;
+ }
+
+
+ public String getOpaque() {
+ return opaque;
+ }
+
+
+ public void setOpaque(String opaque) {
+ this.opaque = opaque;
+ }
+
+
+ public boolean isValidateUri() {
+ return validateUri;
+ }
+
+
+ public void setValidateUri(boolean validateUri) {
+ this.validateUri = validateUri;
+ }
+
+
// --------------------------------------------------------- Public Methods
-
/**
* Authenticate the user making this request, based on the specified
* login configuration. Return <code>true</code> if any specified
@@ -126,6 +218,7 @@
*
* @exception IOException if an input/output error occurs
*/
+ @Override
public boolean authenticate(Request request,
Response response,
LoginConfig config)
@@ -172,8 +265,13 @@
// Validate any credentials already included with this request
String authorization = request.getHeader("authorization");
+ DigestInfo digestInfo = new DigestInfo(getOpaque(), getNonceValidity(),
+ getKey(), cnonces, isValidateUri());
if (authorization != null) {
- principal = findPrincipal(request, authorization, context.getRealm());
+ if (digestInfo.validate(request, authorization, config)) {
+ principal = digestInfo.authenticate(context.getRealm());
+ }
+
if (principal != null) {
String username = parseUsername(authorization);
register(request, response, principal,
@@ -185,11 +283,12 @@
// Send an "unauthorized" response and an appropriate challenge
- // Next, generate a nOnce token (that is a token which is supposed
+ // Next, generate a nonce token (that is a token which is supposed
// to be unique).
- String nOnce = generateNOnce(request);
+ String nonce = generateNonce(request);
- setAuthenticateHeader(request, response, config, nOnce);
+ setAuthenticateHeader(request, response, config, nonce,
+ digestInfo.isNonceStale());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
// hres.flushBuffer();
return (false);
@@ -201,92 +300,6 @@
/**
- * Parse the specified authorization credentials, and return the
- * associated Principal that these credentials authenticate (if any)
- * from the specified Realm. If there is no such Principal, return
- * <code>null</code>.
- *
- * @param request HTTP servlet request
- * @param authorization Authorization credentials from this request
- * @param realm Realm used to authenticate Principals
- */
- protected static Principal findPrincipal(Request request,
- String authorization,
- Realm realm) {
-
- //System.out.println("Authorization token : " + authorization);
- // Validate the authorization credentials format
- if (authorization == null)
- return (null);
- if (!authorization.startsWith("Digest "))
- return (null);
- authorization = authorization.substring(7).trim();
-
- // Bugzilla 37132: http://issues.apache.org/bugzilla/show_bug.cgi?id=37132
- String[] tokens = authorization.split(",(?=(?:[^\"]*\"[^\"]*\")+$)");
-
- String userName = null;
- String realmName = null;
- String nOnce = null;
- String nc = null;
- String cnonce = null;
- String qop = null;
- String uri = null;
- String response = null;
- String method = request.getMethod();
-
- for (int i = 0; i < tokens.length; i++) {
- String currentToken = tokens[i];
- if (currentToken.length() == 0)
- continue;
-
- int equalSign = currentToken.indexOf('=');
- if (equalSign < 0)
- return null;
- String currentTokenName =
- currentToken.substring(0, equalSign).trim();
- String currentTokenValue =
- currentToken.substring(equalSign + 1).trim();
- if ("username".equals(currentTokenName))
- userName = removeQuotes(currentTokenValue);
- if ("realm".equals(currentTokenName))
- realmName = removeQuotes(currentTokenValue, true);
- if ("nonce".equals(currentTokenName))
- nOnce = removeQuotes(currentTokenValue);
- if ("nc".equals(currentTokenName))
- nc = removeQuotes(currentTokenValue);
- if ("cnonce".equals(currentTokenName))
- cnonce = removeQuotes(currentTokenValue);
- if ("qop".equals(currentTokenName))
- qop = removeQuotes(currentTokenValue);
- if ("uri".equals(currentTokenName))
- uri = removeQuotes(currentTokenValue);
- if ("response".equals(currentTokenName))
- response = removeQuotes(currentTokenValue);
- }
-
- if ( (userName == null) || (realmName == null) || (nOnce == null)
- || (uri == null) || (response == null) )
- return null;
-
- // Second MD5 digest used to calculate the digest :
- // MD5(Method + ":" + uri)
- String a2 = method + ":" + uri;
- //System.out.println("A2:" + a2);
-
- byte[] buffer = null;
- synchronized (md5Helper) {
- buffer = md5Helper.digest(a2.getBytes());
- }
- String md5a2 = md5Encoder.encode(buffer);
-
- return (realm.authenticate(userName, response, nOnce, nc, cnonce, qop,
- realmName, md5a2));
-
- }
-
-
- /**
* Parse the username from the specified authorization string. If none
* can be identified, return <code>null</code>
*
@@ -294,7 +307,6 @@
*/
protected String parseUsername(String authorization) {
- //System.out.println("Authorization token : " + authorization);
// Validate the authorization credentials format
if (authorization == null)
return (null);
@@ -354,20 +366,20 @@
*
* @param request HTTP Servlet request
*/
- protected String generateNOnce(Request request) {
+ protected String generateNonce(Request request) {
long currentTime = System.currentTimeMillis();
- String nOnceValue = request.getRemoteAddr() + ":" +
- currentTime + ":" + key;
+
+ String ipTimeKey =
+ request.getRemoteAddr() + ":" + currentTime + ":" + getKey();
- byte[] buffer = null;
+ byte[] buffer;
synchronized (md5Helper) {
- buffer = md5Helper.digest(nOnceValue.getBytes());
+ buffer = md5Helper.digest(ipTimeKey.getBytes());
}
- nOnceValue = md5Encoder.encode(buffer);
- return nOnceValue;
+ return currentTime + ":" + md5Encoder.encode(buffer);
}
@@ -379,7 +391,7 @@
* WWW-Authenticate = "WWW-Authenticate" ":" "Digest"
* digest-challenge
*
- * digest-challenge = 1#( realm | [ domain ] | nOnce |
+ * digest-challenge = 1#( realm | [ domain ] | nonce |
* [ digest-opaque ] |[ stale ] | [ algorithm ] )
*
* realm = "realm" "=" realm-value
@@ -396,29 +408,303 @@
* @param response HTTP Servlet response
* @param config Login configuration describing how authentication
* should be performed
- * @param nOnce nonce token
+ * @param nonce nonce token
*/
protected void setAuthenticateHeader(Request request,
Response response,
LoginConfig config,
- String nOnce) {
+ String nonce,
+ boolean isNonceStale) {
// Get the realm name
String realmName = config.getRealmName();
if (realmName == null)
- realmName = "Realm";
+ realmName = REALM_NAME;
- byte[] buffer = null;
- synchronized (md5Helper) {
- buffer = md5Helper.digest(nOnce.getBytes());
- }
+ String authenticateHeader;
+ if (isNonceStale) {
+ authenticateHeader = "Digest realm=\"" + realmName + "\", " +
+ "qop=\"" + QOP + "\", nonce=\"" + nonce + "\", " + "opaque=\"" +
+ getOpaque() + "\", stale=true";
+ } else {
+ authenticateHeader = "Digest realm=\"" + realmName + "\", " +
+ "qop=\"" + QOP + "\", nonce=\"" + nonce + "\", " + "opaque=\"" +
+ getOpaque() + "\"";
+ }
- String authenticateHeader = "Digest realm=\"" + realmName + "\", "
- + "qop=\"auth\", nonce=\"" + nOnce + "\", " + "opaque=\""
- + md5Encoder.encode(buffer) + "\"";
response.setHeader("WWW-Authenticate", authenticateHeader);
}
+ // ------------------------------------------------------- Lifecycle Methods
+
+ @Override
+ public void start() throws LifecycleException {
+ super.start();
+
+ // Generate a random secret key
+ if (getKey() == null) {
+ setKey(generateSessionId());
+ }
+
+ // Generate the opaque string the same way
+ if (getOpaque() == null) {
+ setOpaque(generateSessionId());
+ }
+
+ cnonces = new LinkedHashMap<String, DigestAuthenticator.NonceInfo>() {
+
+ private static final long serialVersionUID = 1L;
+ private static final long LOG_SUPPRESS_TIME = 5 * 60 * 1000;
+
+ private long lastLog = 0;
+
+ @Override
+ protected boolean removeEldestEntry(
+ Map.Entry<String,NonceInfo> eldest) {
+ // This is called from a sync so keep it simple
+ long currentTime = System.currentTimeMillis();
+ if (size() > getCnonceCacheSize()) {
+ if (lastLog < currentTime &&
+ currentTime - eldest.getValue().getTimestamp() <
+ getNonceValidity()) {
+ // Replay attack is possible
+ log.warn(sm.getString(
+ "digestAuthenticator.cacheRemove"));
+ lastLog = currentTime + LOG_SUPPRESS_TIME;
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+
+ private static class DigestInfo {
+
+ private String opaque;
+ private long nonceValidity;
+ private String key;
+ private Map<String,NonceInfo> cnonces;
+ private boolean validateUri = true;
+
+ private String userName = null;
+ private String method = null;
+ private String uri = null;
+ private String response = null;
+ private String nonce = null;
+ private String nc = null;
+ private String cnonce = null;
+ private String realmName = null;
+ private String qop = null;
+
+ private boolean nonceStale = false;
+
+
+ public DigestInfo(String opaque, long nonceValidity, String key,
+ Map<String,NonceInfo> cnonces, boolean validateUri) {
+ this.opaque = opaque;
+ this.nonceValidity = nonceValidity;
+ this.key = key;
+ this.cnonces = cnonces;
+ this.validateUri = validateUri;
+ }
+
+ public boolean validate(Request request, String authorization,
+ LoginConfig config) {
+ // Validate the authorization credentials format
+ if (authorization == null) {
+ return false;
+ }
+ if (!authorization.startsWith("Digest ")) {
+ return false;
+ }
+ authorization = authorization.substring(7).trim();
+
+ // Bugzilla 37132: http://issues.apache.org/bugzilla/show_bug.cgi?id=37132
+ String[] tokens = authorization.split(",(?=(?:[^\"]*\"[^\"]*\")+$)");
+
+ method = request.getMethod();
+ String opaque = null;
+
+ for (int i = 0; i < tokens.length; i++) {
+ String currentToken = tokens[i];
+ if (currentToken.length() == 0)
+ continue;
+
+ int equalSign = currentToken.indexOf('=');
+ if (equalSign < 0) {
+ return false;
+ }
+ String currentTokenName =
+ currentToken.substring(0, equalSign).trim();
+ String currentTokenValue =
+ currentToken.substring(equalSign + 1).trim();
+ if ("username".equals(currentTokenName))
+ userName = removeQuotes(currentTokenValue);
+ if ("realm".equals(currentTokenName))
+ realmName = removeQuotes(currentTokenValue, true);
+ if ("nonce".equals(currentTokenName))
+ nonce = removeQuotes(currentTokenValue);
+ if ("nc".equals(currentTokenName))
+ nc = removeQuotes(currentTokenValue);
+ if ("cnonce".equals(currentTokenName))
+ cnonce = removeQuotes(currentTokenValue);
+ if ("qop".equals(currentTokenName))
+ qop = removeQuotes(currentTokenValue);
+ if ("uri".equals(currentTokenName))
+ uri = removeQuotes(currentTokenValue);
+ if ("response".equals(currentTokenName))
+ response = removeQuotes(currentTokenValue);
+ if ("opaque".equals(currentTokenName))
+ opaque = removeQuotes(currentTokenValue);
+ }
+
+ if ( (userName == null) || (realmName == null) || (nonce == null)
+ || (uri == null) || (response == null) ) {
+ return false;
+ }
+
+ // Validate the URI - should match the request line sent by client
+ if (validateUri) {
+ String uriQuery;
+ String query = request.getQueryString();
+ if (query == null) {
+ uriQuery = request.getRequestURI();
+ } else {
+ uriQuery = request.getRequestURI() + "?" + query;
+ }
+ if (!uri.equals(uriQuery)) {
+ return false;
+ }
+ }
+
+ // Validate the Realm name
+ String lcRealm = config.getRealmName();
+ if (lcRealm == null) {
+ lcRealm = REALM_NAME;
+ }
+ if (!lcRealm.equals(realmName)) {
+ return false;
+ }
+
+ // Validate the opaque string
+ if (!this.opaque.equals(opaque)) {
+ return false;
+ }
+
+ // Validate nonce
+ int i = nonce.indexOf(":");
+ if (i < 0 || (i + 1) == nonce.length()) {
+ return false;
+ }
+ long nonceTime;
+ try {
+ nonceTime = Long.parseLong(nonce.substring(0, i));
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ String md5clientIpTimeKey = nonce.substring(i + 1);
+ long currentTime = System.currentTimeMillis();
+ if ((currentTime - nonceTime) > nonceValidity) {
+ nonceStale = true;
+ return false;
+ }
+ String serverIpTimeKey =
+ request.getRemoteAddr() + ":" + nonceTime + ":" + key;
+ byte[] buffer = null;
+ synchronized (md5Helper) {
+ buffer = md5Helper.digest(serverIpTimeKey.getBytes());
+ }
+ String md5ServerIpTimeKey = md5Encoder.encode(buffer);
+ if (!md5ServerIpTimeKey.equals(md5clientIpTimeKey)) {
+ return false;
+ }
+
+ // Validate qop
+ if (qop != null && !QOP.equals(qop)) {
+ return false;
+ }
+
+ // Validate cnonce and nc
+ // Check if presence of nc and nonce is consistent with presence of qop
+ if (qop == null) {
+ if (cnonce != null || nc != null) {
+ return false;
+ }
+ } else {
+ if (cnonce == null || nc == null) {
+ return false;
+ }
+ if (nc.length() != 8) {
+ return false;
+ }
+ long count;
+ try {
+ count = Long.parseLong(nc, 16);
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ NonceInfo info;
+ synchronized (cnonces) {
+ info = cnonces.get(cnonce);
+ }
+ if (info == null) {
+ info = new NonceInfo();
+ } else {
+ if (count <= info.getCount()) {
+ return false;
+ }
+ }
+ info.setCount(count);
+ info.setTimestamp(currentTime);
+ synchronized (cnonces) {
+ cnonces.put(cnonce, info);
+ }
+ }
+ return true;
+ }
+
+ public boolean isNonceStale() {
+ return nonceStale;
+ }
+
+ public Principal authenticate(Realm realm) {
+ // Second MD5 digest used to calculate the digest :
+ // MD5(Method + ":" + uri)
+ String a2 = method + ":" + uri;
+
+ byte[] buffer;
+ synchronized (md5Helper) {
+ buffer = md5Helper.digest(a2.getBytes());
+ }
+ String md5a2 = md5Encoder.encode(buffer);
+
+ return realm.authenticate(userName, response, nonce, nc, cnonce,
+ qop, realmName, md5a2);
+ }
+
+ }
+
+ private static class NonceInfo {
+ private volatile long count;
+ private volatile long timestamp;
+
+ public void setCount(long l) {
+ count = l;
+ }
+
+ public long getCount() {
+ return count;
+ }
+
+ public void setTimestamp(long l) {
+ timestamp = l;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+ }
}
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/LocalStrings.properties
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/LocalStrings.properties 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/LocalStrings.properties 2012-02-13 18:19:46 UTC (rev 1959)
@@ -12,3 +12,5 @@
authenticator.sessionExpired=The time allowed for the login process has been exceeded. If you wish to continue you must either click back twice and re-click the link you requested or close and re-open your browser
authenticator.unauthorized=Cannot authenticate with the provided credentials
authenticator.userDataConstraint=This request violates a User Data constraint for this application
+
+DigestAuthenticator.cacheRemove=A valid entry has been removed from client nonce cache to make room for new entries. A replay attack is now possible. To prevent the possibility of replay attacks, reduce nonceValidity or increase cnonceCacheSize. Further warnings of this type will be suppressed for 5 minutes
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/mbeans-descriptors.xml
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/mbeans-descriptors.xml 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/authenticator/mbeans-descriptors.xml 2012-02-13 18:19:46 UTC (rev 1959)
@@ -44,10 +44,30 @@
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
-
+
+ <attribute name="cnonceCacheSize"
+ description="The size of the cnonce cache used to prevent replay attacks"
+ type="int"/>
+
<attribute name="entropy"
description="A String initialization parameter used to increase the entropy of the initialization of our random number generator"
type="java.lang.String"/>
+
+ <attribute name="key"
+ description="The secret key used by digest authentication"
+ type="java.lang.String"/>
+
+ <attribute name="nonceValidity"
+ description="The time, in milliseconds, for which a server issued nonce will be valid"
+ type="long"/>
+
+ <attribute name="opaque"
+ description="The opaque server string used by digest authentication"
+ type="java.lang.String"/>
+
+ <attribute name="validateUri"
+ description="Should the uri be validated as required by RFC2617?"
+ type="boolean"/>
</mbean>
<mbean name="FormAuthenticator"
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/connector/OutputBuffer.java
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/connector/OutputBuffer.java 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/connector/OutputBuffer.java 2012-02-13 18:19:46 UTC (rev 1959)
@@ -464,6 +464,10 @@
outputCharChunk.setChars(buf, off, len);
while (outputCharChunk.getLength() > 0) {
conv.convert(outputCharChunk, bb);
+ if (bb.getLength() == 0) {
+ // Break out of the loop if more chars are needed to produce any output
+ break;
+ }
if (outputCharChunk.getLength() > 0) {
bb.flushBuffer();
}
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/realm/RealmBase.java
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/realm/RealmBase.java 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/catalina/realm/RealmBase.java 2012-02-13 18:19:46 UTC (rev 1959)
@@ -367,8 +367,13 @@
String md5a1 = getDigest(username, realm);
if (md5a1 == null)
return null;
- String serverDigestValue = md5a1 + ":" + nOnce + ":" + nc + ":"
- + cnonce + ":" + qop + ":" + md5a2;
+ String serverDigestValue;
+ if (qop == null) {
+ serverDigestValue = md5a1 + ":" + nOnce + ":" + md5a2;
+ } else {
+ serverDigestValue = md5a1 + ":" + nOnce + ":" + nc + ":" +
+ cnonce + ":" + qop + ":" + md5a2;
+ }
byte[] valueBytes = null;
if(getDigestEncoding() == null) {
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/jasper/xmlparser/ParserUtils.java
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/jasper/xmlparser/ParserUtils.java 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/jasper/xmlparser/ParserUtils.java 2012-02-13 18:19:46 UTC (rev 1959)
@@ -87,10 +87,10 @@
// Perform an XML parse of this document, via JAXP
try {
DocumentBuilderFactory factory =
- DocumentBuilderFactory.newInstance();
+ createDocumentBuilderFactory();
factory.setNamespaceAware(true);
factory.setValidating(validating);
- DocumentBuilder builder = factory.newDocumentBuilder();
+ DocumentBuilder builder = newDocumentBuilder(factory);
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(errorHandler);
document = builder.parse(is);
@@ -184,6 +184,36 @@
// Return the completed TreeNode graph
return (treeNode);
}
+
+ protected static DocumentBuilderFactory createDocumentBuilderFactory()
+ {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ DocumentBuilderFactory factory;
+ try
+ {
+ Thread.currentThread().setContextClassLoader(ParserUtils.class.getClassLoader());
+ factory = DocumentBuilderFactory.newInstance();
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ return factory;
+ }
+
+ protected static DocumentBuilder newDocumentBuilder(DocumentBuilderFactory factory) throws ParserConfigurationException
+ {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ Thread.currentThread().setContextClassLoader(ParserUtils.class.getClassLoader());
+ return factory.newDocumentBuilder();
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
}
@@ -232,4 +262,4 @@
public void fatalError(SAXParseException ex) throws SAXException {
throw ex;
}
-}
\ No newline at end of file
+}
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/http/MimeHeaders.java
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/http/MimeHeaders.java 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/http/MimeHeaders.java 2012-02-13 18:19:46 UTC (rev 1959)
@@ -23,9 +23,6 @@
import org.apache.tomcat.util.buf.MessageBytes;
-/* XXX XXX XXX Need a major rewrite !!!!
- */
-
/**
* This class is used to contain standard internet message headers,
* used for SMTP (RFC822) and HTTP (RFC2068) messages as well as for
@@ -77,12 +74,6 @@
* to avoid inside tomcat. The goal is to use _only_ MessageByte-based Fields,
* and reduce to 0 the memory overhead of tomcat.
*
- * TODO:
- * XXX one-buffer parsing - for http ( other protocols don't need that )
- * XXX remove unused methods
- * XXX External enumerations, with 0 GC.
- * XXX use HeaderName ID
- *
*
* @author dac(a)eng.sun.com
* @author James Todd [gonzo(a)eng.sun.com]
@@ -212,9 +203,10 @@
}
/** Initial size - should be == average number of headers per request
- * XXX make it configurable ( fine-tuning of web-apps )
*/
public static final int DEFAULT_HEADER_SIZE = 8;
+ protected static final int MAX_COUNT =
+ Integer.valueOf(System.getProperty("org.apache.tomcat.util.http.MimeHeaders.MAX_COUNT", "128")).intValue();
/**
* The header fields.
@@ -333,6 +325,9 @@
MimeHeaderField mh;
int len = headers.length;
if (count >= len) {
+ if (count >= MAX_COUNT) {
+ throw new IllegalStateException("Header count exceeded allowed maximum: " + MAX_COUNT);
+ }
// expand header list array
MimeHeaderField tmp[] = new MimeHeaderField[count * 2];
System.arraycopy(headers, 0, tmp, 0, len);
@@ -441,9 +436,7 @@
* @param name the name of the header field to be removed
*/
public void removeHeader(String name) {
- // XXX
// warning: rather sticky code; heavily tuned
-
for (int i = 0; i < count; i++) {
if (headers[i].getName().equalsIgnoreCase(name)) {
removeHeader(i--);
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/http/Parameters.java
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/http/Parameters.java 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/http/Parameters.java 2012-02-13 18:19:46 UTC (rev 1959)
@@ -40,6 +40,8 @@
protected static final int LAST = -1;
public static final int INITIAL_SIZE = 8;
protected static final String[] ARRAY_TYPE = new String[0];
+ protected static final int MAX_COUNT =
+ Integer.valueOf(System.getProperty("org.apache.tomcat.util.http.Parameters.MAX_COUNT", "512")).intValue();
protected class Field {
MessageBytes name = MessageBytes.newInstance();
@@ -212,6 +214,9 @@
int len = fields.length;
int pos = count;
if (count >= len) {
+ if (count >= MAX_COUNT) {
+ throw new IllegalStateException("Parameter count exceeded allowed maximum: " + MAX_COUNT);
+ }
// expand header list array
Field tmp[] = new Field[pos * 2];
System.arraycopy(fields, 0, tmp, 0, len);
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/net/AprEndpoint.java
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/net/AprEndpoint.java 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/java/org/apache/tomcat/util/net/AprEndpoint.java 2012-02-13 18:19:46 UTC (rev 1959)
@@ -301,7 +301,7 @@
/**
* Use sendfile for sending static files.
*/
- protected boolean useSendfile = Library.APR_HAS_SENDFILE;
+ protected boolean useSendfile = false; /* CVE-2011-2526 */
public void setUseSendfile(boolean useSendfile) { this.useSendfile = useSendfile; }
public boolean getUseSendfile() { return useSendfile; }
Modified: tags/JBOSSWEB_2_1_12_GA_patch03/webapps/docs/changelog.xml
===================================================================
--- tags/JBOSSWEB_2_1_12_GA_patch03/webapps/docs/changelog.xml 2012-02-13 17:44:09 UTC (rev 1958)
+++ tags/JBOSSWEB_2_1_12_GA_patch03/webapps/docs/changelog.xml 2012-02-13 18:19:46 UTC (rev 1959)
@@ -33,6 +33,16 @@
<fix>
<jboss-jira>JBWEB-186</jboss-jira>: Using the native connector causes the JVM to crash when shutting down on Windows. (remm)
</fix>
+ <fix>
+ Improve DIGEST authentication security. (remm)
+ </fix>
+ <fix>
+ <bug>51698</bug>: Fix CVE-2011-3190. Prevent AJP message injection. (markt)
+ </fix>
+ <fix>
+ Add system properties which restrict parameter count (org.apache.tomcat.util.http.Parameters.MAX_COUNT
+ default to 512) and header count (org.apache.tomcat.util.http.MimeHeaders.MAX_COUNT to 128). (remm)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">
[View Less]
12 years, 11 months
JBossWeb SVN: r1958 - tags.
by jbossweb-commits@lists.jboss.org
Author: bmaxwell
Date: 2012-02-13 12:44:09 -0500 (Mon, 13 Feb 2012)
New Revision: 1958
Added:
tags/JBOSSWEB_2_1_12_GA_patch03/
Log:
[JBPAPP-8110] create 2.1.12 patch03 tag
12 years, 11 months
JBossWeb SVN: r1957 - branches.
by jbossweb-commits@lists.jboss.org
Author: bmaxwell
Date: 2012-02-13 10:39:44 -0500 (Mon, 13 Feb 2012)
New Revision: 1957
Added:
branches/JBOSSWEB_2_1_12_GA_JBPAPP-8108/
Log:
[JBPAPP-8108] one off patch branch
12 years, 11 months