[undertow] 01/02: Add CVE-2017-2666.patch and CVE-2017-2670.patch
Markus Koschany
apo at moszumanska.debian.org
Tue Jul 11 14:00:59 UTC 2017
This is an automated email from the git hooks/post-receive script.
apo pushed a commit to branch stretch
in repository undertow.
commit 0c69a17767663dd72218affbc8588d4c515f3a80
Author: Markus Koschany <apo at debian.org>
Date: Tue Jul 11 13:36:41 2017 +0200
Add CVE-2017-2666.patch and CVE-2017-2670.patch
---
debian/patches/CVE-2017-2666.patch | 625 +++++++++++++++++++++++++++++++++++++
debian/patches/CVE-2017-2670.patch | 24 ++
debian/patches/series | 2 +
3 files changed, 651 insertions(+)
diff --git a/debian/patches/CVE-2017-2666.patch b/debian/patches/CVE-2017-2666.patch
new file mode 100644
index 0000000..78f2a86
--- /dev/null
+++ b/debian/patches/CVE-2017-2666.patch
@@ -0,0 +1,625 @@
+From: Markus Koschany <apo at debian.org>
+Date: Tue, 11 Jul 2017 13:25:29 +0200
+Subject: CVE-2017-2666
+
+Bug-Debian: https://bugs.debian.org/864405
+Bug-Upstream: https://issues.jboss.org/browse/UNDERTOW-1101
+Origin: https://github.com/undertow-io/undertow/commit/1e72647818c9fb31b693a953b1ae595a6c82eb7f
+---
+ .../main/java/io/undertow/UndertowMessages.java | 10 ++
+ .../protocols/http2/Http2HeaderBlockParser.java | 4 +
+ .../main/java/io/undertow/server/Connectors.java | 76 ++++++++++++
+ .../server/protocol/ajp/AjpReadListener.java | 4 +
+ .../server/protocol/ajp/AjpRequestParser.java | 6 +-
+ .../server/protocol/http/HttpReadListener.java | 4 +
+ .../server/protocol/http/HttpRequestParser.java | 27 +++++
+ .../protocol/http2/Http2ReceiveListener.java | 5 +
+ core/src/main/java/io/undertow/util/Methods.java | 6 +-
+ .../undertow/server/InvalidHtpRequestTestCase.java | 134 +++++++++++++++++++++
+ .../handlers/form/FormDataParserTestCase.java | 16 ++-
+ .../server/protocol/http/SimpleParserTestCase.java | 17 +--
+ .../RequestParserGenerator.java | 5 +
+ .../io/undertow/servlet/test/path/PathFilter.java | 2 +-
+ 14 files changed, 299 insertions(+), 17 deletions(-)
+ create mode 100644 core/src/test/java/io/undertow/server/InvalidHtpRequestTestCase.java
+
+diff --git a/core/src/main/java/io/undertow/UndertowMessages.java b/core/src/main/java/io/undertow/UndertowMessages.java
+index a836bef..db631ed 100644
+--- a/core/src/main/java/io/undertow/UndertowMessages.java
++++ b/core/src/main/java/io/undertow/UndertowMessages.java
+@@ -504,4 +504,14 @@ public interface UndertowMessages {
+
+ @Message(id = 157, value = "Invalid GZIP footer")
+ IOException invalidGZIPFooter();
++
++ @Message(id = 163, value = "Invalid token %s")
++ IllegalArgumentException invalidToken(byte c);
++
++ @Message(id = 164, value = "Request contained invalid headers")
++ IllegalArgumentException invalidHeaders();
++
++ @Message(id = 165, value = "Invalid character %s in request-target")
++ String invalidCharacterInRequestTarget(char next);
++
+ }
+diff --git a/core/src/main/java/io/undertow/protocols/http2/Http2HeaderBlockParser.java b/core/src/main/java/io/undertow/protocols/http2/Http2HeaderBlockParser.java
+index 2ca8654..dc6ea3d 100644
+--- a/core/src/main/java/io/undertow/protocols/http2/Http2HeaderBlockParser.java
++++ b/core/src/main/java/io/undertow/protocols/http2/Http2HeaderBlockParser.java
+@@ -28,6 +28,7 @@ import org.xnio.Bits;
+ import io.undertow.UndertowLogger;
+
+ import io.undertow.UndertowMessages;
++import io.undertow.server.Connectors;
+ import io.undertow.util.HeaderMap;
+ import io.undertow.util.Headers;
+ import io.undertow.util.HttpString;
+@@ -168,6 +169,9 @@ abstract class Http2HeaderBlockParser extends Http2PushBackParser implements Hpa
+ if(c>= 'A' && c <= 'Z') {
+ invalid = true;
+ UndertowLogger.REQUEST_LOGGER.debugf("Malformed request, header %s contains uppercase characters", name);
++ } else if(c != ':' && !Connectors.isValidTokenCharacter(c)) {
++ invalid = true;
++ UndertowLogger.REQUEST_LOGGER.debugf("Malformed request, header %s contains invalid token character", name);
+ }
+ }
+
+diff --git a/core/src/main/java/io/undertow/server/Connectors.java b/core/src/main/java/io/undertow/server/Connectors.java
+index 310dcab..45db405 100644
+--- a/core/src/main/java/io/undertow/server/Connectors.java
++++ b/core/src/main/java/io/undertow/server/Connectors.java
+@@ -19,10 +19,14 @@
+ package io.undertow.server;
+
+ import io.undertow.UndertowLogger;
++import io.undertow.UndertowMessages;
+ import io.undertow.UndertowOptions;
+ import io.undertow.server.handlers.Cookie;
+ import io.undertow.util.DateUtils;
++import io.undertow.util.HeaderMap;
++import io.undertow.util.HeaderValues;
+ import io.undertow.util.Headers;
++import io.undertow.util.HttpString;
+ import io.undertow.util.ParameterLimitException;
+ import io.undertow.util.StatusCodes;
+ import io.undertow.util.URLUtils;
+@@ -45,7 +49,40 @@ import java.util.concurrent.RejectedExecutionException;
+ */
+ public class Connectors {
+
++ private static final boolean[] ALLOWED_TOKEN_CHARACTERS = new boolean[256];
+
++ static {
++ for(int i = 0; i < ALLOWED_TOKEN_CHARACTERS.length; ++i) {
++ if((i >='0' && i <= '9') ||
++ (i >='a' && i <= 'z') ||
++ (i >='A' && i <= 'Z')) {
++ ALLOWED_TOKEN_CHARACTERS[i] = true;
++ } else {
++ switch (i) {
++ case '!':
++ case '#':
++ case '$':
++ case '%':
++ case '&':
++ case '\'':
++ case '*':
++ case '+':
++ case '-':
++ case '.':
++ case '^':
++ case '_':
++ case '`':
++ case '|':
++ case '~': {
++ ALLOWED_TOKEN_CHARACTERS[i] = true;
++ break;
++ }
++ default:
++ ALLOWED_TOKEN_CHARACTERS[i] = false;
++ }
++ }
++ }
++ }
+ /**
+ * Flattens the exchange cookie map into the response header map. This should be called by a
+ * connector just before the response is started.
+@@ -358,4 +395,43 @@ public class Connectors {
+ public static ConduitStreamSinkChannel getConduitSinkChannel(HttpServerExchange exchange) {
+ return exchange.getConnection().getSinkChannel();
+ }
++
++ /**
++ * Verifies that the contents of the HttpString are a valid token according to rfc7230.
++ * @param header The header to verify
++ */
++ public static void verifyToken(HttpString header) {
++ int length = header.length();
++ for(int i = 0; i < length; ++i) {
++ byte c = header.byteAt(i);
++ if(!ALLOWED_TOKEN_CHARACTERS[c]) {
++ throw UndertowMessages.MESSAGES.invalidToken(c);
++ }
++ }
++ }
++
++ /**
++ * Returns true if the token character is valid according to rfc7230
++ */
++ public static boolean isValidTokenCharacter(byte c) {
++ return ALLOWED_TOKEN_CHARACTERS[c];
++ }
++
++
++ /**
++ * Verifies that the provided request headers are valid according to rfc7230. In particular:
++ * - At most one content-length or transfer encoding
++ */
++ public static boolean areRequestHeadersValid(HeaderMap headers) {
++ HeaderValues te = headers.get(Headers.TRANSFER_ENCODING);
++ HeaderValues cl = headers.get(Headers.CONTENT_LENGTH);
++ if(te != null && cl != null) {
++ return false;
++ } else if(te != null && te.size() > 1) {
++ return false;
++ } else if(cl != null && cl.size() > 1) {
++ return false;
++ }
++ return true;
++ }
+ }
+diff --git a/core/src/main/java/io/undertow/server/protocol/ajp/AjpReadListener.java b/core/src/main/java/io/undertow/server/protocol/ajp/AjpReadListener.java
+index ee3d51d..3547c39 100644
+--- a/core/src/main/java/io/undertow/server/protocol/ajp/AjpReadListener.java
++++ b/core/src/main/java/io/undertow/server/protocol/ajp/AjpReadListener.java
+@@ -237,6 +237,10 @@ final class AjpReadListener implements ChannelListener<StreamSourceChannel> {
+ if(connectorStatistics != null) {
+ connectorStatistics.setup(httpServerExchange);
+ }
++ if(!Connectors.areRequestHeadersValid(httpServerExchange.getRequestHeaders())) {
++ oldState.badRequest = true;
++ UndertowLogger.REQUEST_IO_LOGGER.debugf("Invalid AJP request from %s, request contained invalid headers", connection.getPeerAddress());
++ }
+
+ if(oldState.badRequest) {
+ httpServerExchange.setStatusCode(StatusCodes.BAD_REQUEST);
+diff --git a/core/src/main/java/io/undertow/server/protocol/ajp/AjpRequestParser.java b/core/src/main/java/io/undertow/server/protocol/ajp/AjpRequestParser.java
+index 7170a29..bcec7a8 100644
+--- a/core/src/main/java/io/undertow/server/protocol/ajp/AjpRequestParser.java
++++ b/core/src/main/java/io/undertow/server/protocol/ajp/AjpRequestParser.java
+@@ -54,6 +54,7 @@ import java.util.TreeMap;
+
+ import io.undertow.UndertowMessages;
+ import io.undertow.security.impl.ExternalAuthenticationMechanism;
++import io.undertow.server.Connectors;
+ import io.undertow.server.HttpServerExchange;
+ import io.undertow.util.Headers;
+ import io.undertow.util.HttpString;
+@@ -341,6 +342,7 @@ public class AjpRequestParser {
+ state.currentHeader = result.header;
+ } else {
+ state.currentHeader = HttpString.tryFromString(result.value);
++ Connectors.verifyToken(state.currentHeader);
+ }
+ }
+ StringHolder result = parseString(buf, state, StringType.OTHER);
+@@ -418,7 +420,9 @@ public class AjpRequestParser {
+ } else if (state.currentAttribute.equals(AUTH_TYPE)) {
+ exchange.putAttachment(ExternalAuthenticationMechanism.EXTERNAL_AUTHENTICATION_TYPE, result);
+ } else if (state.currentAttribute.equals(STORED_METHOD)) {
+- exchange.setRequestMethod(new HttpString(result));
++ HttpString requestMethod = new HttpString(result);
++ Connectors.verifyToken(requestMethod);
++ exchange.setRequestMethod(requestMethod);
+ } else if (state.currentAttribute.equals(AJP_REMOTE_PORT)) {
+ state.remotePort = Integer.parseInt(result);
+ } else if (state.currentAttribute.equals(SSL_SESSION)) {
+diff --git a/core/src/main/java/io/undertow/server/protocol/http/HttpReadListener.java b/core/src/main/java/io/undertow/server/protocol/http/HttpReadListener.java
+index 3f569b6..6081aff 100644
+--- a/core/src/main/java/io/undertow/server/protocol/http/HttpReadListener.java
++++ b/core/src/main/java/io/undertow/server/protocol/http/HttpReadListener.java
+@@ -240,6 +240,10 @@ final class HttpReadListener implements ChannelListener<ConduitStreamSourceChann
+ return;
+ }
+ }
++ if(!Connectors.areRequestHeadersValid(httpServerExchange.getRequestHeaders())) {
++ sendBadRequestAndClose(connection.getChannel(), UndertowMessages.MESSAGES.invalidHeaders());
++ return;
++ }
+ Connectors.executeRootHandler(connection.getRootHandler(), httpServerExchange);
+ } catch (Exception e) {
+ sendBadRequestAndClose(connection.getChannel(), e);
+diff --git a/core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java b/core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java
+index cd5ceb0..a561918 100644
+--- a/core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java
++++ b/core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java
+@@ -165,6 +165,8 @@ public abstract class HttpRequestParser {
+ private final boolean decode;
+ private final String charset;
+
++ private static final boolean[] ALLOWED_TARGET_CHARACTER = new boolean[256];
++
+ static {
+ try {
+ HTTP = "HTTP/1.".getBytes("ASCII");
+@@ -172,6 +174,28 @@ public abstract class HttpRequestParser {
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
++ for(int i = 0; i < 256; ++i) {
++ if(i < 32 || i > 126) {
++ ALLOWED_TARGET_CHARACTER[i] = false;
++ } else {
++ switch ((char)i) {
++ case '\"':
++ case '#':
++ case '<':
++ case '>':
++ case '\\':
++ case '^':
++ case '`':
++ case '{':
++ case '|':
++ case '}':
++ ALLOWED_TARGET_CHARACTER[i] = false;
++ break;
++ default:
++ ALLOWED_TARGET_CHARACTER[i] = true;
++ }
++ }
++ }
+ }
+
+ public HttpRequestParser(OptionMap options) {
+@@ -345,6 +369,9 @@ public abstract class HttpRequestParser {
+
+ while (buffer.hasRemaining()) {
+ char next = (char) (buffer.get() & 0xFF);
++ if(!ALLOWED_TARGET_CHARACTER[next]) {
++ throw new BadRequestException(UndertowMessages.MESSAGES.invalidCharacterInRequestTarget(next));
++ }
+ if (next == ' ' || next == '\t') {
+ if (stringBuilder.length() != 0) {
+ final String path = stringBuilder.toString();
+diff --git a/core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java b/core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java
+index 695d595..9dcbfc6 100644
+--- a/core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java
++++ b/core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java
+@@ -143,6 +143,11 @@ public class Http2ReceiveListener implements ChannelListener<Http2Channel> {
+ exchange.setProtocol(Protocols.HTTP_2_0);
+ exchange.setRequestMethod(Methods.fromString(exchange.getRequestHeaders().getFirst(METHOD)));
+ exchange.getRequestHeaders().put(Headers.HOST, exchange.getRequestHeaders().getFirst(AUTHORITY));
++ if(!Connectors.areRequestHeadersValid(exchange.getRequestHeaders())) {
++ UndertowLogger.REQUEST_IO_LOGGER.debugf("Invalid headers in HTTP/2 request, closing connection. Remote peer %s", connection.getPeerAddress());
++ channel.sendGoAway(Http2Channel.ERROR_PROTOCOL_ERROR);
++ return;
++ }
+
+ final String path = exchange.getRequestHeaders().getFirst(PATH);
+ try {
+diff --git a/core/src/main/java/io/undertow/util/Methods.java b/core/src/main/java/io/undertow/util/Methods.java
+index 678d4ee..a0801dd 100644
+--- a/core/src/main/java/io/undertow/util/Methods.java
++++ b/core/src/main/java/io/undertow/util/Methods.java
+@@ -22,6 +22,8 @@ import java.util.Collections;
+ import java.util.HashMap;
+ import java.util.Map;
+
++import io.undertow.server.Connectors;
++
+ /**
+ * NOTE: If you add a new method here you must also add it to {@link io.undertow.server.protocol.http.HttpRequestParser}
+ *
+@@ -138,7 +140,9 @@ public final class Methods {
+ public static HttpString fromString(String method) {
+ HttpString res = METHODS.get(method);
+ if(res == null) {
+- return new HttpString(method);
++ HttpString httpString = new HttpString(method);
++ Connectors.verifyToken(httpString);
++ return httpString;
+ }
+ return res;
+ }
+diff --git a/core/src/test/java/io/undertow/server/InvalidHtpRequestTestCase.java b/core/src/test/java/io/undertow/server/InvalidHtpRequestTestCase.java
+new file mode 100644
+index 0000000..4761eeb
+--- /dev/null
++++ b/core/src/test/java/io/undertow/server/InvalidHtpRequestTestCase.java
+@@ -0,0 +1,134 @@
++/*
++ * JBoss, Home of Professional Open Source.
++ * Copyright 2014 Red Hat, Inc., and individual contributors
++ * as indicated by the @author tags.
++ *
++ * Licensed 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.
++ */
++
++package io.undertow.server;
++
++import java.io.IOException;
++import java.net.URI;
++import java.net.URISyntaxException;
++
++import org.apache.http.HttpResponse;
++import org.apache.http.client.methods.HttpGet;
++import org.apache.http.client.methods.HttpRequestBase;
++import org.junit.Assert;
++import org.junit.BeforeClass;
++import org.junit.Test;
++import org.junit.runner.RunWith;
++import io.undertow.server.handlers.ResponseCodeHandler;
++import io.undertow.testutils.DefaultServer;
++import io.undertow.testutils.HttpOneOnly;
++import io.undertow.testutils.ProxyIgnore;
++import io.undertow.testutils.TestHttpClient;
++import io.undertow.util.Headers;
++import io.undertow.util.StatusCodes;
++
++/**
++ * @author Stuart Douglas
++ */
++ at RunWith(DefaultServer.class)
++ at ProxyIgnore
++ at HttpOneOnly
++public class InvalidHtpRequestTestCase {
++
++ @BeforeClass
++ public static void setup() {
++ DefaultServer.setRootHandler(ResponseCodeHandler.HANDLE_200);
++ }
++
++ @Test
++ public void testInvalidCharacterInMethod() throws IOException {
++ final TestHttpClient client = new TestHttpClient();
++ try {
++ HttpRequestBase method = new HttpRequestBase() {
++
++ @Override
++ public String getMethod() {
++ return "GET;POST";
++ }
++
++ @Override
++ public URI getURI() {
++ try {
++ return new URI(DefaultServer.getDefaultServerURL());
++ } catch (URISyntaxException e) {
++ throw new RuntimeException(e);
++ }
++ }
++ };
++ HttpResponse result = client.execute(method);
++ Assert.assertEquals(StatusCodes.BAD_REQUEST, result.getStatusLine().getStatusCode());
++ } finally {
++ client.getConnectionManager().shutdown();
++ }
++ }
++
++
++ @Test
++ public void testInvalidCharacterInHeader() throws IOException {
++ final TestHttpClient client = new TestHttpClient();
++ try {
++ HttpRequestBase method = new HttpGet(DefaultServer.getDefaultServerURL());
++ method.addHeader("fake;header", "value");
++ HttpResponse result = client.execute(method);
++ Assert.assertEquals(StatusCodes.BAD_REQUEST, result.getStatusLine().getStatusCode());
++ } finally {
++ client.getConnectionManager().shutdown();
++ }
++ }
++
++ @Test
++ public void testMultipleContentLengths() throws IOException {
++ final TestHttpClient client = new TestHttpClient();
++ try {
++ HttpRequestBase method = new HttpGet(DefaultServer.getDefaultServerURL());
++ method.addHeader(Headers.CONTENT_LENGTH_STRING, "0");
++ method.addHeader(Headers.CONTENT_LENGTH_STRING, "10");
++ HttpResponse result = client.execute(method);
++ Assert.assertEquals(StatusCodes.BAD_REQUEST, result.getStatusLine().getStatusCode());
++ } finally {
++ client.getConnectionManager().shutdown();
++ }
++ }
++ @Test
++ public void testContentLengthAndTransferEncoding() throws IOException {
++ final TestHttpClient client = new TestHttpClient();
++ try {
++ HttpRequestBase method = new HttpGet(DefaultServer.getDefaultServerURL());
++ method.addHeader(Headers.CONTENT_LENGTH_STRING, "0");
++ method.addHeader(Headers.TRANSFER_ENCODING_STRING, "chunked");
++ HttpResponse result = client.execute(method);
++ Assert.assertEquals(StatusCodes.BAD_REQUEST, result.getStatusLine().getStatusCode());
++ } finally {
++ client.getConnectionManager().shutdown();
++ }
++ }
++
++ @Test
++ public void testMultipleTransferEncoding() throws IOException {
++ final TestHttpClient client = new TestHttpClient();
++ try {
++ HttpRequestBase method = new HttpGet(DefaultServer.getDefaultServerURL());
++ method.addHeader(Headers.TRANSFER_ENCODING_STRING, "chunked");
++ method.addHeader(Headers.TRANSFER_ENCODING_STRING, "gzip, chunked");
++ HttpResponse result = client.execute(method);
++ Assert.assertEquals(StatusCodes.BAD_REQUEST, result.getStatusLine().getStatusCode());
++ } finally {
++ client.getConnectionManager().shutdown();
++ }
++ }
++}
+diff --git a/core/src/test/java/io/undertow/server/handlers/form/FormDataParserTestCase.java b/core/src/test/java/io/undertow/server/handlers/form/FormDataParserTestCase.java
+index 7895187..a964bb3 100644
+--- a/core/src/test/java/io/undertow/server/handlers/form/FormDataParserTestCase.java
++++ b/core/src/test/java/io/undertow/server/handlers/form/FormDataParserTestCase.java
+@@ -22,8 +22,10 @@ import java.io.IOException;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Collection;
++import java.util.HashMap;
+ import java.util.Iterator;
+ import java.util.List;
++import java.util.Map;
+
+ import io.undertow.server.HttpHandler;
+ import io.undertow.server.HttpServerExchange;
+@@ -35,6 +37,7 @@ import io.undertow.util.HttpString;
+ import io.undertow.testutils.TestHttpClient;
+ import io.undertow.util.StatusCodes;
+ import junit.textui.TestRunner;
++import org.apache.http.Header;
+ import org.apache.http.HttpResponse;
+ import org.apache.http.NameValuePair;
+ import org.apache.http.client.entity.UrlEncodedFormEntity;
+@@ -77,7 +80,7 @@ public class FormDataParserTestCase {
+ while (it.hasNext()) {
+ String fd = it.next();
+ for (FormData.FormValue val : data.get(fd)) {
+- exchange.getResponseHeaders().add(new HttpString(fd), val.getValue());
++ exchange.getResponseHeaders().add(new HttpString("res"), fd + ":" + val.getValue());
+ }
+ }
+ }
+@@ -100,7 +103,7 @@ public class FormDataParserTestCase {
+ while (it.hasNext()) {
+ String fd = it.next();
+ for (FormData.FormValue val : data.get(fd)) {
+- exchange.getResponseHeaders().add(new HttpString(fd), val.getValue());
++ exchange.getResponseHeaders().add(new HttpString("res"), fd + ":" + val.getValue());
+ }
+ }
+ } catch (IOException e) {
+@@ -144,8 +147,15 @@ public class FormDataParserTestCase {
+ }
+
+ private void checkResult(final List<NameValuePair> data, final HttpResponse result) {
++ Map<String, String> res = new HashMap<>();
++ for(Header d : result.getHeaders("res")) {
++ String[] split = d.getValue().split(":");
++ res.put(split[0], split.length == 1 ? "" : split[1]);
++ }
++
++
+ for (NameValuePair vp : data) {
+- Assert.assertEquals(vp.getValue() == null ? "" : vp.getValue(), result.getHeaders(vp.getName())[0].getValue());
++ Assert.assertEquals(vp.getValue() == null ? "" : vp.getValue(), res.get(vp.getName()));
+ }
+ }
+
+diff --git a/core/src/test/java/io/undertow/server/protocol/http/SimpleParserTestCase.java b/core/src/test/java/io/undertow/server/protocol/http/SimpleParserTestCase.java
+index 0ff39e8..a59d33d 100644
+--- a/core/src/test/java/io/undertow/server/protocol/http/SimpleParserTestCase.java
++++ b/core/src/test/java/io/undertow/server/protocol/http/SimpleParserTestCase.java
+@@ -163,16 +163,15 @@ public class SimpleParserTestCase {
+ runTest(in);
+ }
+
+- @Test
++ @Test(expected = BadRequestException.class)
+ public void testTabWhitespace() throws HttpRequestParser.BadRequestException {
+ byte[] in = "GET\t/somepath\tHTTP/1.1\nHost: \t www.somehost.net\nOtherHeader:\tsome\n \t value\n\r\n".getBytes();
+ runTest(in);
+ }
+
+ @Test
+- public void testCanonicalPath() throws HttpRequestParser.BadRequestException {
+- byte[] in = "GET\thttp://www.somehost.net/somepath\tHTTP/1.1\nHost: \t www.somehost.net\nOtherHeader:\tsome\n \t value\n\r\n".getBytes();
+-
++ public void testCanonicalPath() throws BadRequestException {
++ byte[] in = "GET http://www.somehost.net/somepath HTTP/1.1\nHost: \t www.somehost.net\nOtherHeader:\tsome\n \t value\n\r\n".getBytes();
+ final ParseState context = new ParseState();
+ HttpServerExchange result = new HttpServerExchange(null);
+ HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
+@@ -182,7 +181,7 @@ public class SimpleParserTestCase {
+
+ @Test
+ public void testNoHeaders() throws HttpRequestParser.BadRequestException {
+- byte[] in = "GET\t/aa\tHTTP/1.1\n\n\n".getBytes();
++ byte[] in = "GET /aa HTTP/1.1\n\n\n".getBytes();
+
+ final ParseState context = new ParseState();
+ HttpServerExchange result = new HttpServerExchange(null);
+@@ -211,8 +210,7 @@ public class SimpleParserTestCase {
+
+ @Test
+ public void testSameHttpStringReturned() throws HttpRequestParser.BadRequestException {
+- byte[] in = "GET\thttp://www.somehost.net/somepath\tHTTP/1.1\nHost: \t www.somehost.net\nAccept-Charset:\tsome\n \t value\n\r\n".getBytes();
+-
++ byte[] in = "GET http://www.somehost.net/somepath HTTP/1.1\nHost: \t www.somehost.net\nAccept-Charset:\tsome\n \t value\n\r\n".getBytes();
+ final ParseState context1 = new ParseState();
+ HttpServerExchange result1 = new HttpServerExchange(null);
+ HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context1, result1);
+@@ -255,16 +253,13 @@ public class SimpleParserTestCase {
+ Assert.assertEquals("666", result.getQueryParameters().get("777").getFirst());
+ Assert.assertEquals("44", result.getQueryParameters().get(";?").getFirst());
+ }
+- @Test
++ @Test(expected = BadRequestException.class)
+ public void testNonEncodedAsciiCharacters() throws UnsupportedEncodingException, HttpRequestParser.BadRequestException {
+ byte[] in = "GET /bår HTTP/1.1\r\n\r\n".getBytes("ISO-8859-1");
+
+ final ParseState context = new ParseState();
+ HttpServerExchange result = new HttpServerExchange(null);
+ HttpRequestParser.instance(OptionMap.EMPTY).handle(ByteBuffer.wrap(in), context, result);
+- Assert.assertSame(Methods.GET, result.getRequestMethod());
+- Assert.assertEquals("/bår", result.getRequestPath());
+- Assert.assertEquals("/bår", result.getRequestURI()); //not decoded
+ }
+
+ private void runTest(final byte[] in) throws HttpRequestParser.BadRequestException {
+diff --git a/parser-generator/src/main/java/io/undertow/annotationprocessor/RequestParserGenerator.java b/parser-generator/src/main/java/io/undertow/annotationprocessor/RequestParserGenerator.java
+index 3e13588..36c87c8 100644
+--- a/parser-generator/src/main/java/io/undertow/annotationprocessor/RequestParserGenerator.java
++++ b/parser-generator/src/main/java/io/undertow/annotationprocessor/RequestParserGenerator.java
+@@ -32,6 +32,7 @@ public class RequestParserGenerator extends AbstractParserGenerator {
+ public static final String PARSE_STATE_CLASS = "io.undertow.server.protocol.http.ParseState";
+ public static final String HTTP_EXCHANGE_CLASS = "io.undertow.server.HttpServerExchange";
+ public static final String HTTP_EXCHANGE_DESCRIPTOR = "Lio/undertow/server/HttpServerExchange;";
++ private static final String CONNECTORS_CLASS = "io.undertow.server.Connectors";
+
+
+ //parsing states
+@@ -66,6 +67,8 @@ public class RequestParserGenerator extends AbstractParserGenerator {
+ public void handleOtherToken(final CodeAttribute c) {
+ c.aload(PARSE_STATE_VAR);
+ c.swap();
++ c.dup();
++ c.invokestatic(CONNECTORS_CLASS, "verifyToken", "(" + HTTP_STRING_DESCRIPTOR + ")V");
+ c.putfield(parseStateClass, "nextHeader", HTTP_STRING_DESCRIPTOR);
+ }
+
+@@ -150,6 +153,8 @@ public class RequestParserGenerator extends AbstractParserGenerator {
+ public void handleOtherToken(final CodeAttribute c) {
+ c.aload(HTTP_RESULT);
+ c.swap();
++ c.dup();
++ c.invokestatic(CONNECTORS_CLASS, "verifyToken", "(" + HTTP_STRING_DESCRIPTOR + ")V");
+ c.invokevirtual(resultClass, "setRequestMethod", "(" + HTTP_STRING_DESCRIPTOR + ")" + HTTP_EXCHANGE_DESCRIPTOR);
+ c.pop();
+ }
+diff --git a/servlet/src/test/java/io/undertow/servlet/test/path/PathFilter.java b/servlet/src/test/java/io/undertow/servlet/test/path/PathFilter.java
+index b3deb18..eb1bce2 100644
+--- a/servlet/src/test/java/io/undertow/servlet/test/path/PathFilter.java
++++ b/servlet/src/test/java/io/undertow/servlet/test/path/PathFilter.java
+@@ -43,7 +43,7 @@ public class PathFilter implements Filter {
+ @Override
+ public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
+ HttpServletResponse resp = (HttpServletResponse) response;
+- resp.addHeader("filter" + filterConfig.getFilterName(), filterConfig.getFilterName());
++ resp.addHeader("filter" + filterConfig.getFilterName().replace("/", "-"), filterConfig.getFilterName());
+ chain.doFilter(request, response);
+ }
+
diff --git a/debian/patches/CVE-2017-2670.patch b/debian/patches/CVE-2017-2670.patch
new file mode 100644
index 0000000..4242899
--- /dev/null
+++ b/debian/patches/CVE-2017-2670.patch
@@ -0,0 +1,24 @@
+From: Markus Koschany <apo at debian.org>
+Date: Tue, 11 Jul 2017 12:57:00 +0200
+Subject: CVE-2017-2670
+
+Bug-Debian: https://bugs.debian.org/864405
+Bug-Upstream: https://issues.jboss.org/browse/UNDERTOW-1035
+Origin: https://github.com/undertow-io/undertow/commit/9bfe9fbbb595d51157b61693f072895f7dbadd1d
+---
+ .../server/protocol/framed/AbstractFramedStreamSourceChannel.java | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedStreamSourceChannel.java b/core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedStreamSourceChannel.java
+index 0409c99..5ab8c52 100644
+--- a/core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedStreamSourceChannel.java
++++ b/core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedStreamSourceChannel.java
+@@ -285,7 +285,7 @@ public abstract class AbstractFramedStreamSourceChannel<C extends AbstractFramed
+ //although we may be flushed as part of a batch
+ moreData = (frameDataRemaining > 0 && data != null) || !pendingFrameData.isEmpty() || anyAreSet(state, STATE_WAITNG_MINUS_ONE);
+ }
+- while (allAreSet(state, STATE_READS_RESUMED) && allAreClear(state, STATE_CLOSED) && moreData);
++ while (allAreSet(state, STATE_READS_RESUMED) && allAreClear(state, STATE_CLOSED | STATE_STREAM_BROKEN) && moreData);
+ } finally {
+ state &= ~STATE_IN_LISTENER_LOOP;
+ }
diff --git a/debian/patches/series b/debian/patches/series
index 9b2f906..9da88c0 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +1,3 @@
ALPN.patch
+CVE-2017-2670.patch
+CVE-2017-2666.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/undertow.git
More information about the pkg-java-commits
mailing list