[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