JBossWeb SVN: r1879 - trunk/java/org/apache/catalina/core.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2011-11-30 08:54:33 -0500 (Wed, 30 Nov 2011)
New Revision: 1879
Modified:
trunk/java/org/apache/catalina/core/StandardHostValve.java
Log:
AS7-2844: Modify suspicious check in the host valve, since the path being null (or not) may
be from some specification change.
Modified: trunk/java/org/apache/catalina/core/StandardHostValve.java
===================================================================
--- trunk/java/org/apache/catalina/core/StandardHostValve.java 2011-11-28 15:15:21 UTC (rev 1878)
+++ trunk/java/org/apache/catalina/core/StandardHostValve.java 2011-11-30 13:54:33 UTC (rev 1879)
@@ -285,7 +285,8 @@
status(request, response);
}
}
- if (request.isEventMode() && asyncContext.getPath() == null) {
+ // AS7-2844 if (request.isEventMode() && asyncContext.getPath() == null) {
+ if (!asyncContext.isReady()) {
asyncContext.complete();
}
}
13 years
JBossWeb SVN: r1878 - tags.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2011-11-28 10:15:21 -0500 (Mon, 28 Nov 2011)
New Revision: 1878
Added:
tags/JBOSSWEB_7_0_4_FINAL/
Log:
Web 7.0.4
13 years
JBossWeb SVN: r1877 - in branches/2.1.x: webapps/docs and 1 other directories.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2011-11-24 08:56:17 -0500 (Thu, 24 Nov 2011)
New Revision: 1877
Modified:
branches/2.1.x/java/org/apache/catalina/manager/JspHelper.java
branches/2.1.x/webapps/docs/changelog.xml
branches/2.1.x/webapps/manager/sessionDetail.jsp
branches/2.1.x/webapps/manager/sessionsList.jsp
Log:
Fix CVE-2010-4172. Multiple XSS in Manager web application
Modified: branches/2.1.x/java/org/apache/catalina/manager/JspHelper.java
===================================================================
--- branches/2.1.x/java/org/apache/catalina/manager/JspHelper.java 2011-11-24 12:15:14 UTC (rev 1876)
+++ branches/2.1.x/java/org/apache/catalina/manager/JspHelper.java 2011-11-24 13:56:17 UTC (rev 1877)
@@ -58,7 +58,7 @@
}
private static String localeToString(Locale locale) {
if (locale != null) {
- return locale.toString();//locale.getDisplayName();
+ return escapeXml(locale.toString());//locale.getDisplayName();
} else {
return "";
}
Modified: branches/2.1.x/webapps/docs/changelog.xml
===================================================================
--- branches/2.1.x/webapps/docs/changelog.xml 2011-11-24 12:15:14 UTC (rev 1876)
+++ branches/2.1.x/webapps/docs/changelog.xml 2011-11-24 13:56:17 UTC (rev 1877)
@@ -35,6 +35,9 @@
<fix>
<jira>JBPAPP-7267</jira> ParserUtils uses JAXP in a non-modular classloading compatible way (jfclere)
</fix>
+ <fix>
+ CVE-2010-4172: Multiple XSS in Manager application. (markt/kkolinko)
+ </fix>
</changelog>
</subsection>
</section>
Modified: branches/2.1.x/webapps/manager/sessionDetail.jsp
===================================================================
--- branches/2.1.x/webapps/manager/sessionDetail.jsp 2011-11-24 12:15:14 UTC (rev 1876)
+++ branches/2.1.x/webapps/manager/sessionDetail.jsp 2011-11-24 13:56:17 UTC (rev 1877)
@@ -14,8 +14,10 @@
<% String path = (String) request.getAttribute("path");
Session currentSession = (Session)request.getAttribute("currentSession");
HttpSession currentHttpSession = currentSession.getSession();
- String currentSessionId = currentSession.getId();
- String submitUrl = ((HttpServletRequest)pageContext.getRequest()).getRequestURL().toString();
+ String currentSessionId = JspHelper.escapeXml(currentSession.getId());
+ String submitUrl = JspHelper.escapeXml(response.encodeURL(
+ ((HttpServletRequest) pageContext.getRequest()).getRequestURI() +
+ "?path=" + path));
%>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
@@ -29,7 +31,7 @@
<title>Sessions Administration: details for <%= currentSessionId %></title>
</head>
<body>
-<h1>Details for Session <%= JspHelper.escapeXml(currentSessionId) %></h1>
+<h1>Details for Session <%= currentSessionId %></h1>
<table style="text-align: left;" border="0">
<tr>
@@ -104,7 +106,7 @@
String attributeName = (String) attributeNamesEnumeration.nextElement();
%>
<tr>
- <td align="center"><form action="<%= submitUrl %>"><div><input type="hidden" name="path" value="<%= path %>" /><input type="hidden" name="action" value="removeSessionAttribute" /><input type="hidden" name="sessionId" value="<%= currentSessionId %>" /><input type="hidden" name="attributeName" value="<%= attributeName %>" /><input type="submit" value="Remove" /></div></form></td>
+ <td align="center"><form action="<%= submitUrl %>"><div><input type="hidden" name="action" value="<%= JspHelper.escapeXml(attributeName) %>" /><input type="hidden" name="sessionId" value="<%= currentSessionId %>" /><input type="hidden" name="attributeName" value="<%= attributeName %>" /><input type="submit" value="Remove" /></div></form></td>
<td><%= JspHelper.escapeXml(attributeName) %></td>
<td><% Object attributeValue = currentHttpSession.getAttribute(attributeName); %><span title="<%= attributeValue == null ? "" : attributeValue.getClass().toString() %>"><%= JspHelper.escapeXml(attributeValue) %></span></td>
</tr>
Modified: branches/2.1.x/webapps/manager/sessionsList.jsp
===================================================================
--- branches/2.1.x/webapps/manager/sessionsList.jsp 2011-11-24 12:15:14 UTC (rev 1876)
+++ branches/2.1.x/webapps/manager/sessionsList.jsp 2011-11-24 13:56:17 UTC (rev 1877)
@@ -10,7 +10,9 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<% String path = (String) request.getAttribute("path");
- String submitUrl = ((HttpServletRequest)pageContext.getRequest()).getRequestURL().append("?path=").append(path).toString();
+ String submitUrl = JspHelper.escapeXml(response.encodeURL(
+ ((HttpServletRequest) pageContext.getRequest()).getRequestURI() +
+ "?path=" + path));
Collection activeSessions = (Collection) request.getAttribute("activeSessions");
%>
<head>
@@ -22,10 +24,10 @@
<meta name="author" content="Cedrik LIME"/>
<meta name="copyright" content="copyright 2005-2007 the Apache Software Foundation"/>
<meta name="robots" content="noindex,nofollow,noarchive"/>
- <title>Sessions Administration for <%= path %></title>
+ <title>Sessions Administration for <%= JspHelper.escapeXml(path) %></title>
</head>
<body>
-<h1>Sessions Administration for <%= path %></h1>
+<h1>Sessions Administration for <%= JspHelper.escapeXml(path) %></h1>
<p>Tips:</p>
<ul>
@@ -39,13 +41,13 @@
<form action="<%= submitUrl %>" method="post" id="sessionsForm">
<fieldset><legend>Active HttpSessions informations</legend>
<input type="hidden" name="action" id="sessionsFormAction" value="injectSessions"/>
- <input type="hidden" name="sort" id="sessionsFormSort" value="<%= (String) request.getAttribute("sort") %>"/>
+ <input type="hidden" name="sort" id="sessionsFormSort" value="<%= JspHelper.escapeXml(request.getAttribute("sort")) %>"/>
<% String order = (String) request.getAttribute("order");
if (order == null || "".equals(order)) {
order = "ASC";
}
%>
- <input type="hidden" name="order" id="sessionsFormSortOrder" value="<%= order %>"/>
+ <input type="hidden" name="order" id="sessionsFormSortOrder" value="<%= JspHelper.escapeXml(order) %>"/>
<input type="submit" name="refresh" id="refreshButton" value="Refresh Sessions list" onclick="document.getElementById('sessionsFormAction').value='refreshSessions'; return true;"/>
<%= JspHelper.formatNumber(activeSessions.size()) %> active Sessions<br/>
<table border="1" cellpadding="2" cellspacing="2" width="100%">
@@ -79,11 +81,11 @@
<% Iterator iter = activeSessions.iterator();
while (iter.hasNext()) {
Session currentSession = (Session) iter.next();
- String currentSessionId = currentSession.getId();
+ String currentSessionId = JspHelper.escapeXml(currentSession.getId());
%>
<tr>
<td>
-<input type="checkbox" name="sessionIds" value="<%= currentSessionId %>" /><a href="<%= submitUrl %>&action=sessionDetail&sessionId=<%= currentSessionId %>" target="_new"><%= JspHelper.escapeXml(currentSessionId) %></a>
+<input type="checkbox" name="sessionIds" value="<%= currentSessionId %>" /><a href="<%= submitUrl %>&action=sessionDetail&sessionId=<%= currentSessionId %>" target="_new"><%= currentSessionId %></a>
</td>
<td style="text-align: center;"><%= JspHelper.guessDisplayLocaleFromSession(currentSession) %></td>
<td style="text-align: center;"><%= JspHelper.guessDisplayUserFromSession(currentSession) %></td>
@@ -119,4 +121,4 @@
</div--%>
</body>
-</html>
\ No newline at end of file
+</html>
13 years, 1 month
JBossWeb SVN: r1876 - branches/2.1.x/webapps/manager.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2011-11-24 07:15:14 -0500 (Thu, 24 Nov 2011)
New Revision: 1876
Modified:
branches/2.1.x/webapps/manager/sessionDetail.jsp
Log:
Arrange the dos format...
Modified: branches/2.1.x/webapps/manager/sessionDetail.jsp
===================================================================
--- branches/2.1.x/webapps/manager/sessionDetail.jsp 2011-11-24 10:40:48 UTC (rev 1875)
+++ branches/2.1.x/webapps/manager/sessionDetail.jsp 2011-11-24 12:15:14 UTC (rev 1876)
@@ -1,132 +1,132 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<%@page session="false" contentType="text/html; charset=ISO-8859-1" %>
-<%@page import="java.util.Enumeration" %>
-<%@page import="javax.servlet.http.HttpSession" %>
-<%@page import="org.apache.catalina.Session" %>
-<%@page import="org.apache.catalina.manager.JspHelper" %>
-<!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<%--!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"--%>
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-<% String path = (String) request.getAttribute("path");
- Session currentSession = (Session)request.getAttribute("currentSession");
- HttpSession currentHttpSession = currentSession.getSession();
- String currentSessionId = currentSession.getId();
- String submitUrl = ((HttpServletRequest)pageContext.getRequest()).getRequestURL().toString();
-%>
-<head>
- <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
- <meta http-equiv="pragma" content="no-cache"/><!-- HTTP 1.0 -->
- <meta http-equiv="cache-control" content="no-cache,must-revalidate"/><!-- HTTP 1.1 -->
- <meta http-equiv="expires" content="0"/><!-- 0 is an invalid value and should be treated as 'now' -->
- <meta http-equiv="content-language" content="en"/>
- <meta name="author" content="Cedrik LIME"/>
- <meta name="copyright" content="copyright 2005-2007 the Apache Software Foundation"/>
- <meta name="robots" content="noindex,nofollow,noarchive"/>
- <title>Sessions Administration: details for <%= currentSessionId %></title>
-</head>
-<body>
-<h1>Details for Session <%= JspHelper.escapeXml(currentSessionId) %></h1>
-
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<%@page session="false" contentType="text/html; charset=ISO-8859-1" %>
+<%@page import="java.util.Enumeration" %>
+<%@page import="javax.servlet.http.HttpSession" %>
+<%@page import="org.apache.catalina.Session" %>
+<%@page import="org.apache.catalina.manager.JspHelper" %>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<%--!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"--%>
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<% String path = (String) request.getAttribute("path");
+ Session currentSession = (Session)request.getAttribute("currentSession");
+ HttpSession currentHttpSession = currentSession.getSession();
+ String currentSessionId = currentSession.getId();
+ String submitUrl = ((HttpServletRequest)pageContext.getRequest()).getRequestURL().toString();
+%>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
+ <meta http-equiv="pragma" content="no-cache"/><!-- HTTP 1.0 -->
+ <meta http-equiv="cache-control" content="no-cache,must-revalidate"/><!-- HTTP 1.1 -->
+ <meta http-equiv="expires" content="0"/><!-- 0 is an invalid value and should be treated as 'now' -->
+ <meta http-equiv="content-language" content="en"/>
+ <meta name="author" content="Cedrik LIME"/>
+ <meta name="copyright" content="copyright 2005-2007 the Apache Software Foundation"/>
+ <meta name="robots" content="noindex,nofollow,noarchive"/>
+ <title>Sessions Administration: details for <%= currentSessionId %></title>
+</head>
+<body>
+<h1>Details for Session <%= JspHelper.escapeXml(currentSessionId) %></h1>
+
<table style="text-align: left;" border="0">
<tr>
<th>Session Id</th>
<td><%= currentSessionId %></td>
</tr>
- <tr>
- <th>Guessed Locale</th>
- <td><%= JspHelper.guessDisplayLocaleFromSession(currentSession) %></td>
- </tr>
- <tr>
- <th>Guessed User</th>
- <td><%= JspHelper.guessDisplayUserFromSession(currentSession) %></td>
- </tr>
- <tr>
- <th>Creation Time</th>
- <td><%= JspHelper.getDisplayCreationTimeForSession(currentSession) %></td>
- </tr>
- <tr>
- <th>Last Accessed Time</th>
- <td><%= JspHelper.getDisplayLastAccessedTimeForSession(currentSession) %></td>
- </tr>
- <tr>
- <th>Session Max Inactive Interval</th>
- <td><%= JspHelper.secondsToTimeString(currentSession.getMaxInactiveInterval()) %></td>
- </tr>
- <tr>
- <th>Used Time</th>
- <td><%= JspHelper.getDisplayUsedTimeForSession(currentSession) %></td>
- </tr>
- <tr>
- <th>Inactive Time</th>
- <td><%= JspHelper.getDisplayInactiveTimeForSession(currentSession) %></td>
- </tr>
- <tr>
- <th>TTL</th>
- <td><%= JspHelper.getDisplayTTLForSession(currentSession) %></td>
- </tr>
+ <tr>
+ <th>Guessed Locale</th>
+ <td><%= JspHelper.guessDisplayLocaleFromSession(currentSession) %></td>
+ </tr>
+ <tr>
+ <th>Guessed User</th>
+ <td><%= JspHelper.guessDisplayUserFromSession(currentSession) %></td>
+ </tr>
+ <tr>
+ <th>Creation Time</th>
+ <td><%= JspHelper.getDisplayCreationTimeForSession(currentSession) %></td>
+ </tr>
+ <tr>
+ <th>Last Accessed Time</th>
+ <td><%= JspHelper.getDisplayLastAccessedTimeForSession(currentSession) %></td>
+ </tr>
+ <tr>
+ <th>Session Max Inactive Interval</th>
+ <td><%= JspHelper.secondsToTimeString(currentSession.getMaxInactiveInterval()) %></td>
+ </tr>
+ <tr>
+ <th>Used Time</th>
+ <td><%= JspHelper.getDisplayUsedTimeForSession(currentSession) %></td>
+ </tr>
+ <tr>
+ <th>Inactive Time</th>
+ <td><%= JspHelper.getDisplayInactiveTimeForSession(currentSession) %></td>
+ </tr>
+ <tr>
+ <th>TTL</th>
+ <td><%= JspHelper.getDisplayTTLForSession(currentSession) %></td>
+ </tr>
</table>
-
-<p style="text-align: center;"><button type="button" onclick="window.location.reload()">Refresh</button></p>
-
-<div class="error"><%= JspHelper.escapeXml(request.getAttribute("error")) %></div>
-<div class="message"><%= JspHelper.escapeXml(request.getAttribute("message")) %></div>
-
-<table style="text-align: left;" border="1" cellpadding="2" cellspacing="2">
-<% int nAttributes = 0;
- Enumeration attributeNamesEnumeration = currentHttpSession.getAttributeNames();
- while (attributeNamesEnumeration.hasMoreElements()) {
- attributeNamesEnumeration.nextElement();
- ++nAttributes;
- }
-%>
- <caption style="font-variant: small-caps;"><%= JspHelper.formatNumber(nAttributes) %> attributes</caption>
- <thead>
- <tr>
- <th>Remove Attribute</th>
- <th>Attribute name</th>
- <th>Attribute value</th>
- </tr>
- </thead>
- <%--tfoot>
- <tr>
- <td colspan="3" style="text-align: center;">
- TODO: set Max Inactive Interval on sessions
- </td>
- </tr>
- </tfoot--%>
- <tbody>
-<% attributeNamesEnumeration = currentHttpSession.getAttributeNames();
- while (attributeNamesEnumeration.hasMoreElements()) {
- String attributeName = (String) attributeNamesEnumeration.nextElement();
-%>
- <tr>
- <td align="center"><form action="<%= submitUrl %>"><div><input type="hidden" name="path" value="<%= path %>" /><input type="hidden" name="action" value="removeSessionAttribute" /><input type="hidden" name="sessionId" value="<%= currentSessionId %>" /><input type="hidden" name="attributeName" value="<%= attributeName %>" /><input type="submit" value="Remove" /></div></form></td>
- <td><%= JspHelper.escapeXml(attributeName) %></td>
- <td><% Object attributeValue = currentHttpSession.getAttribute(attributeName); %><span title="<%= attributeValue == null ? "" : attributeValue.getClass().toString() %>"><%= JspHelper.escapeXml(attributeValue) %></span></td>
- </tr>
-<% } // end while %>
- </tbody>
-</table>
-
-<p style="text-align: center;"><button type="button" onclick="window.close()">Close window</button></p>
-
-<%--div style="display: none;">
-<p>
- <a href="http://validator.w3.org/check?uri=referer"><img
- src="http://www.w3.org/Icons/valid-html401"
- alt="Valid HTML 4.01!" height="31" width="88"></a>
- <a href="http://validator.w3.org/check?uri=referer"><img
- src="http://www.w3.org/Icons/valid-xhtml10"
- alt="Valid XHTML 1.0!" height="31" width="88" /></a>
- <a href="http://validator.w3.org/check?uri=referer"><img
- src="http://www.w3.org/Icons/valid-xhtml11"
- alt="Valid XHTML 1.1!" height="31" width="88" /></a>
-</p>
-</div--%>
-
-</body>
-</html>
\ No newline at end of file
+
+<p style="text-align: center;"><button type="button" onclick="window.location.reload()">Refresh</button></p>
+
+<div class="error"><%= JspHelper.escapeXml(request.getAttribute("error")) %></div>
+<div class="message"><%= JspHelper.escapeXml(request.getAttribute("message")) %></div>
+
+<table style="text-align: left;" border="1" cellpadding="2" cellspacing="2">
+<% int nAttributes = 0;
+ Enumeration attributeNamesEnumeration = currentHttpSession.getAttributeNames();
+ while (attributeNamesEnumeration.hasMoreElements()) {
+ attributeNamesEnumeration.nextElement();
+ ++nAttributes;
+ }
+%>
+ <caption style="font-variant: small-caps;"><%= JspHelper.formatNumber(nAttributes) %> attributes</caption>
+ <thead>
+ <tr>
+ <th>Remove Attribute</th>
+ <th>Attribute name</th>
+ <th>Attribute value</th>
+ </tr>
+ </thead>
+ <%--tfoot>
+ <tr>
+ <td colspan="3" style="text-align: center;">
+ TODO: set Max Inactive Interval on sessions
+ </td>
+ </tr>
+ </tfoot--%>
+ <tbody>
+<% attributeNamesEnumeration = currentHttpSession.getAttributeNames();
+ while (attributeNamesEnumeration.hasMoreElements()) {
+ String attributeName = (String) attributeNamesEnumeration.nextElement();
+%>
+ <tr>
+ <td align="center"><form action="<%= submitUrl %>"><div><input type="hidden" name="path" value="<%= path %>" /><input type="hidden" name="action" value="removeSessionAttribute" /><input type="hidden" name="sessionId" value="<%= currentSessionId %>" /><input type="hidden" name="attributeName" value="<%= attributeName %>" /><input type="submit" value="Remove" /></div></form></td>
+ <td><%= JspHelper.escapeXml(attributeName) %></td>
+ <td><% Object attributeValue = currentHttpSession.getAttribute(attributeName); %><span title="<%= attributeValue == null ? "" : attributeValue.getClass().toString() %>"><%= JspHelper.escapeXml(attributeValue) %></span></td>
+ </tr>
+<% } // end while %>
+ </tbody>
+</table>
+
+<p style="text-align: center;"><button type="button" onclick="window.close()">Close window</button></p>
+
+<%--div style="display: none;">
+<p>
+ <a href="http://validator.w3.org/check?uri=referer"><img
+ src="http://www.w3.org/Icons/valid-html401"
+ alt="Valid HTML 4.01!" height="31" width="88"></a>
+ <a href="http://validator.w3.org/check?uri=referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+ <a href="http://validator.w3.org/check?uri=referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml11"
+ alt="Valid XHTML 1.1!" height="31" width="88" /></a>
+</p>
+</div--%>
+
+</body>
+</html>
Property changes on: branches/2.1.x/webapps/manager/sessionDetail.jsp
___________________________________________________________________
Added: svn:keywords
+ Author Date Id Revision
Added: svn:eol-style
+ native
13 years, 1 month
JBossWeb SVN: r1875 - in branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina: users and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2011-11-24 05:40:48 -0500 (Thu, 24 Nov 2011)
New Revision: 1875
Modified:
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/users/MemoryUser.java
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/users/MemoryUserDatabase.java
Log:
Fix CVE-2011-2204.
Prevent user passwords appearing in log files if a runtime exception (e.g. OOME) occurs while creating a new user for a MemoryUserDatabase via JMX.
Modified: branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java 2011-11-24 10:25:05 UTC (rev 1874)
+++ branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java 2011-11-24 10:40:48 UTC (rev 1875)
@@ -180,7 +180,7 @@
MBeanUtils.createMBean(group);
} catch (Exception e) {
IllegalArgumentException iae = new IllegalArgumentException
- ("Exception creating group " + group + " MBean");
+ ("Exception creating group [" + groupname + "] MBean");
iae.initCause(e);
throw iae;
}
@@ -203,7 +203,7 @@
MBeanUtils.createMBean(role);
} catch (Exception e) {
IllegalArgumentException iae = new IllegalArgumentException
- ("Exception creating role " + role + " MBean");
+ ("Exception creating role [" + rolename + "] MBean");
iae.initCause(e);
throw iae;
}
@@ -228,7 +228,7 @@
MBeanUtils.createMBean(user);
} catch (Exception e) {
IllegalArgumentException iae = new IllegalArgumentException
- ("Exception creating user " + user + " MBean");
+ ("Exception creating user [" + username + "] MBean");
iae.initCause(e);
throw iae;
}
@@ -256,7 +256,7 @@
return (oname.toString());
} catch (MalformedObjectNameException e) {
IllegalArgumentException iae = new IllegalArgumentException
- ("Cannot create object name for group " + group);
+ ("Cannot create object name for group [" + groupname + "]");
iae.initCause(e);
throw iae;
}
@@ -283,7 +283,7 @@
return (oname.toString());
} catch (MalformedObjectNameException e) {
IllegalArgumentException iae = new IllegalArgumentException
- ("Cannot create object name for role " + role);
+ ("Cannot create object name for role [" + rolename + "]");
iae.initCause(e);
throw iae;
}
@@ -310,7 +310,7 @@
return (oname.toString());
} catch (MalformedObjectNameException e) {
IllegalArgumentException iae = new IllegalArgumentException
- ("Cannot create object name for user " + user);
+ ("Cannot create object name for user [" + username + "]");
iae.initCause(e);
throw iae;
}
@@ -335,7 +335,7 @@
database.removeGroup(group);
} catch (Exception e) {
IllegalArgumentException iae = new IllegalArgumentException
- ("Exception destroying group " + group + " MBean");
+ ("Exception destroying group [" + groupname + "] MBean");
iae.initCause(e);
throw iae;
}
@@ -360,7 +360,7 @@
database.removeRole(role);
} catch (Exception e) {
IllegalArgumentException iae = new IllegalArgumentException
- ("Exception destroying role " + role + " MBean");
+ ("Exception destroying role [" + rolename + "] MBean");
iae.initCause(e);
throw iae;
}
@@ -385,7 +385,7 @@
database.removeUser(user);
} catch (Exception e) {
IllegalArgumentException iae = new IllegalArgumentException
- ("Exception destroying user " + user + " MBean");
+ ("Exception destroying user [" + username + "] MBean");
iae.initCause(e);
throw iae;
}
Modified: branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/users/MemoryUser.java
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/users/MemoryUser.java 2011-11-24 10:25:05 UTC (rev 1874)
+++ branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/users/MemoryUser.java 2011-11-24 10:40:48 UTC (rev 1875)
@@ -246,7 +246,7 @@
* <code>username</code> or </code>name</code> for the username
* property.</p>
*/
- public String toString() {
+ public String toXml() {
StringBuffer sb = new StringBuffer("<user username=\"");
sb.append(RequestUtil.filter(username));
@@ -293,5 +293,52 @@
}
+ /**
+ * <p>Return a String representation of this user.</p>
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("User username=\"");
+ sb.append(RequestUtil.filter(username));
+ sb.append("\"");
+ if (fullName != null) {
+ sb.append(", fullName=\"");
+ sb.append(RequestUtil.filter(fullName));
+ sb.append("\"");
+ }
+ synchronized (groups) {
+ if (groups.size() > 0) {
+ sb.append(", groups=\"");
+ int n = 0;
+ Iterator<Group> values = groups.iterator();
+ while (values.hasNext()) {
+ if (n > 0) {
+ sb.append(',');
+ }
+ n++;
+ sb.append(RequestUtil.filter(values.next().getGroupname()));
+ }
+ sb.append("\"");
+ }
+ }
+ synchronized (roles) {
+ if (roles.size() > 0) {
+ sb.append(", roles=\"");
+ int n = 0;
+ Iterator<Role> values = roles.iterator();
+ while (values.hasNext()) {
+ if (n > 0) {
+ sb.append(',');
+ }
+ n++;
+ sb.append(RequestUtil.filter(values.next().getRolename()));
+ }
+ sb.append("\"");
+ }
+ }
+ return (sb.toString());
+ }
+
+
}
Modified: branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/users/MemoryUserDatabase.java
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/users/MemoryUserDatabase.java 2011-11-24 10:25:05 UTC (rev 1874)
+++ branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/users/MemoryUserDatabase.java 2011-11-24 10:40:48 UTC (rev 1875)
@@ -541,7 +541,7 @@
values = getUsers();
while (values.hasNext()) {
writer.print(" ");
- writer.println(values.next());
+ writer.println(((MemoryUser) values.next()).toXml());
}
// Print the file epilog
13 years, 1 month
JBossWeb SVN: r1874 - in branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina: realm and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2011-11-24 05:25:05 -0500 (Thu, 24 Nov 2011)
New Revision: 1874
Modified:
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/AuthenticatorBase.java
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/DigestAuthenticator.java
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/LocalStrings.properties
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/mbeans-descriptors.xml
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/realm/RealmBase.java
Log:
Port DIGEST improvements. (r1848)
Modified: branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/AuthenticatorBase.java
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/AuthenticatorBase.java 2011-11-24 10:10:19 UTC (rev 1873)
+++ branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/AuthenticatorBase.java 2011-11-24 10:25:05 UTC (rev 1874)
@@ -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: branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/DigestAuthenticator.java
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/DigestAuthenticator.java 2011-11-24 10:10:19 UTC (rev 1873)
+++ branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/DigestAuthenticator.java 2011-11-24 10:25:05 UTC (rev 1874)
@@ -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: branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/LocalStrings.properties
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/LocalStrings.properties 2011-11-24 10:10:19 UTC (rev 1873)
+++ branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/LocalStrings.properties 2011-11-24 10:25:05 UTC (rev 1874)
@@ -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: branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/mbeans-descriptors.xml
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/mbeans-descriptors.xml 2011-11-24 10:10:19 UTC (rev 1873)
+++ branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/authenticator/mbeans-descriptors.xml 2011-11-24 10:25:05 UTC (rev 1874)
@@ -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: branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/realm/RealmBase.java
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/realm/RealmBase.java 2011-11-24 10:10:19 UTC (rev 1873)
+++ branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/realm/RealmBase.java 2011-11-24 10:25:05 UTC (rev 1874)
@@ -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) {
13 years, 1 month
JBossWeb SVN: r1873 - branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/net.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2011-11-24 05:10:19 -0500 (Thu, 24 Nov 2011)
New Revision: 1873
Modified:
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/net/AprEndpoint.java
Log:
Fix for CVE-2011-2526.
Modified: branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/net/AprEndpoint.java
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/net/AprEndpoint.java 2011-11-24 10:05:31 UTC (rev 1872)
+++ branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/net/AprEndpoint.java 2011-11-24 10:10:19 UTC (rev 1873)
@@ -301,7 +301,7 @@
/**
* Use endfile 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; }
13 years, 1 month
JBossWeb SVN: r1872 - branches/2.1.x/java/org/apache/tomcat/util/net.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2011-11-24 05:05:31 -0500 (Thu, 24 Nov 2011)
New Revision: 1872
Modified:
branches/2.1.x/java/org/apache/tomcat/util/net/AprEndpoint.java
Log:
Fix CVE-2011-2526.
Modified: branches/2.1.x/java/org/apache/tomcat/util/net/AprEndpoint.java
===================================================================
--- branches/2.1.x/java/org/apache/tomcat/util/net/AprEndpoint.java 2011-11-24 08:23:37 UTC (rev 1871)
+++ branches/2.1.x/java/org/apache/tomcat/util/net/AprEndpoint.java 2011-11-24 10:05:31 UTC (rev 1872)
@@ -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; }
13 years, 1 month
JBossWeb SVN: r1871 - in branches/2.1.x: java/org/apache/catalina/realm and 1 other directories.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2011-11-24 03:23:37 -0500 (Thu, 24 Nov 2011)
New Revision: 1871
Modified:
branches/2.1.x/java/org/apache/catalina/authenticator/AuthenticatorBase.java
branches/2.1.x/java/org/apache/catalina/authenticator/DigestAuthenticator.java
branches/2.1.x/java/org/apache/catalina/authenticator/LocalStrings.properties
branches/2.1.x/java/org/apache/catalina/authenticator/mbeans-descriptors.xml
branches/2.1.x/java/org/apache/catalina/realm/RealmBase.java
branches/2.1.x/webapps/docs/changelog.xml
Log:
Port DIGEST improvements. (r1848)
Modified: branches/2.1.x/java/org/apache/catalina/authenticator/AuthenticatorBase.java
===================================================================
--- branches/2.1.x/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2011-11-23 09:52:37 UTC (rev 1870)
+++ branches/2.1.x/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2011-11-24 08:23:37 UTC (rev 1871)
@@ -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: branches/2.1.x/java/org/apache/catalina/authenticator/DigestAuthenticator.java
===================================================================
--- branches/2.1.x/java/org/apache/catalina/authenticator/DigestAuthenticator.java 2011-11-23 09:52:37 UTC (rev 1870)
+++ branches/2.1.x/java/org/apache/catalina/authenticator/DigestAuthenticator.java 2011-11-24 08:23:37 UTC (rev 1871)
@@ -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: branches/2.1.x/java/org/apache/catalina/authenticator/LocalStrings.properties
===================================================================
--- branches/2.1.x/java/org/apache/catalina/authenticator/LocalStrings.properties 2011-11-23 09:52:37 UTC (rev 1870)
+++ branches/2.1.x/java/org/apache/catalina/authenticator/LocalStrings.properties 2011-11-24 08:23:37 UTC (rev 1871)
@@ -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: branches/2.1.x/java/org/apache/catalina/authenticator/mbeans-descriptors.xml
===================================================================
--- branches/2.1.x/java/org/apache/catalina/authenticator/mbeans-descriptors.xml 2011-11-23 09:52:37 UTC (rev 1870)
+++ branches/2.1.x/java/org/apache/catalina/authenticator/mbeans-descriptors.xml 2011-11-24 08:23:37 UTC (rev 1871)
@@ -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: branches/2.1.x/java/org/apache/catalina/realm/RealmBase.java
===================================================================
--- branches/2.1.x/java/org/apache/catalina/realm/RealmBase.java 2011-11-23 09:52:37 UTC (rev 1870)
+++ branches/2.1.x/java/org/apache/catalina/realm/RealmBase.java 2011-11-24 08:23:37 UTC (rev 1871)
@@ -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: branches/2.1.x/webapps/docs/changelog.xml
===================================================================
--- branches/2.1.x/webapps/docs/changelog.xml 2011-11-23 09:52:37 UTC (rev 1870)
+++ branches/2.1.x/webapps/docs/changelog.xml 2011-11-24 08:23:37 UTC (rev 1871)
@@ -17,6 +17,13 @@
<body>
<section name="JBoss Web 2.1.13.GA (jfclere)">
<subsection name="Coyote">
+ <changelog>
+ <fix>
+ Improve DIGEST authentication security. (remm)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Coyote">
<changelog>
<fix>
<bug>51698</bug>: Fix CVE-2011-3190. Prevent AJP message injection. (markt)
13 years, 1 month
JBossWeb SVN: r1870 - in trunk: webapps/docs and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2011-11-23 04:52:37 -0500 (Wed, 23 Nov 2011)
New Revision: 1870
Modified:
trunk/java/org/apache/coyote/http11/Http11AprProcessor.java
trunk/java/org/apache/coyote/http11/Http11Processor.java
trunk/webapps/docs/changelog.xml
Log:
AS7-2739: Use the keepalive flag to close the connection in async or event mode.
Modified: trunk/java/org/apache/coyote/http11/Http11AprProcessor.java
===================================================================
--- trunk/java/org/apache/coyote/http11/Http11AprProcessor.java 2011-11-18 17:03:59 UTC (rev 1869)
+++ trunk/java/org/apache/coyote/http11/Http11AprProcessor.java 2011-11-23 09:52:37 UTC (rev 1870)
@@ -799,7 +799,7 @@
boolean pipelined = inputBuffer.nextRequest();
outputBuffer.nextRequest();
recycle();
- return (pipelined) ? SocketState.CLOSED : SocketState.OPEN;
+ return (pipelined || !keepAlive) ? SocketState.CLOSED : SocketState.OPEN;
} else {
return SocketState.LONG;
}
Modified: trunk/java/org/apache/coyote/http11/Http11Processor.java
===================================================================
--- trunk/java/org/apache/coyote/http11/Http11Processor.java 2011-11-18 17:03:59 UTC (rev 1869)
+++ trunk/java/org/apache/coyote/http11/Http11Processor.java 2011-11-23 09:52:37 UTC (rev 1870)
@@ -776,7 +776,7 @@
boolean pipelined = inputBuffer.nextRequest();
outputBuffer.nextRequest();
recycle();
- return (pipelined) ? SocketState.CLOSED : SocketState.OPEN;
+ return (pipelined || !keepAlive) ? SocketState.CLOSED : SocketState.OPEN;
} else {
return SocketState.LONG;
}
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2011-11-18 17:03:59 UTC (rev 1869)
+++ trunk/webapps/docs/changelog.xml 2011-11-23 09:52:37 UTC (rev 1870)
@@ -16,7 +16,7 @@
<body>
-<section name="JBoss Web 7.0.3.Final (remm)">
+<section name="JBoss Web 7.0.4.Final (remm)">
<subsection name="Catalina">
<changelog>
<update>
@@ -29,6 +29,10 @@
<fix>
Filter out multibyte chars when writing a char chunk. (rjung)
</fix>
+ <fix>
+ <jira>AS7-2739</jira>: Allow using the keepalive disable configuration
+ when using the async or event modes. (remm)
+ </fix>
</changelog>
</subsection>
</section>
13 years, 1 month