diff -Nru tomcat7-7.0.21/debian/changelog tomcat7-7.0.21/debian/changelog
--- tomcat7-7.0.21/debian/changelog 2011-09-07 15:47:47.000000000 +0700
+++ tomcat7-7.0.21/debian/changelog 2013-02-06 16:44:57.000000000 +0700
@@ -1,3 +1,34 @@
+tomcat7 (7.0.21-1ubuntu0.1) oneiric-security; urgency=low
+
+ * SECURITY UPDATE: Fix multiple vulnerabilities in Tomcat7
+ (LP: #1115053)
+ - debian/patches/CVE-2012-0022.patch: Fix for Denial of service. Based on
+ upstream patch.
+ - CVE-2012-0022, CVE-2011-4858
+ - debian/patches/CVE-2011-3375.patch: Fix for information disclosure. Based
+ on upstream patch.
+ - CVE-2011-3375
+ - debian/patches/CVE-2011-3376.patch: Fix for privilege escalation. Based on
+ upstream patch.
+ - CVE-2011-3376
+ - debian/patches/CVE-2012-2733.patch: Fix for Apache Tomcat Denial of
+ Service. Based on upstream patch.
+ - CVE-2012-2733
+ - debian/patches/CVE-2012-3546.patch: Fix for bypass of security
+ constraints. Based on upstream patch.
+ - CVE-2012-3546
+ - debian/patches/CVE-2012-4431.patch: Fix for bypass of CSRF prevention
+ filter. Based on upstream patch.
+ - CVE-2012-4431
+ - debian/patches/CVE-2012-4534.patch: Fix for CVE-2012-4534 Denial of
+ Service Vulnerability. Based on upstream patch.
+ - CVE-2012-4534
+ - debian/patches/CVE-2012-3439.patch: Fix for DIGEST authentication
+ weaknesses. Based on upstream patch.
+ - CVE-2012-3439, CVE-2012-5885, CVE-2012-5886, 2012-5887
+
+ -- Christian Kuersteiner Fri, 01 Feb 2013 14:28:48 +0700
+
tomcat7 (7.0.21-1) unstable; urgency=low
* New upstream release.
diff -Nru tomcat7-7.0.21/debian/control tomcat7-7.0.21/debian/control
--- tomcat7-7.0.21/debian/control 2011-09-07 15:47:47.000000000 +0700
+++ tomcat7-7.0.21/debian/control 2013-02-04 16:54:05.000000000 +0700
@@ -1,7 +1,8 @@
Source: tomcat7
Section: java
Priority: optional
-Maintainer: Debian Java Maintainers
+Maintainer: Ubuntu Developers
+XSBC-Original-Maintainer: Debian Java Maintainers
Uploaders: James Page ,
Miguel Landaeta , tony mancill
Build-Depends: default-jdk, ant-optional, debhelper (>= 7), po-debconf
diff -Nru tomcat7-7.0.21/debian/patches/CVE-2011-3375.patch tomcat7-7.0.21/debian/patches/CVE-2011-3375.patch
--- tomcat7-7.0.21/debian/patches/CVE-2011-3375.patch 1970-01-01 07:00:00.000000000 +0700
+++ tomcat7-7.0.21/debian/patches/CVE-2011-3375.patch 2013-02-08 13:20:32.000000000 +0700
@@ -0,0 +1,160 @@
+Description: Ensure access log always logs the correct remote IP.
+Ensure requests with multiple errors do not result in multiple access log entries.
+Origin: upstream
+Author: ckuerste@gmx.ch
+Bug: http://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.22
+Ubuntu-Bug: https://bugs.launchpad.net/ubuntu/+source/tomcat7/+bug/1115053
+--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
++++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
+@@ -466,10 +466,8 @@
+
+ Request request = (Request) req.getNote(ADAPTER_NOTES);
+ Response response = (Response) res.getNote(ADAPTER_NOTES);
+- boolean create = false;
+
+ if (request == null) {
+- create = true;
+ // Create objects
+ request = connector.createRequest();
+ request.setCoyoteRequest(req);
+@@ -511,9 +509,7 @@
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ log.warn(sm.getString("coyoteAdapter.accesslogFail"), t);
+- }
+-
+- if (create) {
++ } finally {
+ request.recycle();
+ response.recycle();
+ }
+--- a/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
++++ b/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
+@@ -759,7 +759,6 @@
+ secret = true;
+ if (!tmpMB.equals(requiredSecret)) {
+ response.setStatus(403);
+- adapter.log(request, response, 0);
+ error = true;
+ }
+ }
+@@ -776,7 +775,6 @@
+ // Check if secret was submitted if required
+ if ((requiredSecret != null) && !secret) {
+ response.setStatus(403);
+- adapter.log(request, response, 0);
+ error = true;
+ }
+
+@@ -810,6 +808,9 @@
+ MessageBytes valueMB = request.getMimeHeaders().getValue("host");
+ parseHost(valueMB);
+
++ if (error) {
++ adapter.log(request, response, 0);
++ }
+ }
+
+
+@@ -825,7 +826,6 @@
+ request.serverName().duplicate(request.localName());
+ } catch (IOException e) {
+ response.setStatus(400);
+- adapter.log(request, response, 0);
+ error = true;
+ }
+ return;
+@@ -877,7 +877,6 @@
+ error = true;
+ // 400 - Bad request
+ response.setStatus(400);
+- adapter.log(request, response, 0);
+ break;
+ }
+ port = port + (charValue * mult);
+--- a/java/org/apache/coyote/ajp/AjpNioProcessor.java
++++ b/java/org/apache/coyote/ajp/AjpNioProcessor.java
+@@ -163,7 +163,7 @@
+ }
+ }
+
+- if (endpoint.isPaused()) {
++ if (!error && endpoint.isPaused()) {
+ // 503 - Service unavailable
+ response.setStatus(503);
+ adapter.log(request, response, 0);
+--- a/java/org/apache/coyote/ajp/AjpProcessor.java
++++ b/java/org/apache/coyote/ajp/AjpProcessor.java
+@@ -179,7 +179,7 @@
+ }
+ }
+
+- if (endpoint.isPaused()) {
++ if (!error && endpoint.isPaused()) {
+ // 503 - Service unavailable
+ response.setStatus(503);
+ adapter.log(request, response, 0);
+--- a/java/org/apache/coyote/http11/AbstractHttp11Processor.java
++++ b/java/org/apache/coyote/http11/AbstractHttp11Processor.java
+@@ -823,7 +823,6 @@
+ " Unsupported HTTP version \""+protocolMB+"\"");
+ }
+ response.setStatus(505);
+- adapter.log(request, response, 0);
+ }
+
+ MessageBytes methodMB = request.method();
+@@ -919,7 +918,6 @@
+ error = true;
+ // 501 - Unimplemented
+ response.setStatus(501);
+- adapter.log(request, response, 0);
+ }
+ startPos = commaPos + 1;
+ commaPos = transferEncodingValue.indexOf(',', startPos);
+@@ -935,7 +933,6 @@
+ " Unsupported transfer encoding \""+encodingName+"\"");
+ }
+ response.setStatus(501);
+- adapter.log(request, response, 0);
+ }
+ }
+
+@@ -958,7 +955,6 @@
+ " host header missing");
+ }
+ response.setStatus(400);
+- adapter.log(request, response, 0);
+ }
+
+ parseHost(valueMB);
+@@ -988,6 +984,10 @@
+ request.setAttribute("org.apache.tomcat.comet.timeout.support",
+ Boolean.TRUE);
+ }
++
++ if (error) {
++ adapter.log(request, response, 0);
++ }
+ }
+
+
+@@ -1207,7 +1207,6 @@
+ error = true;
+ // 400 - Bad request
+ response.setStatus(400);
+- adapter.log(request, response, 0);
+ break;
+ }
+ port = port + (charValue * mult);
+@@ -1236,8 +1235,9 @@
+ } finally {
+ if (error) {
+ // 500 - Internal Server Error
++ // Can't add a 500 to the access log since that has already been
++ // written in the Adapter.service method.
+ response.setStatus(500);
+- adapter.log(request, response, 0);
+ }
+ }
+
diff -Nru tomcat7-7.0.21/debian/patches/CVE-2011-3376.patch tomcat7-7.0.21/debian/patches/CVE-2011-3376.patch
--- tomcat7-7.0.21/debian/patches/CVE-2011-3376.patch 1970-01-01 07:00:00.000000000 +0700
+++ tomcat7-7.0.21/debian/patches/CVE-2011-3376.patch 2013-02-08 12:27:31.000000000 +0700
@@ -0,0 +1,18 @@
+Description: ContainerServlets are always restricted.
+Origin: upstream
+Author: ckuerste@gmx.ch
+Bug: http://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.22
+Ubuntu-Bug: https://bugs.launchpad.net/ubuntu/+source/tomcat7/+bug/1115053
+--- a/java/org/apache/catalina/core/DefaultInstanceManager.java
++++ b/java/org/apache/catalina/core/DefaultInstanceManager.java
+@@ -420,6 +420,10 @@
+ if (Filter.class.isAssignableFrom(clazz)) {
+ checkAccess(clazz, restrictedFilters);
+ } else if (Servlet.class.isAssignableFrom(clazz)) {
++ if (ContainerServlet.class.isAssignableFrom(clazz)) {
++ throw new SecurityException("Restricted (ContainerServlet) " +
++ clazz);
++ }
+ checkAccess(clazz, restrictedServlets);
+ } else {
+ checkAccess(clazz, restrictedListeners);
diff -Nru tomcat7-7.0.21/debian/patches/CVE-2012-0022.patch tomcat7-7.0.21/debian/patches/CVE-2012-0022.patch
--- tomcat7-7.0.21/debian/patches/CVE-2012-0022.patch 1970-01-01 07:00:00.000000000 +0700
+++ tomcat7-7.0.21/debian/patches/CVE-2012-0022.patch 2013-02-08 12:27:31.000000000 +0700
@@ -0,0 +1,934 @@
+Description: Re-factor parameter parsing to improve performance
+Origin: upstream
+Author: ckuerste@gmx.ch
+Bug: http://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.23
+Ubuntu-Bug: https://bugs.launchpad.net/ubuntu/+source/tomcat7/+bug/1115053
+--- a/java/org/apache/catalina/connector/Connector.java
++++ b/java/org/apache/catalina/connector/Connector.java
+@@ -172,6 +172,13 @@
+
+
+ /**
++ * The maximum number of parameters (GET plus POST) which will be
++ * automatically parsed by the container. 10000 by default. A value of less
++ * than 0 means no limit.
++ */
++ protected int maxParameterCount = 10000;
++
++ /**
+ * Maximum size of a POST which will be automatically parsed by the
+ * container. 2MB by default.
+ */
+@@ -405,12 +412,33 @@
+ */
+ public Mapper getMapper() {
+
+- return (mapper);
++ return (mapper);
+
+ }
+
+
+ /**
++ * Return the maximum number of parameters (GET plus POST) that will be
++ * automatically parsed by the container. A value of less than 0 means no
++ * limit.
++ */
++ public int getMaxParameterCount() {
++ return maxParameterCount;
++ }
++
++
++ /**
++ * Set the maximum number of parameters (GET plus POST) that will be
++ * automatically parsed by the container. A value less than 0 means no
++ * limit.
++ *
++ * @param maxParameterCount The new setting
++ */
++ public void setMaxParameterCount(int maxParameterCount) {
++ this.maxParameterCount = maxParameterCount;
++ }
++
++ /**
+ * Return the maximum size of a POST which will be automatically
+ * parsed by the container.
+ */
+--- a/java/org/apache/catalina/connector/Request.java
++++ b/java/org/apache/catalina/connector/Request.java
+@@ -24,6 +24,7 @@
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.io.UnsupportedEncodingException;
++import java.nio.charset.Charset;
+ import java.security.Principal;
+ import java.text.SimpleDateFormat;
+ import java.util.ArrayList;
+@@ -2656,26 +2657,59 @@
+ parts = new ArrayList();
+ try {
+ List items = upload.parseRequest(this);
++ int maxPostSize = getConnector().getMaxPostSize();
++ int postSize = 0;
++ String enc = getCharacterEncoding();
++ Charset charset = null;
++ if (enc != null) {
++ try {
++ charset = B2CConverter.getCharset(enc);
++ } catch (UnsupportedEncodingException e) {
++ // Ignore
++ }
++ }
+ for (FileItem item : items) {
+ ApplicationPart part = new ApplicationPart(item, mce);
+ parts.add(part);
+ if (part.getFilename() == null) {
++ String name = part.getName();
++ String value = null;
+ try {
+ String encoding = parameters.getEncoding();
+ if (encoding == null) {
+ encoding = Parameters.DEFAULT_ENCODING;
+ }
+- parameters.addParameterValues(part.getName(),
+- new String[] { part.getString(encoding) });
++ value = part.getString(encoding);
+ } catch (UnsupportedEncodingException uee) {
+ try {
+- parameters.addParameterValues(part.getName(),
+- new String[] {part.getString(
+- Parameters.DEFAULT_ENCODING)});
++ value = part.getString(Parameters.DEFAULT_ENCODING);
+ } catch (UnsupportedEncodingException e) {
+ // Should not be possible
+ }
+ }
++ if (maxPostSize > 0) {
++ // Have to calculate equivalent size. Not completely
++ // accurate but close enough.
++ if (charset == null) {
++ // Name length
++ postSize += name.getBytes().length;
++ } else {
++ postSize += name.getBytes(charset).length;
++ }
++ if (value != null) {
++ // Equals sign
++ postSize++;
++ // Value length
++ postSize += part.getSize();
++ }
++ // Value separator
++ postSize++;
++ if (postSize > maxPostSize) {
++ throw new IllegalStateException(sm.getString(
++ "coyoteRequest.maxPostSizeExceeded"));
++ }
++ }
++ parameters.addParameter(name, value);
+ }
+ }
+
+@@ -2853,6 +2887,8 @@
+ parametersParsed = true;
+
+ Parameters parameters = coyoteRequest.getParameters();
++ // Set this every time in case limit has been changed via JMX
++ parameters.setLimit(getConnector().getMaxParameterCount());
+
+ // getCharacterEncoding() may have been overridden to search for
+ // hidden form field containing request encoding
+--- a/java/org/apache/catalina/connector/mbeans-descriptors.xml
++++ b/java/org/apache/catalina/connector/mbeans-descriptors.xml
+@@ -81,6 +81,10 @@
+ description="Maximum number of Keep-Alive requests to honor per connection"
+ type="int"/>
+
++
++
+
+--- a/java/org/apache/tomcat/util/buf/ByteChunk.java
++++ b/java/org/apache/tomcat/util/buf/ByteChunk.java
+@@ -19,6 +19,10 @@
+
+ import java.io.IOException;
+ import java.io.Serializable;
++import java.io.UnsupportedEncodingException;
++import java.nio.ByteBuffer;
++import java.nio.CharBuffer;
++import java.nio.charset.Charset;
+
+ /*
+ * In a server it is very important to be able to operate on
+@@ -97,15 +101,25 @@
+ as most standards seem to converge, but the servlet API requires
+ 8859_1, and this object is used mostly for servlets.
+ */
+- public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
+-
++ public static final Charset DEFAULT_CHARSET;
++
++ static {
++ Charset c = null;
++ try {
++ c = B2CConverter.getCharset("ISO-8859-1");
++ } catch (UnsupportedEncodingException e) {
++ // Should never happen since all JVMs must support ISO-8859-1
++ }
++ DEFAULT_CHARSET = c;
++ }
++
+ // byte[]
+ private byte[] buff;
+
+ private int start=0;
+ private int end;
+
+- private String enc;
++ private Charset charset;
+
+ private boolean isSet=false; // XXX
+
+@@ -146,7 +160,7 @@
+ */
+ public void recycle() {
+ // buff = null;
+- enc=null;
++ charset=null;
+ start=0;
+ end=0;
+ isSet=false;
+@@ -186,13 +200,15 @@
+ this.optimizedWrite = optimizedWrite;
+ }
+
+- public void setEncoding( String enc ) {
+- this.enc=enc;
++ public void setCharset(Charset charset) {
++ this.charset = charset;
+ }
+- public String getEncoding() {
+- if (enc == null)
+- enc=DEFAULT_CHARACTER_ENCODING;
+- return enc;
++
++ public Charset getCharset() {
++ if (charset == null) {
++ charset = DEFAULT_CHARSET;
++ }
++ return charset;
+ }
+
+ /**
+@@ -497,31 +513,15 @@
+ }
+
+ public String toStringInternal() {
+- String strValue=null;
+- try {
+- if (enc == null) {
+- enc = DEFAULT_CHARACTER_ENCODING;
+- }
+- strValue = new String(buff, start, end-start,
+- B2CConverter.getCharset(enc));
+- /*
+- Does not improve the speed too much on most systems,
+- it's safer to use the "classical" new String().
+-
+- Most overhead is in creating char[] and copying,
+- the internal implementation of new String() is very close to
+- what we do. The decoder is nice for large buffers and if
+- we don't go to String ( so we can take advantage of reduced GC)
+-
+- // Method is commented out, in:
+- return B2CConverter.decodeString( enc );
+- */
+- } catch (java.io.UnsupportedEncodingException e) {
+- // Use the platform encoding in that case; the usage of a bad
+- // encoding will have been logged elsewhere already
+- strValue = new String(buff, start, end-start);
++ if (charset == null) {
++ charset = DEFAULT_CHARSET;
+ }
+- return strValue;
++ // new String(byte[], int, int, Charset) takes a defensive copy of the
++ // entire byte array. This is expensive if only a small subset of the
++ // bytes will be used. The code below is from Apache Harmony.
++ CharBuffer cb;
++ cb = charset.decode(ByteBuffer.wrap(buff, start, end-start));
++ return new String(cb.array(), cb.arrayOffset(), cb.length());
+ }
+
+ public int getInt()
+--- a/java/org/apache/tomcat/util/buf/MessageBytes.java
++++ b/java/org/apache/tomcat/util/buf/MessageBytes.java
+@@ -130,13 +130,13 @@
+ * previous conversion is reset.
+ * If no encoding is set, we'll use 8859-1.
+ */
+- public void setEncoding( String enc ) {
++ public void setCharset(Charset charset) {
+ if( !byteC.isNull() ) {
+ // if the encoding changes we need to reset the conversion results
+ charC.recycle();
+ hasStrValue=false;
+ }
+- byteC.setEncoding(enc);
++ byteC.setCharset(charset);
+ }
+
+ /**
+--- a/java/org/apache/tomcat/util/buf/StringCache.java
++++ b/java/org/apache/tomcat/util/buf/StringCache.java
+@@ -17,6 +17,7 @@
+
+ package org.apache.tomcat.util.buf;
+
++import java.nio.charset.Charset;
+ import java.util.ArrayList;
+ import java.util.HashMap;
+ import java.util.Map.Entry;
+@@ -299,7 +300,7 @@
+ System.arraycopy(bc.getBuffer(), start, entry.name,
+ 0, end - start);
+ // Set encoding
+- entry.enc = bc.getEncoding();
++ entry.charset = bc.getCharset();
+ // Initialize occurrence count to one
+ count = new int[1];
+ count[0] = 1;
+@@ -485,7 +486,7 @@
+ protected static final String find(ByteChunk name) {
+ int pos = findClosest(name, bcCache, bcCache.length);
+ if ((pos < 0) || (compare(name, bcCache[pos].name) != 0)
+- || !(name.getEncoding().equals(bcCache[pos].enc))) {
++ || !(name.getCharset().equals(bcCache[pos].charset))) {
+ return null;
+ } else {
+ return bcCache[pos].value;
+@@ -640,7 +641,7 @@
+ public static class ByteEntry {
+
+ public byte[] name = null;
+- public String enc = null;
++ public Charset charset = null;
+ public String value = null;
+
+ @Override
+--- /dev/null
++++ b/java/org/apache/tomcat/util/http/LocalStrings.properties
+@@ -0,0 +1,23 @@
++# Licensed to the Apache Software Foundation (ASF) under one or more
++# contributor license agreements. See the NOTICE file distributed with
++# this work for additional information regarding copyright ownership.
++# The ASF licenses this file to You under the Apache License, Version 2.0
++# (the "License"); you may not use this file except in compliance with
++# the License. You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
++parameters.bytes=Start processing with input [{0}]
++paramerers.copyFail=Failed to create copy of original parameter values for debug logging purposes
++parameters.decodeFail.debug=Character decoding failed. Parameter [{0}] with value [{1}] has been ignored.
++parameters.decodeFail.info=Character decoding failed. Parameter [{0}] with value [{1}] has been ignored. Note that the name and value quoted here may be corrupted due to the failed decoding. Use debug level logging to see the original, non-corrupted values.
++parameters.invalidChunk=Invalid chunk starting at byte [{0}] and ending at byte [{1}] with a value of [{2}] ignored
++parameters.maxCountFail=More than the maximum number of request parameters (GET plus POST) for a single request ([{0}]) were detected. Any parameters beyond this limit have been ignored. To change this limit, set the maxParameterCount attribute on the Connector.
++parameters.multipleDecodingFail=Character decoding failed. A total of [{0}] failures were detected but only the first was logged. Enable debug level logging for this logger to log all failures.
++parameters.noequal=Parameter starting at position [{0}] and ending at position [{1}] with a value of [{0}] was not followed by an '=' character
+--- a/java/org/apache/tomcat/util/http/Parameters.java
++++ b/java/org/apache/tomcat/util/http/Parameters.java
+@@ -18,14 +18,20 @@
+ package org.apache.tomcat.util.http;
+
+ import java.io.IOException;
++import java.io.UnsupportedEncodingException;
+ import java.nio.charset.Charset;
++import java.util.ArrayList;
++import java.util.Collections;
+ import java.util.Enumeration;
+-import java.util.Hashtable;
++import java.util.HashMap;
++import java.util.Map;
+
++import org.apache.tomcat.util.buf.B2CConverter;
+ import org.apache.tomcat.util.buf.ByteChunk;
+ import org.apache.tomcat.util.buf.CharChunk;
+ import org.apache.tomcat.util.buf.MessageBytes;
+ import org.apache.tomcat.util.buf.UDecoder;
++import org.apache.tomcat.util.res.StringManager;
+
+ /**
+ *
+@@ -34,14 +40,15 @@
+ public final class Parameters {
+
+
+- private static final org.apache.juli.logging.Log log=
++ private static final org.apache.juli.logging.Log log =
+ org.apache.juli.logging.LogFactory.getLog(Parameters.class );
+-
+- // Transition: we'll use the same Hashtable( String->String[] )
+- // for the beginning. When we are sure all accesses happen through
+- // this class - we can switch to MultiMap
+- private Hashtable paramHashStringArray =
+- new Hashtable();
++
++ protected static final StringManager sm =
++ StringManager.getManager("org.apache.tomcat.util.http");
++
++ private final HashMap> paramHashValues =
++ new HashMap>();
++
+ private boolean didQueryParameters=false;
+
+ MessageBytes queryMB;
+@@ -51,7 +58,10 @@
+
+ String encoding=null;
+ String queryStringEncoding=null;
+-
++
++ private int limit = -1;
++ private int parameterCount = 0;
++
+ public Parameters() {
+ // NO-OP
+ }
+@@ -60,6 +70,10 @@
+ this.queryMB=queryMB;
+ }
+
++ public void setLimit(int limit) {
++ this.limit = limit;
++ }
++
+ public String getEncoding() {
+ return encoding;
+ }
+@@ -79,7 +93,8 @@
+ }
+
+ public void recycle() {
+- paramHashStringArray.clear();
++ parameterCount = 0;
++ paramHashValues.clear();
+ didQueryParameters=false;
+ encoding=null;
+ decodedQuery.recycle();
+@@ -88,46 +103,47 @@
+ // -------------------- Data access --------------------
+ // Access to the current name/values, no side effect ( processing ).
+ // You must explicitly call handleQueryParameters and the post methods.
+-
+- // This is the original data representation ( hash of String->String[])
+
+- public void addParameterValues( String key, String[] newValues) {
+- if ( key==null ) return;
+- String values[];
+- if (paramHashStringArray.containsKey(key)) {
+- String oldValues[] = paramHashStringArray.get(key);
+- values = new String[oldValues.length + newValues.length];
+- for (int i = 0; i < oldValues.length; i++) {
+- values[i] = oldValues[i];
+- }
+- for (int i = 0; i < newValues.length; i++) {
+- values[i+ oldValues.length] = newValues[i];
+- }
++ @Deprecated
++ public void addParameterValues(String key, String[] newValues) {
++ if (key == null) {
++ return;
++ }
++ ArrayList values = paramHashValues.get(key);
++ if (values == null) {
++ values = new ArrayList(newValues.length);
++ paramHashValues.put(key, values);
+ } else {
+- values = newValues;
++ values.ensureCapacity(values.size() + newValues.length);
++ }
++ for (String newValue : newValues) {
++ values.add(newValue);
+ }
+-
+- paramHashStringArray.put(key, values);
+ }
+
+ public String[] getParameterValues(String name) {
+ handleQueryParameters();
+ // no "facade"
+- String values[] = paramHashStringArray.get(name);
+- return values;
++ ArrayList values = paramHashValues.get(name);
++ if (values == null) {
++ return null;
++ }
++ return values.toArray(new String[values.size()]);
+ }
+
+ public Enumeration getParameterNames() {
+ handleQueryParameters();
+- return paramHashStringArray.keys();
++ return Collections.enumeration(paramHashValues.keySet());
+ }
+
+- // Shortcut.
+ public String getParameter(String name ) {
+- String[] values = getParameterValues(name);
++ handleQueryParameters();
++ ArrayList values = paramHashValues.get(name);
+ if (values != null) {
+- if( values.length==0 ) return "";
+- return values[0];
++ if (values.size() == 0) {
++ return "";
++ }
++ return values.get(0);
+ } else {
+ return null;
+ }
+@@ -157,25 +173,26 @@
+ processParameters( decodedQuery, queryStringEncoding );
+ }
+
+- // incredibly inefficient data representation for parameters,
+- // until we test the new one
+- private void addParam( String key, String value ) {
++
++ public void addParameter( String key, String value )
++ throws IllegalStateException {
++
+ if( key==null ) return;
+- String values[];
+- if (paramHashStringArray.containsKey(key)) {
+- String oldValues[] = paramHashStringArray.get(key);
+- values = new String[oldValues.length + 1];
+- for (int i = 0; i < oldValues.length; i++) {
+- values[i] = oldValues[i];
+- }
+- values[oldValues.length] = value;
+- } else {
+- values = new String[1];
+- values[0] = value;
++
++ parameterCount ++;
++ if (limit > -1 && parameterCount > limit) {
++ // Processing this parameter will push us over the limit. ISE is
++ // what Request.parseParts() uses for requests that are too big
++ throw new IllegalStateException(sm.getString(
++ "parameters.maxCountFail", Integer.valueOf(limit)));
+ }
+-
+-
+- paramHashStringArray.put(key, values);
++
++ ArrayList values = paramHashValues.get(key);
++ if (values == null) {
++ values = new ArrayList(1);
++ paramHashValues.put(key, values);
++ }
++ values.add(value);
+ }
+
+ public void setURLDecoder( UDecoder u ) {
+@@ -191,109 +208,162 @@
+ private ByteChunk origValue=new ByteChunk();
+ CharChunk tmpNameC=new CharChunk(1024);
+ public static final String DEFAULT_ENCODING = "ISO-8859-1";
+- public static final Charset DEFAULT_CHARSET =
++ private static final Charset DEFAULT_CHARSET =
+ Charset.forName(DEFAULT_ENCODING);
+
+
+ public void processParameters( byte bytes[], int start, int len ) {
+- processParameters(bytes, start, len, encoding);
++ processParameters(bytes, start, len, getCharset(encoding));
+ }
+
+- public void processParameters( byte bytes[], int start, int len,
+- String enc ) {
+- int end=start+len;
+- int pos=start;
+-
++ private void processParameters(byte bytes[], int start, int len,
++ Charset charset) {
++
+ if(log.isDebugEnabled()) {
+- log.debug("Bytes: " +
+- new String(bytes, start, len, DEFAULT_CHARSET));
++ log.debug(sm.getString("parameters.bytes",
++ new String(bytes, start, len, DEFAULT_CHARSET)));
+ }
+
+- do {
+- boolean noEq=false;
+- int valStart=-1;
+- int valEnd=-1;
+-
+- int nameStart=pos;
+- int nameEnd=ByteChunk.indexOf(bytes, nameStart, end, '=' );
+- // Workaround for a&b&c encoding
+- int nameEnd2=ByteChunk.indexOf(bytes, nameStart, end, '&' );
+- if( (nameEnd2!=-1 ) &&
+- ( nameEnd==-1 || nameEnd > nameEnd2) ) {
+- nameEnd=nameEnd2;
+- noEq=true;
+- valStart=nameEnd;
+- valEnd=nameEnd;
+- if(log.isDebugEnabled()) {
+- log.debug("no equal " + nameStart + " " + nameEnd + " " +
+- new String(bytes, nameStart, nameEnd-nameStart,
+- DEFAULT_CHARSET));
++ int decodeFailCount = 0;
++ int pos = start;
++ int end = start + len;
++
++ while(pos < end) {
++ int nameStart = pos;
++ int nameEnd = -1;
++ int valueStart = -1;
++ int valueEnd = -1;
++
++ boolean parsingName = true;
++ boolean decodeName = false;
++ boolean decodeValue = false;
++ boolean parameterComplete = false;
++
++ do {
++ switch(bytes[pos]) {
++ case '=':
++ if (parsingName) {
++ // Name finished. Value starts from next character
++ nameEnd = pos;
++ parsingName = false;
++ valueStart = ++pos;
++ } else {
++ // Equals character in value
++ pos++;
++ }
++ break;
++ case '&':
++ if (parsingName) {
++ // Name finished. No value.
++ nameEnd = pos;
++ } else {
++ // Value finished
++ valueEnd = pos;
++ }
++ parameterComplete = true;
++ pos++;
++ break;
++ case '%':
++ // Decoding required
++ if (parsingName) {
++ decodeName = true;
++ } else {
++ decodeValue = true;
++ }
++ pos++;
++ break;
++ default:
++ pos++;
++ break;
+ }
+- }
+- if( nameEnd== -1 )
+- nameEnd=end;
++ } while (!parameterComplete && pos < end);
+
+- if( ! noEq ) {
+- valStart= (nameEnd < end) ? nameEnd+1 : end;
+- valEnd=ByteChunk.indexOf(bytes, valStart, end, '&');
+- if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
++ if (pos == end) {
++ if (nameEnd == -1) {
++ nameEnd = pos;
++ } else if (valueStart > -1 && valueEnd == -1){
++ valueEnd = pos;
++ }
+ }
+
+- pos=valEnd+1;
+-
+- if( nameEnd<=nameStart ) {
++ if (log.isDebugEnabled() && valueStart == -1) {
++ log.debug(sm.getString("parameters.noequal",
++ Integer.valueOf(nameStart), Integer.valueOf(nameEnd),
++ new String(bytes, nameStart, nameEnd-nameStart,
++ DEFAULT_CHARSET)));
++ }
++
++ if (nameEnd <= nameStart) {
+ if (log.isInfoEnabled()) {
+- StringBuilder msg = new StringBuilder("Parameters: Invalid chunk ");
+- // No name eg ...&=xx&... will trigger this
+- if (valEnd >= nameStart) {
+- msg.append('\'');
+- msg.append(new String(bytes, nameStart,
+- valEnd - nameStart, DEFAULT_CHARSET));
+- msg.append("' ");
++ String extract;
++ if (valueEnd >= nameStart) {
++ extract = new String(bytes, nameStart,
++ valueEnd - nameStart, DEFAULT_CHARSET);
++ log.info(sm.getString("parameters.invalidChunk",
++ Integer.valueOf(nameStart),
++ Integer.valueOf(valueEnd),
++ extract));
++ } else {
++ log.info(sm.getString("parameters.invalidChunk",
++ Integer.valueOf(nameStart),
++ Integer.valueOf(nameEnd),
++ null));
+ }
+- msg.append("ignored.");
+- log.info(msg);
+ }
+ continue;
+ // invalid chunk - it's better to ignore
+ }
+- tmpName.setBytes( bytes, nameStart, nameEnd-nameStart );
+- tmpValue.setBytes( bytes, valStart, valEnd-valStart );
+-
++
++ tmpName.setBytes(bytes, nameStart, nameEnd - nameStart);
++ tmpValue.setBytes(bytes, valueStart, valueEnd - valueStart);
++
+ // Take copies as if anything goes wrong originals will be
+ // corrupted. This means original values can be logged.
+ // For performance - only done for debug
+ if (log.isDebugEnabled()) {
+ try {
+- origName.append(bytes, nameStart, nameEnd-nameStart);
+- origValue.append(bytes, valStart, valEnd-valStart);
++ origName.append(bytes, nameStart, nameEnd - nameStart);
++ origValue.append(bytes, valueStart, valueEnd - valueStart);
+ } catch (IOException ioe) {
+ // Should never happen...
+- log.error("Error copying parameters", ioe);
++ log.error(sm.getString("paramerers.copyFail"), ioe);
+ }
+ }
+
+ try {
+- addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) );
++ String name;
++ String value;
++
++ if (decodeName) {
++ urlDecode(tmpName);
++ }
++ tmpName.setCharset(charset);
++ name = tmpName.toString();
++
++ if (decodeValue) {
++ urlDecode(tmpValue);
++ }
++ tmpValue.setCharset(charset);
++ value = tmpValue.toString();
++
++ try {
++ addParameter(name, value);
++ } catch (IllegalStateException ise) {
++ // Hitting limit stops processing further params but does
++ // not cause request to fail.
++ log.warn(ise.getMessage());
++ break;
++ }
+ } catch (IOException e) {
+- StringBuilder msg =
+- new StringBuilder("Parameters: Character decoding failed.");
+- msg.append(" Parameter '");
+- if (log.isDebugEnabled()) {
+- msg.append(origName.toString());
+- msg.append("' with value '");
+- msg.append(origValue.toString());
+- msg.append("' has been ignored.");
+- log.debug(msg, e);
+- } else if (log.isInfoEnabled()) {
+- msg.append(tmpName.toString());
+- msg.append("' with value '");
+- msg.append(tmpValue.toString());
+- msg.append("' has been ignored. Note that the name and ");
+- msg.append("value quoted here may be corrupted due to ");
+- msg.append("the failed decoding. Use debug level logging ");
+- msg.append("to see the original, non-corrupted values.");
+- log.info(msg);
++ decodeFailCount++;
++ if (decodeFailCount == 1 || log.isDebugEnabled()) {
++ if (log.isDebugEnabled()) {
++ log.debug(sm.getString("parameters.decodeFail.debug",
++ origName.toString(), origValue.toString()), e);
++ } else if (log.isInfoEnabled()) {
++ log.info(sm.getString("parameters.decodeFail.info",
++ tmpName.toString(), tmpValue.toString()), e);
++ }
+ }
+ }
+
+@@ -304,35 +374,20 @@
+ origName.recycle();
+ origValue.recycle();
+ }
+- } while( pos 1 && !log.isDebugEnabled()) {
++ log.info(sm.getString("parameters.multipleDecodingFail",
++ Integer.valueOf(decodeFailCount)));
++ }
+ }
+
+- private String urlDecode(ByteChunk bc, String enc)
++ private void urlDecode(ByteChunk bc)
+ throws IOException {
+ if( urlDec==null ) {
+ urlDec=new UDecoder();
+ }
+ urlDec.convert(bc);
+- String result = null;
+- if (enc != null) {
+- bc.setEncoding(enc);
+- result = bc.toString();
+- } else {
+- CharChunk cc = tmpNameC;
+- int length = bc.getLength();
+- cc.allocate(length, -1);
+- // Default encoding: fast conversion
+- byte[] bbuf = bc.getBuffer();
+- char[] cbuf = cc.getBuffer();
+- int start = bc.getStart();
+- for (int i = 0; i < length; i++) {
+- cbuf[i] = (char) (bbuf[i + start] & 0xff);
+- }
+- cc.setChars(cbuf, 0, length);
+- result = cc.toString();
+- cc.recycle();
+- }
+- return result;
+ }
+
+ public void processParameters( MessageBytes data, String encoding ) {
+@@ -343,23 +398,33 @@
+ }
+ ByteChunk bc=data.getByteChunk();
+ processParameters( bc.getBytes(), bc.getOffset(),
+- bc.getLength(), encoding);
++ bc.getLength(), getCharset(encoding));
+ }
+
+- /** Debug purpose
++ private Charset getCharset(String encoding) {
++ if (encoding == null) {
++ return DEFAULT_CHARSET;
++ }
++ try {
++ return B2CConverter.getCharset(encoding);
++ } catch (UnsupportedEncodingException e) {
++ return DEFAULT_CHARSET;
++ }
++ }
++
++ /**
++ * Debug purpose
+ */
+ public String paramsAsString() {
+- StringBuilder sb=new StringBuilder();
+- Enumeration en= paramHashStringArray.keys();
+- while( en.hasMoreElements() ) {
+- String k = en.nextElement();
+- sb.append( k ).append("=");
+- String v[] = paramHashStringArray.get( k );
+- for( int i=0; i> e : paramHashValues.entrySet()) {
++ sb.append(e.getKey()).append('=');
++ ArrayList values = e.getValue();
++ for (String value : values) {
++ sb.append(value).append(',');
++ }
++ sb.append('\n');
+ }
+ return sb.toString();
+ }
+-
+ }
+--- a/webapps/docs/config/ajp.xml
++++ b/webapps/docs/config/ajp.xml
+@@ -95,6 +95,12 @@
+ By default, DNS lookups are disabled.
+
+
++
++ The maximum number of parameters (GET plus POST) which will be
++ automatically parsed by the container. A value of less than 0 means no
++ limit. If not specified, a default of 10000 is used.
++
++
+
+ The maximum size in bytes of the POST which will be handled by
+ the container FORM URL parameter parsing. The limit can be disabled by
+--- a/webapps/docs/config/http.xml
++++ b/webapps/docs/config/http.xml
+@@ -93,6 +93,12 @@
+ By default, DNS lookups are disabled.
+
+
++
++ The maximum number of parameters (GET plus POST) which will be
++ automatically parsed by the container. A value of less than 0 means no
++ limit. If not specified, a default of 10000 is used.
++
++
+
+ The maximum size in bytes of the POST which will be handled by
+ the container FORM URL parameter parsing. The limit can be disabled by
+--- a/java/org/apache/tomcat/util/buf/B2CConverter.java
++++ b/java/org/apache/tomcat/util/buf/B2CConverter.java
+@@ -106,14 +106,22 @@
+ static final int BUFFER_SIZE=8192;
+ char result[]=new char[BUFFER_SIZE];
+
+- public void convert( ByteChunk bb, CharChunk cb, int limit)
++ /**
++ * Convert a buffer of bytes into a chars.
++ *
++ * @param bb Input byte buffer
++ * @param cb Output char buffer
++ * @param limit Number of bytes to convert
++ * @throws IOException
++ */
++ public void convert( ByteChunk bb, CharChunk cb, int limit)
+ throws IOException
+ {
+ iis.setByteChunk( bb );
+ try {
+ // read from the reader
+ int bbLengthBeforeRead = 0;
+- while( limit > 0 ) { // conv.ready() ) {
++ while( limit > 0 ) {
+ int size = limit < BUFFER_SIZE ? limit : BUFFER_SIZE;
+ bbLengthBeforeRead = bb.getLength();
+ int cnt=conv.read( result, 0, size );
+--- a/java/org/apache/catalina/connector/LocalStrings.properties
++++ b/java/org/apache/catalina/connector/LocalStrings.properties
+@@ -69,6 +69,7 @@
+ coyoteRequest.uploadLocationInvalid=The temporary upload location [{0}] is not valid
+ coyoteRequest.sessionEndAccessFail=Exception triggered ending access to session while recycling request
+ coyoteRequest.sendfileNotCanonical=Unable to determine canonical name of file [{0}] specified for use with sendfile
++coyoteRequest.maxPostSizeExceeded=The multi-part request contained parameter data (excluding uploaded files) that exceeded the limit for maxPostSize set on the associated connector
+
+ requestFacade.nullRequest=The request object has been recycled and is no longer associated with this facade
+
diff -Nru tomcat7-7.0.21/debian/patches/CVE-2012-2733.patch tomcat7-7.0.21/debian/patches/CVE-2012-2733.patch
--- tomcat7-7.0.21/debian/patches/CVE-2012-2733.patch 1970-01-01 07:00:00.000000000 +0700
+++ tomcat7-7.0.21/debian/patches/CVE-2012-2733.patch 2013-02-08 12:27:31.000000000 +0700
@@ -0,0 +1,36 @@
+Description: Improve InternalNioInputBuffer#parseHeaders()
+Origin: upstream
+Author: ckuerste@gmx.ch
+Bug: http://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.28
+Ubuntu-Bug: https://bugs.launchpad.net/ubuntu/+source/tomcat7/+bug/1115053
+--- a/java/org/apache/coyote/http11/InternalNioInputBuffer.java
++++ b/java/org/apache/coyote/http11/InternalNioInputBuffer.java
+@@ -433,10 +433,6 @@
+
+ do {
+ status = parseHeader();
+- } while ( status == HeaderParseStatus.HAVE_MORE_HEADERS );
+- if (status == HeaderParseStatus.DONE) {
+- parsingHeader = false;
+- end = pos;
+ // Checking that
+ // (1) Headers plus request line size does not exceed its limit
+ // (2) There are enough bytes to avoid expanding the buffer when
+@@ -445,11 +441,15 @@
+ // limitation to enforce the meaning of headerBufferSize
+ // From the way how buf is allocated and how blank lines are being
+ // read, it should be enough to check (1) only.
+- if (end - skipBlankLinesBytes > headerBufferSize
+- || buf.length - end < socketReadBufferSize) {
++ if (pos - skipBlankLinesBytes > headerBufferSize
++ || buf.length - pos < socketReadBufferSize) {
+ throw new IllegalArgumentException(
+ sm.getString("iib.requestheadertoolarge.error"));
+ }
++ } while ( status == HeaderParseStatus.HAVE_MORE_HEADERS );
++ if (status == HeaderParseStatus.DONE) {
++ parsingHeader = false;
++ end = pos;
+ return true;
+ } else {
+ return false;
diff -Nru tomcat7-7.0.21/debian/patches/CVE-2012-3439.patch tomcat7-7.0.21/debian/patches/CVE-2012-3439.patch
--- tomcat7-7.0.21/debian/patches/CVE-2012-3439.patch 1970-01-01 07:00:00.000000000 +0700
+++ tomcat7-7.0.21/debian/patches/CVE-2012-3439.patch 2013-02-08 12:27:31.000000000 +0700
@@ -0,0 +1,396 @@
+Description: Digest improvements: disable caching of authenticated user in session by default,
+track server rather than client nonces, better handling of stale nonce values
+Origin: upstream
+Author: ckuerste@gmx.ch
+Bug: http://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.30
+Ubuntu-Bug: https://bugs.launchpad.net/ubuntu/+source/tomcat7/+bug/1115053
+--- a/java/org/apache/catalina/authenticator/DigestAuthenticator.java
++++ b/java/org/apache/catalina/authenticator/DigestAuthenticator.java
+@@ -20,7 +20,6 @@
+
+
+ import java.io.IOException;
+-import java.nio.charset.Charset;
+ import java.security.MessageDigest;
+ import java.security.NoSuchAlgorithmException;
+ import java.security.Principal;
+@@ -38,6 +37,7 @@
+ import org.apache.catalina.util.MD5Encoder;
+ import org.apache.juli.logging.Log;
+ import org.apache.juli.logging.LogFactory;
++import org.apache.tomcat.util.buf.B2CConverter;
+
+
+
+@@ -80,6 +80,7 @@
+
+ public DigestAuthenticator() {
+ super();
++ setCache(false);
+ try {
+ if (md5Helper == null)
+ md5Helper = MessageDigest.getInstance("MD5");
+@@ -100,16 +101,16 @@
+
+
+ /**
+- * List of client nonce values currently being tracked
++ * List of server nonce values currently being tracked
+ */
+- protected Map cnonces;
++ protected Map nonces;
+
+
+ /**
+- * Maximum number of client nonces to keep in the cache. If not specified,
++ * Maximum number of server nonces to keep in the cache. If not specified,
+ * the default value of 1000 is used.
+ */
+- protected int cnonceCacheSize = 1000;
++ protected int nonceCacheSize = 1000;
+
+
+ /**
+@@ -150,13 +151,13 @@
+ }
+
+
+- public int getCnonceCacheSize() {
+- return cnonceCacheSize;
++ public int getNonceCacheSize() {
++ return nonceCacheSize;
+ }
+
+
+- public void setCnonceCacheSize(int cnonceCacheSize) {
+- this.cnonceCacheSize = cnonceCacheSize;
++ public void setNonceCacheSize(int nonceCacheSize) {
++ this.nonceCacheSize = nonceCacheSize;
+ }
+
+
+@@ -263,19 +264,20 @@
+ // Validate any credentials already included with this request
+ String authorization = request.getHeader("authorization");
+ DigestInfo digestInfo = new DigestInfo(getOpaque(), getNonceValidity(),
+- getKey(), cnonces, isValidateUri());
++ getKey(), nonces, isValidateUri());
+ if (authorization != null) {
+- if (digestInfo.validate(request, authorization, config)) {
+- principal = digestInfo.authenticate(context.getRealm());
+- }
++ if (digestInfo.parse(request, authorization)) {
++ if (digestInfo.validate(request, config)) {
++ principal = digestInfo.authenticate(context.getRealm());
++ }
+
+- if (principal != null) {
+- String username = parseUsername(authorization);
+- register(request, response, principal,
+- Constants.DIGEST_METHOD,
+- username, null);
+- return (true);
+- }
++ if (principal != null && !digestInfo.isNonceStale()) {
++ register(request, response, principal,
++ HttpServletRequest.DIGEST_AUTH,
++ digestInfo.getUsername(), null);
++ return true;
++ }
++ }
+ }
+
+ // Send an "unauthorized" response and an appropriate challenge
+@@ -285,11 +287,9 @@
+ String nonce = generateNonce(request);
+
+ setAuthenticateHeader(request, response, config, nonce,
+- digestInfo.isNonceStale());
++ principal != null && digestInfo.isNonceStale());
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+- // hres.flushBuffer();
+- return (false);
+-
++ return false;
+ }
+
+
+@@ -380,10 +380,17 @@
+ byte[] buffer;
+ synchronized (md5Helper) {
+ buffer = md5Helper.digest(
+- ipTimeKey.getBytes(Charset.defaultCharset()));
++ ipTimeKey.getBytes(B2CConverter.ISO_8859_1));
+ }
+
+- return currentTime + ":" + md5Encoder.encode(buffer);
++ String nonce = currentTime + ":" + MD5Encoder.encode(buffer);
++
++ NonceInfo info = new NonceInfo(currentTime, 100);
++ synchronized (nonces) {
++ nonces.put(nonce, info);
++ }
++
++ return nonce;
+ }
+
+
+@@ -457,7 +464,7 @@
+ setOpaque(sessionIdGenerator.generateSessionId());
+ }
+
+- cnonces = new LinkedHashMap() {
++ nonces = new LinkedHashMap() {
+
+ private static final long serialVersionUID = 1L;
+ private static final long LOG_SUPPRESS_TIME = 5 * 60 * 1000;
+@@ -469,7 +476,7 @@
+ Map.Entry eldest) {
+ // This is called from a sync so keep it simple
+ long currentTime = System.currentTimeMillis();
+- if (size() > getCnonceCacheSize()) {
++ if (size() > getNonceCacheSize()) {
+ if (lastLog < currentTime &&
+ currentTime - eldest.getValue().getTimestamp() <
+ getNonceValidity()) {
+@@ -487,10 +494,10 @@
+
+ private static class DigestInfo {
+
+- private String opaque;
+- private long nonceValidity;
+- private String key;
+- private Map cnonces;
++ private final String opaque;
++ private final long nonceValidity;
++ private final String key;
++ private final Map nonces;
+ private boolean validateUri = true;
+
+ private String userName = null;
+@@ -502,21 +509,25 @@
+ private String cnonce = null;
+ private String realmName = null;
+ private String qop = null;
++ private String opaqueReceived = null;
+
+ private boolean nonceStale = false;
+
+
+ public DigestInfo(String opaque, long nonceValidity, String key,
+- Map cnonces, boolean validateUri) {
++ Map nonces, boolean validateUri) {
+ this.opaque = opaque;
+ this.nonceValidity = nonceValidity;
+ this.key = key;
+- this.cnonces = cnonces;
++ this.nonces = nonces;
+ this.validateUri = validateUri;
+ }
+
+- public boolean validate(Request request, String authorization,
+- LoginConfig config) {
++ public String getUsername() {
++ return userName;
++ }
++
++ public boolean parse(Request request, String authorization) {
+ // Validate the authorization credentials format
+ if (authorization == null) {
+ return false;
+@@ -530,7 +541,6 @@
+ String[] tokens = authorization.split(",(?=(?:[^\"]*\"[^\"]*\")+$)");
+
+ method = request.getMethod();
+- String opaque = null;
+
+ for (int i = 0; i < tokens.length; i++) {
+ String currentToken = tokens[i];
+@@ -562,9 +572,13 @@
+ if ("response".equals(currentTokenName))
+ response = removeQuotes(currentTokenValue);
+ if ("opaque".equals(currentTokenName))
+- opaque = removeQuotes(currentTokenValue);
++ opaqueReceived = removeQuotes(currentTokenValue);
+ }
+
++ return true;
++ }
++
++ public boolean validate(Request request, LoginConfig config) {
+ if ( (userName == null) || (realmName == null) || (nonce == null)
+ || (uri == null) || (response == null) ) {
+ return false;
+@@ -613,14 +627,16 @@
+ long currentTime = System.currentTimeMillis();
+ if ((currentTime - nonceTime) > nonceValidity) {
+ nonceStale = true;
+- return false;
++ synchronized (nonces) {
++ nonces.remove(nonce);
++ }
+ }
+ String serverIpTimeKey =
+ request.getRemoteAddr() + ":" + nonceTime + ":" + key;
+ byte[] buffer = null;
+ synchronized (md5Helper) {
+ buffer = md5Helper.digest(
+- serverIpTimeKey.getBytes(Charset.defaultCharset()));
++ serverIpTimeKey.getBytes(B2CConverter.ISO_8859_1));
+ }
+ String md5ServerIpTimeKey = md5Encoder.encode(buffer);
+ if (!md5ServerIpTimeKey.equals(md5clientIpTimeKey)) {
+@@ -633,7 +649,7 @@
+ }
+
+ // Validate cnonce and nc
+- // Check if presence of nc and nonce is consistent with presence of qop
++ // Check if presence of nc and Cnonce is consistent with presence of qop
+ if (qop == null) {
+ if (cnonce != null || nc != null) {
+ return false;
+@@ -652,21 +668,18 @@
+ return false;
+ }
+ NonceInfo info;
+- synchronized (cnonces) {
+- info = cnonces.get(cnonce);
++ synchronized (nonces) {
++ info = nonces.get(nonce);
+ }
+ if (info == null) {
+- info = new NonceInfo();
++ // Nonce is valid but not in cache. It must have dropped out
++ // of the cache - force a re-authentication
++ nonceStale = true;
+ } else {
+- if (count <= info.getCount()) {
++ if (!info.nonceCountValid(count)) {
+ return false;
+ }
+ }
+- info.setCount(count);
+- info.setTimestamp(currentTime);
+- synchronized (cnonces) {
+- cnonces.put(cnonce, info);
+- }
+ }
+ return true;
+ }
+@@ -682,7 +695,7 @@
+
+ byte[] buffer;
+ synchronized (md5Helper) {
+- buffer = md5Helper.digest(a2.getBytes(Charset.defaultCharset()));
++ buffer = md5Helper.digest(a2.getBytes(B2CConverter.ISO_8859_1));
+ }
+ String md5a2 = md5Encoder.encode(buffer);
+
+@@ -693,19 +706,31 @@
+ }
+
+ private static class NonceInfo {
+- private volatile long count;
+ private volatile long timestamp;
+-
+- public void setCount(long l) {
+- count = l;
+- }
+-
+- public long getCount() {
+- return count;
++ private volatile boolean seen[];
++ private volatile int offset;
++ private volatile int count = 0;
++
++ public NonceInfo(long currentTime, int seenWindowSize) {
++ this.timestamp = currentTime;
++ seen = new boolean[seenWindowSize];
++ offset = seenWindowSize / 2;
+ }
+-
+- public void setTimestamp(long l) {
+- timestamp = l;
++
++ public synchronized boolean nonceCountValid(long nonceCount) {
++ if ((count - offset) >= nonceCount ||
++ (nonceCount > count - offset + seen.length)) {
++ return false;
++ }
++ int checkIndex = (int) ((nonceCount + offset) % seen.length);
++ if (seen[checkIndex]) {
++ return false;
++ } else {
++ seen[checkIndex] = true;
++ seen[count % seen.length] = false;
++ count++;
++ return true;
++ }
+ }
+
+ public long getTimestamp() {
+--- a/webapps/docs/config/valve.xml
++++ b/webapps/docs/config/valve.xml
+@@ -805,12 +805,6 @@
+ org.apache.catalina.authenticator.DigestAuthenticator.
+
+
+-
+- 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.
+-
+-
+
+ Controls the caching of pages that are protected by security
+ constraints. Setting this to false
may help work around
+@@ -828,6 +822,12 @@
+ and/or across a cluster.
+
+
++
++ To protect against replay attacks, the DIGEST authenticator tracks
++ server nonce and nonce count values. This attribute controls the size
++ of that cache. If not specified, the default value of 1000 is used.
++
++
+
+ The time, in milliseconds, that a server generated nonce will be
+ considered valid for use in authentication. If not specified, the
+--- a/java/org/apache/tomcat/util/buf/B2CConverter.java
++++ b/java/org/apache/tomcat/util/buf/B2CConverter.java
+@@ -52,6 +52,8 @@
+ private static final Map encodingToCharsetCache =
+ new HashMap();
+
++ public static final Charset ISO_8859_1;
++
+ static {
+ for (Charset charset: Charset.availableCharsets().values()) {
+ encodingToCharsetCache.put(
+@@ -61,6 +63,14 @@
+ alias.toLowerCase(Locale.US), charset);
+ }
+ }
++ Charset iso88591 = null;
++ try {
++ iso88591 = getCharset("ISO-8859-1");
++ } catch (UnsupportedEncodingException e) {
++ // Impossible. All JVMs must support these.
++ e.printStackTrace();
++ }
++ ISO_8859_1 = iso88591;
+ }
+
+ public static Charset getCharset(String enc)
+--- a/java/org/apache/catalina/util/MD5Encoder.java
++++ b/java/org/apache/catalina/util/MD5Encoder.java
+@@ -50,7 +50,7 @@
+ * @param binaryData Array containing the digest
+ * @return Encoded MD5, or null if encoding failed
+ */
+- public String encode( byte[] binaryData ) {
++ public static String encode( byte[] binaryData ) {
+
+ if (binaryData.length != 16)
+ return null;
diff -Nru tomcat7-7.0.21/debian/patches/CVE-2012-3546.patch tomcat7-7.0.21/debian/patches/CVE-2012-3546.patch
--- tomcat7-7.0.21/debian/patches/CVE-2012-3546.patch 1970-01-01 07:00:00.000000000 +0700
+++ tomcat7-7.0.21/debian/patches/CVE-2012-3546.patch 2013-02-08 12:27:31.000000000 +0700
@@ -0,0 +1,47 @@
+Description: Remove unneeded handling of FORM authentication in RealmBase.
+Origin: upstream
+Author: ckuerste@gmx.ch
+Bug: http://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.30
+Ubuntu-Bug: https://bugs.launchpad.net/ubuntu/+source/tomcat7/+bug/1115053
+--- a/java/org/apache/catalina/realm/RealmBase.java
++++ b/java/org/apache/catalina/realm/RealmBase.java
+@@ -45,7 +45,6 @@
+ import org.apache.catalina.connector.Request;
+ import org.apache.catalina.connector.Response;
+ import org.apache.catalina.core.ApplicationSessionCookieConfig;
+-import org.apache.catalina.deploy.LoginConfig;
+ import org.apache.catalina.deploy.SecurityCollection;
+ import org.apache.catalina.deploy.SecurityConstraint;
+ import org.apache.catalina.mbeans.MBeanUtils;
+@@ -786,31 +785,6 @@
+ if (constraints == null || constraints.length == 0)
+ return (true);
+
+- // Specifically allow access to the form login and form error pages
+- // and the "j_security_check" action
+- LoginConfig config = context.getLoginConfig();
+- if ((config != null) &&
+- (Constants.FORM_METHOD.equals(config.getAuthMethod()))) {
+- String requestURI = request.getRequestPathMB().toString();
+- String loginPage = config.getLoginPage();
+- if (loginPage.equals(requestURI)) {
+- if (log.isDebugEnabled())
+- log.debug(" Allow access to login page " + loginPage);
+- return (true);
+- }
+- String errorPage = config.getErrorPage();
+- if (errorPage.equals(requestURI)) {
+- if (log.isDebugEnabled())
+- log.debug(" Allow access to error page " + errorPage);
+- return (true);
+- }
+- if (requestURI.endsWith(Constants.FORM_ACTION)) {
+- if (log.isDebugEnabled())
+- log.debug(" Allow access to username/password submission");
+- return (true);
+- }
+- }
+-
+ // Which user principal have we already authenticated?
+ Principal principal = request.getPrincipal();
+ boolean status = false;
diff -Nru tomcat7-7.0.21/debian/patches/CVE-2012-4431.patch tomcat7-7.0.21/debian/patches/CVE-2012-4431.patch
--- tomcat7-7.0.21/debian/patches/CVE-2012-4431.patch 1970-01-01 07:00:00.000000000 +0700
+++ tomcat7-7.0.21/debian/patches/CVE-2012-4431.patch 2013-02-08 14:57:48.000000000 +0700
@@ -0,0 +1,51 @@
+Description: Improve session management in CsrfPreventionFilter
+Origin: upstream
+Author: ckuerste@gmx.ch
+Bug: http://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.32
+Ubuntu-Bug: https://bugs.launchpad.net/ubuntu/+source/tomcat7/+bug/1115053
+--- a/java/org/apache/catalina/filters/CsrfPreventionFilter.java
++++ b/java/org/apache/catalina/filters/CsrfPreventionFilter.java
+@@ -34,6 +34,7 @@
+ import javax.servlet.http.HttpServletRequest;
+ import javax.servlet.http.HttpServletResponse;
+ import javax.servlet.http.HttpServletResponseWrapper;
++import javax.servlet.http.HttpSession;
+
+ import org.apache.juli.logging.Log;
+ import org.apache.juli.logging.LogFactory;
+@@ -154,16 +155,19 @@
+ }
+ }
+
++ HttpSession session = req.getSession(false);
++
+ @SuppressWarnings("unchecked")
+- LruCache nonceCache =
+- (LruCache) req.getSession(true).getAttribute(
+- Constants.CSRF_NONCE_SESSION_ATTR_NAME);
++ LruCache nonceCache = (session == null) ? null
++ : (LruCache) session.getAttribute(
++ Constants.CSRF_NONCE_SESSION_ATTR_NAME);
+
+ if (!skipNonceCheck) {
+ String previousNonce =
+ req.getParameter(Constants.CSRF_NONCE_REQUEST_PARAM);
+
+- if (nonceCache != null && !nonceCache.contains(previousNonce)) {
++ if (nonceCache == null || previousNonce == null ||
++ !nonceCache.contains(previousNonce)) {
+ res.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+@@ -171,7 +175,10 @@
+
+ if (nonceCache == null) {
+ nonceCache = new LruCache(nonceCacheSize);
+- req.getSession().setAttribute(
++ if (session == null) {
++ session = req.getSession(true);
++ }
++ session.setAttribute(
+ Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
+ }
+
diff -Nru tomcat7-7.0.21/debian/patches/CVE-2012-4534.patch tomcat7-7.0.21/debian/patches/CVE-2012-4534.patch
--- tomcat7-7.0.21/debian/patches/CVE-2012-4534.patch 1970-01-01 07:00:00.000000000 +0700
+++ tomcat7-7.0.21/debian/patches/CVE-2012-4534.patch 2013-02-08 12:27:31.000000000 +0700
@@ -0,0 +1,61 @@
+Description: Fix for CVE-2012-4534 Denial of Service Vulnerability
+Origin: upstream
+Author: ckuerste@gmx.ch
+Bug: http://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.28
+Ubuntu-Bug: https://bugs.launchpad.net/ubuntu/+source/tomcat7/+bug/1115053
+--- a/java/org/apache/tomcat/util/net/NioEndpoint.java
++++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
+@@ -1215,9 +1215,13 @@
+
+ public boolean processSendfile(SelectionKey sk, KeyAttachment attachment, boolean reg, boolean event) {
+ NioChannel sc = null;
++ if (log.isTraceEnabled()) {
++ log.trace("["+new java.sql.Date(System.currentTimeMillis()).toGMTString()+"] Processing send file. ["+sk+"] ");
++ }
+ try {
+ //unreg(sk,attachment);//only do this if we do process send file on a separate thread
+ SendfileData sd = attachment.getSendfileData();
++ // setup the file channel
+ if ( sd.fchannel == null ) {
+ File f = new File(sd.fileName);
+ if ( !f.exists() ) {
+@@ -1226,10 +1230,14 @@
+ }
+ sd.fchannel = new FileInputStream(f).getChannel();
+ }
++
++ // configure output channel
+ sc = attachment.getChannel();
+ sc.setSendFile(true);
++ // ssl channel is slightly different
+ WritableByteChannel wc = ((sc instanceof SecureNioChannel)?sc:sc.getIOChannel());
+
++ // we still have data in the buffer
+ if (sc.getOutboundRemaining()>0) {
+ if (sc.flushOutbound()) {
+ attachment.access();
+@@ -1256,7 +1264,6 @@
+ attachment.setSendfileData(null);
+ try {sd.fchannel.close();}catch(Exception ignore){}
+ if ( sd.keepAlive ) {
+- if (reg) {
+ if (log.isDebugEnabled()) {
+ log.debug("Connection is keep alive, registering back for OP_READ");
+ }
+@@ -1265,7 +1272,6 @@
+ } else {
+ reg(sk,attachment,SelectionKey.OP_READ);
+ }
+- }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Send file connection is being closed");
+@@ -1273,7 +1279,7 @@
+ cancelledKey(sk,SocketStatus.STOP,false);
+ return false;
+ }
+- } else if ( attachment.interestOps() == 0 && reg ) {
++ } else { //if ( attachment.interestOps() == 0 && reg ) {
+ if (log.isDebugEnabled()) {
+ log.debug("OP_WRITE for sendilfe:"+sd.fileName);
+ }
diff -Nru tomcat7-7.0.21/debian/patches/series tomcat7-7.0.21/debian/patches/series
--- tomcat7-7.0.21/debian/patches/series 2011-09-07 15:47:47.000000000 +0700
+++ tomcat7-7.0.21/debian/patches/series 2013-02-08 15:04:57.000000000 +0700
@@ -9,3 +9,11 @@
0009-Use-java.security.policy-file-in-catalina.sh.patch
0010-debianize-build-xml.patch
0011-fix-classpath-lintian-warnings.patch
+CVE-2012-0022.patch
+CVE-2011-3375.patch
+CVE-2011-3376.patch
+CVE-2012-2733.patch
+CVE-2012-3439.patch
+CVE-2012-3546.patch
+CVE-2012-4431.patch
+CVE-2012-4534.patch