327 lines
12 KiB
Diff
327 lines
12 KiB
Diff
From: Markus Koschany <apo@debian.org>
|
|
Date: Wed, 17 Aug 2022 12:58:27 +0200
|
|
Subject: CVE-2022-2047
|
|
|
|
Origin: https://github.com/eclipse/jetty.project/pull/8146
|
|
---
|
|
.../java/org/eclipse/jetty/client/HttpRequest.java | 8 +-
|
|
.../eclipse/jetty/client/HttpClientURITest.java | 45 ++++++++++
|
|
.../main/java/org/eclipse/jetty/http/HttpURI.java | 25 +++++-
|
|
.../java/org/eclipse/jetty/http/HttpURITest.java | 95 ++++++++++++++++++++++
|
|
.../org/eclipse/jetty/proxy/ConnectHandler.java | 2 +-
|
|
.../java/org/eclipse/jetty/server/Request.java | 14 +++-
|
|
.../eclipse/jetty/server/HttpConnectionTest.java | 12 +--
|
|
7 files changed, 187 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
|
|
index f6a453f..2d2afa0 100644
|
|
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
|
|
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
|
|
@@ -177,6 +177,8 @@ public class HttpRequest implements Request
|
|
String rawPath = uri.getRawPath();
|
|
if (rawPath == null)
|
|
rawPath = "";
|
|
+ if (!rawPath.startsWith("/"))
|
|
+ rawPath = "/" + rawPath;
|
|
this.path = rawPath;
|
|
String query = uri.getRawQuery();
|
|
if (query != null)
|
|
@@ -855,14 +857,14 @@ public class HttpRequest implements Request
|
|
return result;
|
|
}
|
|
|
|
- private URI newURI(String uri)
|
|
+ private URI newURI(String path)
|
|
{
|
|
try
|
|
{
|
|
// Handle specially the "OPTIONS *" case, since it is possible to create a URI from "*" (!).
|
|
- if ("*".equals(uri))
|
|
+ if ("*".equals(path))
|
|
return null;
|
|
- URI result = new URI(uri);
|
|
+ URI result = new URI(path);
|
|
return result.isOpaque() ? null : result;
|
|
}
|
|
catch (URISyntaxException x)
|
|
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
|
|
index 9c43512..5a97bdd 100644
|
|
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
|
|
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
|
|
@@ -189,7 +189,7 @@ public class HttpURI
|
|
_uri=uri;
|
|
|
|
if (HttpMethod.CONNECT.is(method))
|
|
- _path=uri;
|
|
+ parse(State.HOST, uri, 0, uri.length());
|
|
else
|
|
parse(uri.startsWith("/")?State.PATH:State.START,uri,0,uri.length());
|
|
}
|
|
@@ -720,17 +720,30 @@ public class HttpURI
|
|
*/
|
|
public void setAuthority(String host, int port)
|
|
{
|
|
+ if (host != null && !isPathValidForAuthority(_path))
|
|
+ throw new IllegalArgumentException("Relative path with authority");
|
|
_host=host;
|
|
_port=port;
|
|
_uri=null;
|
|
}
|
|
|
|
+ private boolean isPathValidForAuthority(String path)
|
|
+ {
|
|
+ if (path == null)
|
|
+ return true;
|
|
+ if (path.isEmpty() || "*".equals(path))
|
|
+ return true;
|
|
+ return path.startsWith("/");
|
|
+ }
|
|
+
|
|
/* ------------------------------------------------------------ */
|
|
/**
|
|
* @param path the path
|
|
*/
|
|
public void setPath(String path)
|
|
{
|
|
+ if (hasAuthority() && !isPathValidForAuthority(path))
|
|
+ throw new IllegalArgumentException("Relative path with authority");
|
|
_uri=null;
|
|
_path=path;
|
|
_decodedPath=null;
|
|
@@ -739,6 +752,8 @@ public class HttpURI
|
|
/* ------------------------------------------------------------ */
|
|
public void setPathQuery(String path)
|
|
{
|
|
+ if (hasAuthority() && !isPathValidForAuthority(path))
|
|
+ throw new IllegalArgumentException("Relative path with authority");
|
|
_uri=null;
|
|
_path=null;
|
|
_decodedPath=null;
|
|
@@ -747,7 +762,13 @@ public class HttpURI
|
|
if (path!=null)
|
|
parse(State.PATH,path,0,path.length());
|
|
}
|
|
-
|
|
+
|
|
+ private boolean hasAuthority()
|
|
+ {
|
|
+ return _host != null;
|
|
+ }
|
|
+
|
|
+
|
|
/* ------------------------------------------------------------ */
|
|
public void setQuery(String query)
|
|
{
|
|
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java
|
|
index 63a58cf..6c3e65f 100644
|
|
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java
|
|
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java
|
|
@@ -32,6 +32,15 @@ import java.nio.charset.StandardCharsets;
|
|
import org.eclipse.jetty.util.MultiMap;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
+import org.junit.jupiter.params.ParameterizedTest;
|
|
+import org.junit.jupiter.params.provider.Arguments;
|
|
+import org.junit.jupiter.params.provider.MethodSource;
|
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
+import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
+import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
+import static org.junit.jupiter.api.Assertions.fail;
|
|
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
|
+
|
|
public class HttpURITest
|
|
{
|
|
@Test
|
|
@@ -100,6 +109,32 @@ public class HttpURITest
|
|
assertThat(uri.getPath(),is("/bar"));
|
|
}
|
|
|
|
+ @Test
|
|
+ public void testCONNECT()
|
|
+ {
|
|
+ HttpURI uri = new HttpURI();
|
|
+
|
|
+ uri.parseRequestTarget("CONNECT", "host:80");
|
|
+ assertThat(uri.getHost(), is("host"));
|
|
+ assertThat(uri.getPort(), is(80));
|
|
+ assertThat(uri.getPath(), nullValue());
|
|
+
|
|
+ uri.parseRequestTarget("CONNECT", "host");
|
|
+ assertThat(uri.getHost(), is("host"));
|
|
+ assertThat(uri.getPort(), is(-1));
|
|
+ assertThat(uri.getPath(), nullValue());
|
|
+
|
|
+ uri.parseRequestTarget("CONNECT", "192.168.0.1:8080");
|
|
+ assertThat(uri.getHost(), is("192.168.0.1"));
|
|
+ assertThat(uri.getPort(), is(8080));
|
|
+ assertThat(uri.getPath(), nullValue());
|
|
+
|
|
+ uri.parseRequestTarget("CONNECT", "[::1]:8080");
|
|
+ assertThat(uri.getHost(), is("[::1]"));
|
|
+ assertThat(uri.getPort(), is(8080));
|
|
+ assertThat(uri.getPath(), nullValue());
|
|
+ }
|
|
+
|
|
@Test
|
|
public void testExtB() throws Exception
|
|
{
|
|
@@ -222,4 +257,64 @@ public class HttpURITest
|
|
assertEquals(uri.getAuthority(), "example.com:8888");
|
|
assertEquals(uri.getUser(), "user:password");
|
|
}
|
|
+
|
|
+ @Test
|
|
+ public void testRelativePathWithAuthority()
|
|
+ {
|
|
+ assertThrows(IllegalArgumentException.class, () ->
|
|
+ {
|
|
+ HttpURI httpURI = new HttpURI();
|
|
+ httpURI.setAuthority("host", 0);
|
|
+ httpURI.setPath("path");
|
|
+ });
|
|
+ assertThrows(IllegalArgumentException.class, () ->
|
|
+ {
|
|
+ HttpURI httpURI = new HttpURI();
|
|
+ httpURI.setAuthority("host", 8080);
|
|
+ httpURI.setPath(";p=v/url");
|
|
+ });
|
|
+ assertThrows(IllegalArgumentException.class, () ->
|
|
+ {
|
|
+ HttpURI httpURI = new HttpURI();
|
|
+ httpURI.setAuthority("host", 0);
|
|
+ httpURI.setPath(";");
|
|
+ });
|
|
+
|
|
+ assertThrows(IllegalArgumentException.class, () ->
|
|
+ {
|
|
+ HttpURI httpURI = new HttpURI();
|
|
+ httpURI.setPath("path");
|
|
+ httpURI.setAuthority("host", 0);
|
|
+ });
|
|
+ assertThrows(IllegalArgumentException.class, () ->
|
|
+ {
|
|
+ HttpURI httpURI = new HttpURI();
|
|
+ httpURI.setPath(";p=v/url");
|
|
+ httpURI.setAuthority("host", 8080);
|
|
+ });
|
|
+ assertThrows(IllegalArgumentException.class, () ->
|
|
+ {
|
|
+ HttpURI httpURI = new HttpURI();
|
|
+ httpURI.setPath(";");
|
|
+ httpURI.setAuthority("host", 0);
|
|
+ });
|
|
+
|
|
+ HttpURI uri = new HttpURI();
|
|
+ uri.setPath("*");
|
|
+ uri.setAuthority("host", 0);
|
|
+ assertEquals("//host*", uri.toString());
|
|
+ uri = new HttpURI();
|
|
+ uri.setAuthority("host", 0);
|
|
+ uri.setPath("*");
|
|
+ assertEquals("//host*", uri.toString());
|
|
+
|
|
+ uri = new HttpURI();
|
|
+ uri.setPath("");
|
|
+ uri.setAuthority("host", 0);
|
|
+ assertEquals("//host", uri.toString());
|
|
+ uri = new HttpURI();
|
|
+ uri.setAuthority("host", 0);
|
|
+ uri.setPath("");
|
|
+ assertEquals("//host", uri.toString());
|
|
+ }
|
|
}
|
|
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
|
|
index 6b7d39e..d3dd5c8 100644
|
|
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
|
|
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
|
|
@@ -197,7 +197,7 @@ public class ConnectHandler extends HandlerWrapper
|
|
{
|
|
if (HttpMethod.CONNECT.is(request.getMethod()))
|
|
{
|
|
- String serverAddress = request.getRequestURI();
|
|
+ String serverAddress = baseRequest.getHttpURI().getAuthority();
|
|
if (LOG.isDebugEnabled())
|
|
LOG.debug("CONNECT request for {}", serverAddress);
|
|
|
|
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
|
|
index b15bcc7..5b996bb 100644
|
|
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
|
|
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
|
|
@@ -1778,9 +1778,19 @@ public class Request implements HttpServletRequest
|
|
|
|
setMethod(request.getMethod());
|
|
HttpURI uri = request.getURI();
|
|
- _originalURI = uri.isAbsolute()&&request.getHttpVersion()!=HttpVersion.HTTP_2?uri.toString():uri.getPathQuery();
|
|
+ String encoded;
|
|
+ if (HttpMethod.CONNECT.is(request.getMethod()))
|
|
+ {
|
|
+ _originalURI = uri.getAuthority();
|
|
+ encoded = "/";
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ _originalURI = uri.isAbsolute() && request.getHttpVersion() != HttpVersion.HTTP_2 ? uri.toString() : uri.getPathQuery();
|
|
+ encoded = uri.getPath();
|
|
+ }
|
|
+
|
|
|
|
- String encoded = uri.getPath();
|
|
String path;
|
|
if (encoded==null)
|
|
{
|
|
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
|
|
index 918a112..22391f7 100644
|
|
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
|
|
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
|
|
@@ -365,7 +365,7 @@ public class HttpConnectionTest
|
|
public void testBadPathDotDotPath() throws Exception
|
|
{
|
|
String response=connector.getResponse("GET /ooops/../../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
|
|
- checkContains(response,0,"HTTP/1.1 400 Bad URI");
|
|
+ checkContains(response,0,"HTTP/1.1 400");
|
|
}
|
|
|
|
@Test
|
|
@@ -380,28 +380,28 @@ public class HttpConnectionTest
|
|
public void testBadPathEncodedDotDotPath() throws Exception
|
|
{
|
|
String response=connector.getResponse("GET /ooops/%2e%2e/%2e%2e/path HTTP/1.0\r\nHost: localhost:80\r\n\n");
|
|
- checkContains(response,0,"HTTP/1.1 400 Bad URI");
|
|
+ checkContains(response,0,"HTTP/1.1 400");
|
|
}
|
|
|
|
@Test
|
|
public void testBadDotDotPath() throws Exception
|
|
{
|
|
String response=connector.getResponse("GET ../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
|
|
- checkContains(response,0,"HTTP/1.1 400 Bad URI");
|
|
+ checkContains(response,0,"HTTP/1.1 400");
|
|
}
|
|
|
|
@Test
|
|
public void testBadSlashDotDotPath() throws Exception
|
|
{
|
|
String response=connector.getResponse("GET /../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
|
|
- checkContains(response,0,"HTTP/1.1 400 Bad URI");
|
|
+ checkContains(response,0,"HTTP/1.1 400");
|
|
}
|
|
|
|
@Test
|
|
public void testEncodedBadDotDotPath() throws Exception
|
|
{
|
|
String response=connector.getResponse("GET %2e%2e/path HTTP/1.0\r\nHost: localhost:80\r\n\n");
|
|
- checkContains(response,0,"HTTP/1.1 400 Bad URI");
|
|
+ checkContains(response,0,"HTTP/1.1 400");
|
|
}
|
|
|
|
@Test
|
|
@@ -1168,7 +1168,7 @@ public class HttpConnectionTest
|
|
"12345\r\n"+
|
|
"0;\r\n" +
|
|
"\r\n");
|
|
- checkContains(response,offset,"HTTP/1.1 400 Bad Request");
|
|
+ checkContains(response,offset,"HTTP/1.1 400");
|
|
}
|
|
catch (Exception e)
|
|
{
|