Package: tomcat6 Version: 6.0.32-6 Severity: normal Tags: patch User: ubuntu-de...@lists.ubuntu.com Usertags: origin-ubuntu precise ubuntu-patch
*** /tmp/tmpibvEX2 In Ubuntu, the attached patch was applied to achieve the following: Fixes two security issues (CVE-2011-1184 and CVE-2011-2526) * SECURITY UPDATE: HTTP DIGEST authentication weaknesses - debian/patches/0014-CVE-2011-1184.patch: add new nonce options in java/org/apache/catalina/authenticator/DigestAuthenticator.java, java/org/apache/catalina/authenticator/LocalStrings.properties, java/org/apache/catalina/authenticator/mbeans-descriptors.xml, java/org/apache/catalina/realm/RealmBase.java, webapps/docs/config/valve.xml. - CVE-2011-1184 * SECURITY UPDATE: file restriction bypass or denial of service via untrusted web application. - debian/patches/0015-CVE-2011-2526.patch: check canonical name in java/org/apache/catalina/connector/LocalStrings.properties, java/org/apache/catalina/connector/Request.java, java/org/apache/catalina/servlets/DefaultServlet.java, java/org/apache/coyote/http11/Http11AprProcessor.java, java/org/apache/coyote/http11/LocalStrings.properties, java/org/apache/tomcat/util/net/AprEndpoint.java, java/org/apache/tomcat/util/net/NioEndpoint.java. - CVE-2011-2526 Thanks for considering the patch. -- System Information: Debian Release: wheezy/sid APT prefers oneiric-updates APT policy: (500, 'oneiric-updates'), (500, 'oneiric-security'), (500, 'oneiric-proposed'), (500, 'oneiric') Architecture: amd64 (x86_64) Kernel: Linux 3.0.0-13-generic (SMP w/4 CPU cores) Locale: LANG=en_CA.UTF-8, LC_CTYPE=en_CA.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash
diff -Nru tomcat6-6.0.32/debian/changelog tomcat6-6.0.32/debian/changelog diff -Nru tomcat6-6.0.32/debian/patches/0014-CVE-2011-1184.patch tomcat6-6.0.32/debian/patches/0014-CVE-2011-1184.patch --- tomcat6-6.0.32/debian/patches/0014-CVE-2011-1184.patch 1969-12-31 19:00:00.000000000 -0500 +++ tomcat6-6.0.32/debian/patches/0014-CVE-2011-1184.patch 2011-11-08 07:55:11.000000000 -0500 @@ -0,0 +1,798 @@ +Description: fix HTTP DIGEST authentication weaknesses +Origin: upstream, http://svn.apache.org/viewvc?view=revision&revision=1158180 + +Index: tomcat6-6.0.32/java/org/apache/catalina/authenticator/DigestAuthenticator.java +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/catalina/authenticator/DigestAuthenticator.java 2010-04-29 11:00:41.000000000 -0400 ++++ tomcat6-6.0.32/java/org/apache/catalina/authenticator/DigestAuthenticator.java 2011-10-13 16:38:43.989355250 -0400 +@@ -23,11 +23,14 @@ + import java.security.MessageDigest; + import java.security.NoSuchAlgorithmException; + import java.security.Principal; ++import java.util.LinkedHashMap; ++import java.util.Map; + import java.util.StringTokenizer; + + import javax.servlet.http.HttpServletResponse; + + ++import org.apache.catalina.LifecycleException; + import org.apache.catalina.Realm; + import org.apache.catalina.connector.Request; + import org.apache.catalina.connector.Response; +@@ -47,8 +50,8 @@ + * @version $Id: DigestAuthenticator.java 939336 2010-04-29 15:00:41Z kkolinko $ + */ + +-public class DigestAuthenticator +- extends AuthenticatorBase { ++public class DigestAuthenticator extends AuthenticatorBase { ++ + private static Log log = LogFactory.getLog(DigestAuthenticator.class); + + +@@ -67,6 +70,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 +100,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 +150,58 @@ + } + + +- // --------------------------------------------------------- Public Methods ++ 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 +215,7 @@ + * + * @exception IOException if an input/output error occurs + */ ++ @Override + public boolean authenticate(Request request, + Response response, + LoginConfig config) +@@ -172,8 +262,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 +280,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 +297,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 +304,6 @@ + */ + protected String parseUsername(String authorization) { + +- //System.out.println("Authorization token : " + authorization); + // Validate the authorization credentials format + if (authorization == null) + return (null); +@@ -354,20 +363,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 +388,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 +405,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_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; ++ } ++ } + } +Index: tomcat6-6.0.32/java/org/apache/catalina/authenticator/LocalStrings.properties +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/catalina/authenticator/LocalStrings.properties 2009-12-21 07:56:09.000000000 -0500 ++++ tomcat6-6.0.32/java/org/apache/catalina/authenticator/LocalStrings.properties 2011-10-13 16:38:43.989355250 -0400 +@@ -28,5 +28,7 @@ + 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. ++ + formAuthenticator.forwardErrorFail=Unexpected error forwarding to error page + formAuthenticator.forwardLoginFail=Unexpected error forwarding to login page +Index: tomcat6-6.0.32/java/org/apache/catalina/authenticator/mbeans-descriptors.xml +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/catalina/authenticator/mbeans-descriptors.xml 2007-08-04 19:30:01.000000000 -0400 ++++ tomcat6-6.0.32/java/org/apache/catalina/authenticator/mbeans-descriptors.xml 2011-10-13 16:38:43.989355250 -0400 +@@ -60,10 +60,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" +Index: tomcat6-6.0.32/java/org/apache/catalina/realm/RealmBase.java +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/catalina/realm/RealmBase.java 2010-04-29 20:08:58.000000000 -0400 ++++ tomcat6-6.0.32/java/org/apache/catalina/realm/RealmBase.java 2011-10-13 16:38:43.989355250 -0400 +@@ -353,22 +353,27 @@ + * + * @param username Username of the Principal to look up + * @param clientDigest Digest which has been submitted by the client +- * @param nOnce Unique (or supposedly unique) token which has been used ++ * @param nonce Unique (or supposedly unique) token which has been used + * for this request + * @param realm Realm name + * @param md5a2 Second MD5 digest used to calculate the digest : + * MD5(Method + ":" + uri) + */ + public Principal authenticate(String username, String clientDigest, +- String nOnce, String nc, String cnonce, ++ String nonce, String nc, String cnonce, + String qop, String realm, + String md5a2) { + + 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) { +@@ -390,7 +395,7 @@ + + if (log.isDebugEnabled()) { + log.debug("Digest : " + clientDigest + " Username:" + username +- + " ClientSigest:" + clientDigest + " nOnce:" + nOnce ++ + " ClientSigest:" + clientDigest + " nonce:" + nonce + + " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop + + " realm:" + realm + "md5a2:" + md5a2 + + " Server digest:" + serverDigest); +Index: tomcat6-6.0.32/webapps/docs/config/valve.xml +=================================================================== +--- tomcat6-6.0.32.orig/webapps/docs/config/valve.xml 2010-11-02 15:26:07.000000000 -0400 ++++ tomcat6-6.0.32/webapps/docs/config/valve.xml 2011-10-13 16:38:43.989355250 -0400 +@@ -460,6 +460,12 @@ + used.</p> + </attribute> + ++ <attribute name="cnonceCacheSize" required="false"> ++ <p>To protect against replay attacks, the DIGEST authenticator tracks ++ client nonce and nonce count values. This attribute controls the size ++ of that cache. If not specified, the default value of 1000 is used.</p> ++ </attribute> ++ + <attribute name="disableProxyCaching" required="false"> + <p>Controls the caching of pages that are protected by security + constraints. Setting this to <code>false</code> may help work around +@@ -470,6 +476,26 @@ + <code>true</code> will be used.</p> + </attribute> + ++ <attribute name="key" required="false"> ++ <p>The secret key used by digest authentication. If not set, a secure ++ random value is generated. This should normally only be set when it is ++ necessary to keep key values constant either across server restarts ++ and/or across a cluster.</p> ++ </attribute> ++ ++ <attribute name="nonceValidity" required="false"> ++ <p>The time, in milliseconds, that a server generated nonce will be ++ considered valid for use in authentication. If not specified, the ++ default value of 300000 (5 minutes) will be used.</p> ++ </attribute> ++ ++ <attribute name="opaque" required="false"> ++ <p>The opaque server string used by digest authentication. If not set, a ++ random value is generated. This should normally only be set when it is ++ necessary to keep opaque values constant either across server restarts ++ and/or across a cluster.</p> ++ </attribute> ++ + <attribute name="securePagesWithPragma" required="false"> + <p>Controls the caching of pages that are protected by security + constraints. Setting this to <code>false</code> may help work around +@@ -479,6 +505,14 @@ + If not set, the default value of <code>true</code> will be used.</p> + </attribute> + ++ <attribute name="validateUri" required="false"> ++ <p>Should the URI be validated as required by RFC2617? If not specified, ++ the default value of <code>true</code> will be used. This should ++ normally only be set when Tomcat is located behind a reverse proxy and ++ the proxy is modifying the URI passed to Tomcat such that DIGEST ++ authentication always fails.</p> ++ </attribute> ++ + </attributes> + + </subsection> diff -Nru tomcat6-6.0.32/debian/patches/0015-CVE-2011-2526.patch tomcat6-6.0.32/debian/patches/0015-CVE-2011-2526.patch --- tomcat6-6.0.32/debian/patches/0015-CVE-2011-2526.patch 1969-12-31 19:00:00.000000000 -0500 +++ tomcat6-6.0.32/debian/patches/0015-CVE-2011-2526.patch 2011-11-08 07:55:19.000000000 -0500 @@ -0,0 +1,144 @@ +Description: fix file restriction bypass or denial of service via untrusted web application +Origin: upstream, http://svn.apache.org/viewvc?view=revision&revision=1146703 +Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634992 + +Index: tomcat6-6.0.32/java/org/apache/catalina/connector/LocalStrings.properties +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/catalina/connector/LocalStrings.properties 2011-01-20 16:36:06.000000000 -0500 ++++ tomcat6-6.0.32/java/org/apache/catalina/connector/LocalStrings.properties 2011-10-13 16:40:14.477357566 -0400 +@@ -61,6 +61,7 @@ + coyoteRequest.parseParameters=Exception thrown whilst processing POSTed parameters + coyoteRequest.postTooLarge=Parameters were not parsed because the size of the posted data was too big. Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs. + coyoteRequest.chunkedPostTooLarge=Parameters were not parsed because the size of the posted data was too big. Because this request was a chunked request, it could not be processed further. Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs. ++coyoteRequest.sendfileNotCanonical=Unable to determine canonical name of file [{0}] specified for use with sendfile + coyoteRequest.sessionEndAccessFail=Exception triggered ending access to session while recycling request + + requestFacade.nullRequest=The request object has been recycled and is no longer associated with this facade +Index: tomcat6-6.0.32/java/org/apache/catalina/connector/Request.java +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/catalina/connector/Request.java 2011-02-01 22:09:54.000000000 -0500 ++++ tomcat6-6.0.32/java/org/apache/catalina/connector/Request.java 2011-10-13 16:39:36.549356595 -0400 +@@ -19,6 +19,7 @@ + package org.apache.catalina.connector; + + ++import java.io.File; + import java.io.InputStream; + import java.io.IOException; + import java.io.BufferedReader; +@@ -1455,6 +1456,26 @@ + return; + } + ++ // Do the security check before any updates are made ++ if (Globals.IS_SECURITY_ENABLED && ++ name.equals("org.apache.tomcat.sendfile.filename")) { ++ // Use the canonical file name to avoid any possible symlink and ++ // relative path issues ++ String canonicalPath; ++ try { ++ canonicalPath = new File(value.toString()).getCanonicalPath(); ++ } catch (IOException e) { ++ throw new SecurityException(sm.getString( ++ "coyoteRequest.sendfileNotCanonical", value), e); ++ } ++ // Sendfile is performed in Tomcat's security context so need to ++ // check if the web app is permitted to access the file while still ++ // in the web app's security context ++ System.getSecurityManager().checkRead(canonicalPath); ++ // Update the value so the canonical path is used ++ value = canonicalPath; ++ } ++ + oldValue = attributes.put(name, value); + if (oldValue != null) { + replaced = true; +Index: tomcat6-6.0.32/java/org/apache/catalina/servlets/DefaultServlet.java +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/catalina/servlets/DefaultServlet.java 2011-01-20 12:08:54.000000000 -0500 ++++ tomcat6-6.0.32/java/org/apache/catalina/servlets/DefaultServlet.java 2011-10-13 16:39:36.549356595 -0400 +@@ -1619,7 +1619,6 @@ + request.setAttribute("org.apache.tomcat.sendfile.start", new Long(range.start)); + request.setAttribute("org.apache.tomcat.sendfile.end", new Long(range.end + 1)); + } +- request.setAttribute("org.apache.tomcat.sendfile.token", this); + return true; + } else { + return false; +Index: tomcat6-6.0.32/java/org/apache/coyote/http11/Http11AprProcessor.java +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/coyote/http11/Http11AprProcessor.java 2011-01-07 12:49:20.000000000 -0500 ++++ tomcat6-6.0.32/java/org/apache/coyote/http11/Http11AprProcessor.java 2011-10-13 16:39:36.549356595 -0400 +@@ -910,7 +910,18 @@ + sendfileData.socket = socket; + sendfileData.keepAlive = keepAlive; + if (!endpoint.getSendfile().add(sendfileData)) { +- openSocket = true; ++ if (sendfileData.socket == 0) { ++ // Didn't send all the data but the socket is no longer ++ // set. Something went wrong. Close the connection. ++ // Too late to set status code. ++ if (log.isDebugEnabled()) { ++ log.debug(sm.getString( ++ "http11processor.sendfile.error")); ++ } ++ error = true; ++ } else { ++ openSocket = true; ++ } + break; + } + } +Index: tomcat6-6.0.32/java/org/apache/coyote/http11/LocalStrings.properties +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/coyote/http11/LocalStrings.properties 2009-05-02 21:29:42.000000000 -0400 ++++ tomcat6-6.0.32/java/org/apache/coyote/http11/LocalStrings.properties 2011-10-13 16:39:36.549356595 -0400 +@@ -56,6 +56,7 @@ + http11processor.socket.info=Exception getting socket information + http11processor.socket.ssl=Exception getting SSL attributes + http11processor.socket.timeout=Error setting socket timeout ++http11processor.sendfile.error=Error sending data using sendfile. May be caused by invalid request attributes for start/end points + + # + # InternalInputBuffer +Index: tomcat6-6.0.32/java/org/apache/tomcat/util/net/AprEndpoint.java +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/tomcat/util/net/AprEndpoint.java 2011-02-01 03:07:46.000000000 -0500 ++++ tomcat6-6.0.32/java/org/apache/tomcat/util/net/AprEndpoint.java 2011-10-13 16:41:23.769359341 -0400 +@@ -1812,7 +1812,9 @@ + data.pos, data.end - data.pos, 0); + if (nw < 0) { + if (!(-nw == Status.EAGAIN)) { +- destroySocket(data.socket); ++ Pool.destroy(data.fdpool); ++ // No need to close socket, this will be done by ++ // calling code since data.socket == 0 + data.socket = 0; + return false; + } else { +Index: tomcat6-6.0.32/java/org/apache/tomcat/util/net/NioEndpoint.java +=================================================================== +--- tomcat6-6.0.32.orig/java/org/apache/tomcat/util/net/NioEndpoint.java 2011-01-07 13:43:39.000000000 -0500 ++++ tomcat6-6.0.32/java/org/apache/tomcat/util/net/NioEndpoint.java 2011-10-13 16:39:36.553356596 -0400 +@@ -1734,6 +1734,13 @@ + sd.pos += written; + sd.length -= written; + attachment.access(); ++ } else { ++ // Unusual not to be able to transfer any bytes ++ // Check the length was set correctly ++ if (sd.fchannel.size() <= sd.pos) { ++ throw new IOException("Sendfile configured to " + ++ "send more data than was available"); ++ } + } + } + if ( sd.length <= 0 && sc.getOutboundRemaining()<=0) { +@@ -1758,6 +1765,7 @@ + log.debug("Send file connection is being closed"); + } + cancelledKey(sk,SocketStatus.STOP,false); ++ return false; + } + } else if ( attachment.interestOps() == 0 && reg ) { + if (log.isDebugEnabled()) { diff -Nru tomcat6-6.0.32/debian/patches/series tomcat6-6.0.32/debian/patches/series --- tomcat6-6.0.32/debian/patches/series 2011-09-15 04:29:14.000000000 -0400 +++ tomcat6-6.0.32/debian/patches/series 2011-11-08 07:55:19.000000000 -0500 @@ -10,3 +10,5 @@ 0011-623242.patch 0012-CVE-2011-2204.patch 0013-CVE-2011-3190.patch +0014-CVE-2011-1184.patch +0015-CVE-2011-2526.patch