[jetty/f15] Resolves CVE-2011-4461

Jeff Johnston jjohnstn at fedoraproject.org
Thu Jan 19 20:48:52 UTC 2012


commit d1279fa574bd2af61ec18cfdf3c4dcdf3a9cb818
Author: Jeff Johnston <jjohnstn at redhat.com>
Date:   Thu Jan 19 15:47:41 2012 -0500

    Resolves CVE-2011-4461

 jetty-hashDOS.patch |  378 +++++++++++++++++++++++++++++++++++++++++++++++++++
 jetty.spec          |    7 +-
 2 files changed, 384 insertions(+), 1 deletions(-)
---
diff --git a/jetty-hashDOS.patch b/jetty-hashDOS.patch
new file mode 100644
index 0000000..7f423b7
--- /dev/null
+++ b/jetty-hashDOS.patch
@@ -0,0 +1,378 @@
+diff -up ./modules/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandler.java.fix ./modules/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandler.java
+--- ./modules/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandler.java.fix	2012-01-16 13:35:18.000000000 -0500
++++ ./modules/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandler.java	2012-01-16 14:31:48.000000000 -0500
+@@ -118,6 +118,7 @@ public class ContextHandler extends Hand
+     private Logger _logger;
+     private boolean _shutdown;
+     private boolean _allowNullPathInfo;
++    private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys",1000).intValue();
+     private int _maxFormContentSize=Integer.getInteger("org.mortbay.jetty.Request.maxFormContentSize",200000).intValue();
+     private boolean _compactPath=false;
+ 
+@@ -1058,11 +1059,30 @@ public class ContextHandler extends Hand
+     }
+     
+     /* ------------------------------------------------------------ */
++    /**
++     * Set the maximum size of a form post, to protect against DOS attacks from large forms.
++     * @param maxSize
++     */
+     public void setMaxFormContentSize(int maxSize)
+     {
+         _maxFormContentSize=maxSize;
+     }
+ 
++    /* ------------------------------------------------------------ */
++    public int getMaxFormKeys()
++    {
++        return _maxFormKeys;
++    }
++
++    /* ------------------------------------------------------------ */
++    /**
++     * Set the maximum number of form Keys to protect against DOS attack from crafted hash keys.
++     * @param max
++     */
++    public void setMaxFormKeys(int max)
++    {
++        _maxFormKeys = max;
++    }
+ 
+     /* ------------------------------------------------------------ */
+     /**
+diff -up ./modules/jetty/src/main/java/org/mortbay/jetty/Request.java.fix ./modules/jetty/src/main/java/org/mortbay/jetty/Request.java
+--- ./modules/jetty/src/main/java/org/mortbay/jetty/Request.java.fix	2012-01-16 13:24:22.000000000 -0500
++++ ./modules/jetty/src/main/java/org/mortbay/jetty/Request.java	2012-01-16 13:32:38.000000000 -0500
+@@ -98,6 +98,13 @@ import org.mortbay.util.ajax.Continuatio
+  * to avoid reparsing headers and cookies that are likely to be the same for 
+  * requests from the same connection.
+  * 
++ * <p>
++ * The form content that a request can process is limited to protect from Denial of Service 
++ * attacks. The size in bytes is limited by {@link ContextHandler#getMaxFormContentSize()} or if there is no 
++ * context then the "org.eclipse.jetty.server.Request.maxFormContentSize" {@link Server} attribute.  
++ * The number of parameters keys is limited by {@link ContextHandler#getMaxFormKeys()} or if there is no
++ * context then the "org.eclipse.jetty.server.Request.maxFormKeys" {@link Server} attribute. 
++ * 
+  * @author gregw
+  *
+  */
+@@ -1546,16 +1553,23 @@ public class Request implements HttpServ
+                     try
+                     {
+                         int maxFormContentSize=-1;
+-                        
++                        int maxFormKeys=-1;
++
++ 
++
+                         if (_context!=null)
++                        {
+                             maxFormContentSize=_context.getContextHandler().getMaxFormContentSize();
++                            maxFormKeys=_context.getContextHandler().getMaxFormKeys();
++                        }
+                         else
+                         {
+-                            Integer size = (Integer)_connection.getConnector().getServer().getAttribute("org.mortbay.jetty.Request.maxFormContentSize");
+-                            if (size!=null)
+-                                maxFormContentSize =size.intValue();
++                            Number size = (Number)_connection.getConnector().getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormContentSize");
++                            maxFormContentSize=size==null?200000:size.intValue();
++                            Number keys = (Number)_connection.getConnector().getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormKeys");
++                            maxFormKeys =keys==null?1000:keys.intValue();
+                         }
+-                        
++
+                         if (content_length>maxFormContentSize && maxFormContentSize > 0)
+                         {
+                             throw new IllegalStateException("Form too large"+content_length+">"+maxFormContentSize);
+@@ -1563,7 +1577,7 @@ public class Request implements HttpServ
+                         InputStream in = getInputStream();
+                        
+                         // Add form params to query params
+-                        UrlEncoded.decodeTo(in, _baseParameters, encoding,content_length<0?maxFormContentSize:-1);
++                        UrlEncoded.decodeTo(in, _baseParameters, encoding,content_length<0?maxFormContentSize:-1,maxFormKeys);
+                     }
+                     catch (IOException e)
+                     {
+diff -up ./modules/jetty/src/test/java/org/mortbay/jetty/RequestTest.java.fix ./modules/jetty/src/test/java/org/mortbay/jetty/RequestTest.java
+--- ./modules/jetty/src/test/java/org/mortbay/jetty/RequestTest.java.fix	2012-01-16 14:38:35.000000000 -0500
++++ ./modules/jetty/src/test/java/org/mortbay/jetty/RequestTest.java	2012-01-16 14:45:16.000000000 -0500
+@@ -15,10 +15,13 @@
+ 
+ package org.mortbay.jetty;
+ 
+-
++import java.io.BufferedReader;
++import java.io.File;
++import java.io.FileReader;
+ import java.io.IOException;
+ import java.io.Reader;
+ import java.util.ArrayList;
++import java.util.HashMap;
+ 
+ import javax.servlet.ServletException;
+ import javax.servlet.http.Cookie;
+@@ -28,10 +31,14 @@ import javax.servlet.http.HttpServletRes
+ import junit.framework.TestCase;
+ 
+ import org.mortbay.jetty.Request;
++import org.eclipse.jetty.http.MimeTypes;
+ import org.mortbay.jetty.handler.AbstractHandler;
+ import org.mortbay.jetty.handler.HandlerCollection;
+ import org.mortbay.util.IO;
+ import org.mortbay.util.StringUtil;
++import org.eclipse.jetty.util.log.Log;
++
++
+ 
+ /**
+  * @author gregw
+@@ -482,7 +489,52 @@ public class RequestTest extends TestCas
+         assertEquals("value7" ,cookie[7]);
+     }
+     
+-    
++    public void testHashDOS() throws Exception
++    {
++        _server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize",-1);
++        _server.setAttribute("org.eclipse.jetty.server.Request.maxFormKeys",1000);
++        
++        // This file is not distributed - as it is dangerous
++        File evil_keys = new File("/tmp/keys_mapping_to_zero_2m");
++        if (!evil_keys.exists())
++        {
++            Log.info("testHashDOS skipped");
++            return;
++        }
++        
++        BufferedReader in = new BufferedReader(new FileReader(evil_keys));
++        StringBuilder buf = new StringBuilder(4000000);
++        
++        String key=null;
++        buf.append("a=b");
++        while((key=in.readLine())!=null)
++        {
++            buf.append("&").append(key).append("=").append("x");
++        }
++        buf.append("&c=d");
++        
++        _handler._checker = new RequestTester()
++        {
++            public boolean check(HttpServletRequest request,HttpServletResponse response)
++            {
++                return "b".equals(request.getParameter("a")) && request.getParameter("c")==null;
++            }
++        };
++
++        String request="POST / HTTP/1.1\r\n"+
++        "Host: whatever\r\n"+
++        "Content-Type: "+MimeTypes.FORM_ENCODED+"\r\n"+
++        "Content-Length: "+buf.length()+"\r\n"+
++        "Connection: close\r\n"+
++        "\r\n"+
++        buf;
++        
++        long start=System.currentTimeMillis();
++        String response = _connector.getResponses(request);
++        assertTrue(response.contains("200 OK"));
++        long now=System.currentTimeMillis();
++        assertTrue((now-start)<5000);
++    }    
+     
+     
+     interface RequestTester
+@@ -498,8 +550,8 @@ public class RequestTest extends TestCas
+         public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException
+         {
+             ((Request)request).setHandled(true);
+-            
+-            if (request.getContentLength()>0)
++
++            if (request.getContentLength()>0 && !MimeTypes.FORM_ENCODED.equals(request.getContentType()))
+                 _content=IO.toString(request.getInputStream());
+             
+             if (_checker!=null && _checker.check(request,response))
+diff -up ./modules/util/src/main/java/org/mortbay/util/UrlEncoded.java.fix ./modules/util/src/main/java/org/mortbay/util/UrlEncoded.java
+--- ./modules/util/src/main/java/org/mortbay/util/UrlEncoded.java.fix	2012-01-16 14:47:05.000000000 -0500
++++ ./modules/util/src/main/java/org/mortbay/util/UrlEncoded.java	2012-01-16 16:59:21.000000000 -0500
+@@ -17,9 +17,11 @@ package org.mortbay.util;
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.io.InputStreamReader;
++import java.io.StringWriter;
+ import java.io.UnsupportedEncodingException;
+ import java.util.Iterator;
+ import java.util.Map;
++import org.mortbay.log.Log;
+ 
+ 
+ /* ------------------------------------------------------------ */
+@@ -73,13 +75,13 @@ public class UrlEncoded extends MultiMap
+     /* ----------------------------------------------------------------- */
+     public void decode(String query)
+     {
+-        decodeTo(query,this,ENCODING);
++        decodeTo(query,this,ENCODING,-1);
+     }
+     
+     /* ----------------------------------------------------------------- */
+     public void decode(String query,String charset)
+     {
+-        decodeTo(query,this,charset);
++        decodeTo(query,this,charset,-1);
+     }
+     
+     /* -------------------------------------------------------------- */
+@@ -174,6 +176,15 @@ public class UrlEncoded extends MultiMap
+      */
+     public static void decodeTo(String content, MultiMap map, String charset)
+     {
++        decodeTo(content,map,charset,-1);
++    }
++    
++    /* -------------------------------------------------------------- */
++    /** Decoded parameters to Map.
++     * @param content the string containing the encoded parameters
++     */
++    public static void decodeTo(String content, MultiMap map, String charset, int maxKeys)
++    {
+         if (charset==null)
+             charset=ENCODING;
+ 
+@@ -204,6 +215,11 @@ public class UrlEncoded extends MultiMap
+                       }
+                       key = null;
+                       value=null;
++                      if (maxKeys>0 && map.size()>maxKeys)
++                      {
++                          Log.warn("maxFormKeys limit exceeded keys>{}",Integer.valueOf(maxKeys));
++                          return;
++                      }
+                       break;
+                   case '=':
+                       if (key!=null)
+@@ -320,9 +336,10 @@ public class UrlEncoded extends MultiMap
+     /** Decoded parameters to Map.
+      * @param in InputSteam to read
+      * @param map MultiMap to add parameters to
+-     * @param maxLength maximum length of content to read 0r -1 for no limit
++     * @param maxLength maximum length of content to read or -1 for no limit
++     * @param maxLength maximum number of keys to read or -1 for no limit
+      */
+-    public static void decode88591To(InputStream in, MultiMap map, int maxLength)
++    public static void decode88591To(InputStream in, MultiMap map, int maxLength, int maxKeys)
+     throws IOException
+     {
+         synchronized(map)
+@@ -352,6 +369,11 @@ public class UrlEncoded extends MultiMap
+                         }
+                         key = null;
+                         value=null;
++                        if (maxKeys>0 && map.size()>maxKeys)
++                        {
++                            Log.warn("maxFormKeys limit exceeded keys>{}",Integer.valueOf(maxKeys));
++                            return;
++                        }
+                         break;
+                         
+                     case '=':
+@@ -400,9 +422,10 @@ public class UrlEncoded extends MultiMap
+     /** Decoded parameters to Map.
+      * @param in InputSteam to read
+      * @param map MultiMap to add parameters to
+-     * @param maxLength maximum length of content to read 0r -1 for no limit
++     * @param maxLength maximum length of content to read or -1 for no limit
++     * @param maxLength maximum number of keys to read or -1 for no limit
+      */
+-    public static void decodeUtf8To(InputStream in, MultiMap map, int maxLength)
++    public static void decodeUtf8To(InputStream in, MultiMap map, int maxLength, int maxKeys)
+     throws IOException
+     {
+         synchronized(map)
+@@ -432,6 +455,11 @@ public class UrlEncoded extends MultiMap
+                         }
+                         key = null;
+                         value=null;
++                        if (maxKeys>0 && map.size()>maxKeys)
++                        {
++                            Log.warn("maxFormKeys limit exceeded keys>{}",Integer.valueOf(maxKeys));
++                            return;
++                        }
+                         break;
+                         
+                     case '=':
+@@ -477,43 +505,38 @@ public class UrlEncoded extends MultiMap
+     }
+     
+     /* -------------------------------------------------------------- */
+-    public static void decodeUtf16To(InputStream in, MultiMap map, int maxLength) throws IOException
++    public static void decodeUtf16To(InputStream in, MultiMap map, int maxLength, int maxKeys) throws IOException
+     {
+         InputStreamReader input = new InputStreamReader(in,StringUtil.__UTF16);
+-        StringBuffer buf = new StringBuffer();
+-
+-        int c;
+-        int length=0;
+-        if (maxLength<0)
+-            maxLength=Integer.MAX_VALUE;
+-        while ((c=input.read())>0 && length++<maxLength)
+-            buf.append((char)c);
+-        decodeTo(buf.toString(),map,ENCODING);
++        StringWriter buf = new StringWriter(8192);
++        IO.copy(input,buf,maxLength);
++        
++        decodeTo(buf.getBuffer().toString(),map,ENCODING,maxKeys);
+     }
+     
+     /* -------------------------------------------------------------- */
+     /** Decoded parameters to Map.
+      * @param in the stream containing the encoded parameters
+      */
+-    public static void decodeTo(InputStream in, MultiMap map, String charset, int maxLength)
++    public static void decodeTo(InputStream in, MultiMap map, String charset, int maxLength, int maxKeys)
+     throws IOException
+     {
+ 
+         if (charset==null || StringUtil.__UTF8.equalsIgnoreCase(charset))
+         {
+-            decodeUtf8To(in,map,maxLength);
++            decodeUtf8To(in,map,maxLength,maxKeys);
+             return;
+         }
+         
+         if (StringUtil.__ISO_8859_1.equals(charset))
+         {
+-            decode88591To(in,map,maxLength);
++            decode88591To(in,map,maxLength,maxKeys);
+             return;
+         }
+ 
+         if (StringUtil.__UTF16.equalsIgnoreCase(charset)) // Should be all 2 byte encodings
+         {
+-            decodeUtf16To(in,map,maxLength);
++            decodeUtf16To(in,map,maxLength,maxKeys);
+             return;
+         }
+         
+diff -up ./modules/util/src/test/java/org/mortbay/util/URLEncodedTest.java.fix ./modules/util/src/test/java/org/mortbay/util/URLEncodedTest.java
+--- ./modules/util/src/test/java/org/mortbay/util/URLEncodedTest.java.fix	2012-01-16 15:19:40.000000000 -0500
++++ ./modules/util/src/test/java/org/mortbay/util/URLEncodedTest.java	2012-01-16 15:20:36.000000000 -0500
+@@ -163,7 +163,7 @@ public class URLEncodedTest extends juni
+         {
+             ByteArrayInputStream in = new ByteArrayInputStream("name\n=value+%30&name1=&name2&n\u00e3me3=value+3".getBytes(charsets[i][0]));
+             MultiMap m = new MultiMap();
+-            UrlEncoded.decodeTo(in, m, charsets[i][1], -1);
++            UrlEncoded.decodeTo(in, m, charsets[i][1], -1, -1);
+             System.err.println(m);
+             assertEquals(i+" stream length",4,m.size());
+             assertEquals(i+" stream name\\n","value 0",m.getString("name\n"));
+@@ -177,7 +177,7 @@ public class URLEncodedTest extends juni
+         {
+             ByteArrayInputStream in2 = new ByteArrayInputStream ("name=%83e%83X%83g".getBytes());
+             MultiMap m2 = new MultiMap();
+-            UrlEncoded.decodeTo(in2, m2, "Shift_JIS", -1);
++            UrlEncoded.decodeTo(in2, m2, "Shift_JIS", -1, -1);
+             assertEquals("stream length",1,m2.size());
+             assertEquals("stream name","\u30c6\u30b9\u30c8",m2.getString("name"));
+         }
diff --git a/jetty.spec b/jetty.spec
index 671081a..f1a983a 100644
--- a/jetty.spec
+++ b/jetty.spec
@@ -42,7 +42,7 @@
 
 Name:           jetty
 Version:        6.1.26
-Release:        6%{?dist}
+Release:        7%{?dist}
 Summary:        The Jetty Webserver and Servlet Container
 
 Group:          Applications/Internet
@@ -56,6 +56,7 @@ Source4:        %{name}-depmap.xml
 Patch0:	        disable-modules.patch
 Patch1:	        jetty-util-pom.patch
 Patch4:	        jetty-plugin-fix-site.patch
+Patch5:	        jetty-hashDOS.patch
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 BuildArch:      noarch
@@ -148,6 +149,7 @@ find . -name "*.class" -exec rm {} \;
 %patch0 -b .sav
 %patch1 -b .sav
 %patch4 -b .sav
+%patch5 -b .sav
 
 cp %{SOURCE1} djetty
 
@@ -371,6 +373,9 @@ fi
 %doc %{_docdir}/%{name}-%{version}
 
 %changelog
+* Mon Jan 16 2012 Jeff Johnston <jjohnstn at redhat.com> - 6.1.26-7
+- Resolves CVE-2011-4461
+
 * Wed Feb 09 2011 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 6.1.26-6
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
 


More information about the scm-commits mailing list