You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2023/05/02 15:49:10 UTC

[tomcat] branch 8.5.x updated: Zero length HTTP header names are not valid (not valid tokens)

This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/8.5.x by this push:
     new 3f379d417e Zero length HTTP header names are not valid (not valid tokens)
3f379d417e is described below

commit 3f379d417ebba69af1f3f48df52e64a2a6f5e283
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue May 2 16:47:25 2023 +0100

    Zero length HTTP header names are not valid (not valid tokens)
---
 .../apache/coyote/http11/Http11InputBuffer.java    | 18 ++++++---
 .../apache/coyote/http11/LocalStrings.properties   |  1 +
 .../coyote/http11/TestHttp11InputBuffer.java       | 44 ++++++++++++++++++++++
 webapps/docs/changelog.xml                         |  4 ++
 4 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/java/org/apache/coyote/http11/Http11InputBuffer.java b/java/org/apache/coyote/http11/Http11InputBuffer.java
index bb8945fc8e..87799b7d6c 100644
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -50,8 +50,8 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
     private static final StringManager sm = StringManager.getManager(Http11InputBuffer.class);
 
 
-    private static final byte[] CLIENT_PREFACE_START = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
-            .getBytes(StandardCharsets.ISO_8859_1);
+    private static final byte[] CLIENT_PREFACE_START =
+            "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1);
 
     /**
      * Associated Coyote request.
@@ -883,6 +883,11 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
             int pos = byteBuffer.position();
             chr = byteBuffer.get();
             if (chr == Constants.COLON) {
+                if (headerData.start == pos) {
+                    // Zero length header name - not valid.
+                    // skipLine() will handle the error
+                    return skipLine(false);
+                }
                 headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
                 headerData.headerValue = headers.addValue(byteBuffer.array(), headerData.start, pos - headerData.start);
                 pos = byteBuffer.position();
@@ -1064,12 +1069,13 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
             }
         }
         if (rejectThisHeader || log.isDebugEnabled()) {
-            String message = sm.getString("iib.invalidheader", HeaderUtil.toPrintableString(byteBuffer.array(),
-                    headerData.lineStart, headerData.lastSignificantChar - headerData.lineStart + 1));
             if (rejectThisHeader) {
-                throw new IllegalArgumentException(message);
+                throw new IllegalArgumentException(
+                        sm.getString("iib.invalidheader.reject", HeaderUtil.toPrintableString(byteBuffer.array(),
+                                headerData.lineStart, headerData.lastSignificantChar - headerData.lineStart + 1)));
             }
-            log.debug(message);
+            log.debug(sm.getString("iib.invalidheader", HeaderUtil.toPrintableString(byteBuffer.array(),
+                    headerData.lineStart, headerData.lastSignificantChar - headerData.lineStart + 1)));
         }
 
         headerParsePos = HeaderParsePosition.HEADER_START;
diff --git a/java/org/apache/coyote/http11/LocalStrings.properties b/java/org/apache/coyote/http11/LocalStrings.properties
index 7af34c2d1d..9be53d2d77 100644
--- a/java/org/apache/coyote/http11/LocalStrings.properties
+++ b/java/org/apache/coyote/http11/LocalStrings.properties
@@ -49,6 +49,7 @@ iib.invalidHttpProtocol=Invalid character found in the HTTP protocol [{0}]
 iib.invalidPhase=Invalid request line parse phase [{0}]
 iib.invalidRequestTarget=Invalid character found in the request target [{0}]. The valid characters are defined in RFC 7230 and RFC 3986
 iib.invalidheader=The HTTP header line [{0}] does not conform to RFC 7230 and has been ignored.
+iib.invalidheader.reject=The HTTP header line [{0}] does not conform to RFC 7230. The request has been rejected.
 iib.invalidmethod=Invalid character found in method name [{0}]. HTTP method names must be tokens
 iib.parseheaders.ise.error=Unexpected state: headers already parsed. Buffer not recycled?
 iib.readtimeout=Timeout attempting to read data from the socket
diff --git a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
index 104f2b4724..186bd0db0a 100644
--- a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
+++ b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
@@ -733,4 +733,48 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
             return true;
         }
     }
+
+
+    private static final class Client extends SimpleHttpClient {
+
+        Client(int port) {
+            setPort(port);
+        }
+
+        @Override
+        public boolean isResponseBodyOK() {
+            return getResponseBody().contains("test - data");
+        }
+    }
+
+
+    @Test
+    public void testInvalidHeader02() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        // This setting means the connection will be closed at the end of the
+        // request
+        Assert.assertTrue(tomcat.getConnector().setProperty("maxKeepAliveRequests", "1"));
+
+        // No file system docBase required
+        Context ctx = tomcat.addContext("", null);
+
+        // Add servlet
+        Tomcat.addServlet(ctx, "TesterServlet", new TesterServlet());
+        ctx.addServletMappingDecoded("/foo", "TesterServlet");
+
+        tomcat.start();
+
+        String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: localhost" + SimpleHttpClient.CRLF + ":b" +
+                SimpleHttpClient.CRLF + "X-Dummy:b" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF;
+
+        Client client = new Client(tomcat.getConnector().getLocalPort());
+        client.setRequest(new String[] { request });
+
+        client.connect(10000, 600000);
+        client.processRequest();
+
+        // Expected response is a 400 response.
+        Assert.assertTrue(client.getResponseLine(), client.isResponse400());
+    }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 10c8c924cc..96acee79fe 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -154,6 +154,10 @@
         <code>false</code> to <code>true</code> to harden the default
         configuration. (markt)
       </scode>
+      <fix>
+        Fix an edge case in HTTP header parsing and ensure that HTTP headers
+        without names are treated as invalid. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Jasper">


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org