Fix CVE-2017-12196,CVE-2019-10184 and CVE-2019-10212
(cherry picked from commit 329cb3a1e2aa3f6f9845ab228d2034edbb7a53f4)
This commit is contained in:
parent
83c7f19aa3
commit
de7631dc7d
151
CVE-2017-12196-1.patch
Normal file
151
CVE-2017-12196-1.patch
Normal file
@ -0,0 +1,151 @@
|
||||
From facb33a5cedaf4b7b96d3840a08210370a806870 Mon Sep 17 00:00:00 2001
|
||||
From: Stuart Douglas <stuart.w.douglas@gmail.com>
|
||||
Date: Tue, 3 Oct 2017 13:29:48 +0200
|
||||
Subject: [PATCH] UNDERTOW-1190 client can use bogus uri in digest
|
||||
authentication
|
||||
|
||||
---
|
||||
.../impl/DigestAuthenticationMechanism.java | 19 +++++++-
|
||||
.../DigestAuthenticationAuthTestCase.java | 45 ++++++++++++++++++-
|
||||
.../security/digest/DigestAuthTestCase.java | 7 +--
|
||||
3 files changed, 65 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/core/src/main/java/io/undertow/security/impl/DigestAuthenticationMechanism.java b/core/src/main/java/io/undertow/security/impl/DigestAuthenticationMechanism.java
|
||||
index e5a75bd834..e01724b44b 100644
|
||||
--- a/core/src/main/java/io/undertow/security/impl/DigestAuthenticationMechanism.java
|
||||
+++ b/core/src/main/java/io/undertow/security/impl/DigestAuthenticationMechanism.java
|
||||
@@ -42,6 +42,7 @@
|
||||
import io.undertow.util.HeaderMap;
|
||||
import io.undertow.util.Headers;
|
||||
import io.undertow.util.HexConverter;
|
||||
+import io.undertow.util.StatusCodes;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
@@ -181,7 +182,7 @@ public AuthenticationMechanismOutcome authenticate(final HttpServerExchange exch
|
||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
||||
}
|
||||
|
||||
- public AuthenticationMechanismOutcome handleDigestHeader(HttpServerExchange exchange, final SecurityContext securityContext) {
|
||||
+ private AuthenticationMechanismOutcome handleDigestHeader(HttpServerExchange exchange, final SecurityContext securityContext) {
|
||||
DigestContext context = exchange.getAttachment(DigestContext.ATTACHMENT_KEY);
|
||||
Map<DigestAuthorizationToken, String> parsedHeader = context.getParsedHeader();
|
||||
// Step 1 - Verify the set of tokens received to ensure valid values.
|
||||
@@ -231,7 +232,21 @@ public AuthenticationMechanismOutcome handleDigestHeader(HttpServerExchange exch
|
||||
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
}
|
||||
|
||||
- // TODO - Validate the URI
|
||||
+ if(parsedHeader.containsKey(DigestAuthorizationToken.DIGEST_URI)) {
|
||||
+ String uri = parsedHeader.get(DigestAuthorizationToken.DIGEST_URI);
|
||||
+ String requestURI = exchange.getRequestURI();
|
||||
+ if(!exchange.getQueryString().isEmpty()) {
|
||||
+ requestURI = requestURI + "?" + exchange.getQueryString();
|
||||
+ }
|
||||
+ if(!uri.equals(requestURI)) {
|
||||
+ //just end the auth process
|
||||
+ exchange.setStatusCode(StatusCodes.BAD_REQUEST);
|
||||
+ exchange.endExchange();
|
||||
+ return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
+ }
|
||||
+ } else {
|
||||
+ return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
+ }
|
||||
|
||||
if (parsedHeader.containsKey(DigestAuthorizationToken.OPAQUE)) {
|
||||
if (!OPAQUE_VALUE.equals(parsedHeader.get(DigestAuthorizationToken.OPAQUE))) {
|
||||
diff --git a/core/src/test/java/io/undertow/server/security/DigestAuthenticationAuthTestCase.java b/core/src/test/java/io/undertow/server/security/DigestAuthenticationAuthTestCase.java
|
||||
index f9fa2b6aa4..d1aff564e1 100644
|
||||
--- a/core/src/test/java/io/undertow/server/security/DigestAuthenticationAuthTestCase.java
|
||||
+++ b/core/src/test/java/io/undertow/server/security/DigestAuthenticationAuthTestCase.java
|
||||
@@ -163,7 +163,7 @@ private static String createAuthorizationLine(final String userName, final Strin
|
||||
sb.append(DigestAuthorizationToken.USERNAME.getName()).append("=").append("\"userOne\"").append(",");
|
||||
sb.append(DigestAuthorizationToken.REALM.getName()).append("=\"").append(REALM_NAME).append("\",");
|
||||
sb.append(DigestAuthorizationToken.NONCE.getName()).append("=\"").append(nonce).append("\",");
|
||||
- sb.append(DigestAuthorizationToken.DIGEST_URI.getName()).append("=\"/\",");
|
||||
+ sb.append(DigestAuthorizationToken.DIGEST_URI.getName()).append("=\"" + uri + "\",");
|
||||
String nonceCountHex = toHex(nonceCount);
|
||||
String response = createResponse(userName, REALM_NAME, password, method, uri, nonce, nonceCountHex, cnonce);
|
||||
sb.append(DigestAuthorizationToken.RESPONSE.getName()).append("=\"").append(response).append("\",");
|
||||
@@ -243,6 +243,49 @@ static void _testDigestSuccess() throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * Test for a successful authentication.
|
||||
+ *
|
||||
+ * Also makes two additional calls to demonstrate nonce re-use with an incrementing nonce count.
|
||||
+ */
|
||||
+ @Test
|
||||
+ public void testDigestBadUri() throws Exception {
|
||||
+ _testDigestBadUri();
|
||||
+ }
|
||||
+
|
||||
+ static void _testDigestBadUri() throws Exception {
|
||||
+ TestHttpClient client = new TestHttpClient();
|
||||
+ HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL());
|
||||
+ HttpResponse result = client.execute(get);
|
||||
+ assertEquals(StatusCodes.UNAUTHORIZED, result.getStatusLine().getStatusCode());
|
||||
+ Header[] values = result.getHeaders(WWW_AUTHENTICATE.toString());
|
||||
+ String value = getAuthHeader(DIGEST, values);
|
||||
+
|
||||
+ Map<DigestWWWAuthenticateToken, String> parsedHeader = DigestWWWAuthenticateToken.parseHeader(value.substring(7));
|
||||
+ assertEquals(REALM_NAME, parsedHeader.get(DigestWWWAuthenticateToken.REALM));
|
||||
+ assertEquals(DigestAlgorithm.MD5.getToken(), parsedHeader.get(DigestWWWAuthenticateToken.ALGORITHM));
|
||||
+ assertEquals(DigestQop.AUTH.getToken(), parsedHeader.get(DigestWWWAuthenticateToken.MESSAGE_QOP));
|
||||
+
|
||||
+ String clientNonce = createNonce();
|
||||
+ int nonceCount = 1;
|
||||
+ String nonce = parsedHeader.get(DigestWWWAuthenticateToken.NONCE);
|
||||
+ String opaque = parsedHeader.get(DigestWWWAuthenticateToken.OPAQUE);
|
||||
+ assertNotNull(opaque);
|
||||
+ // Send 5 requests with an incrementing nonce count on each call.
|
||||
+ for (int i = 0; i < 5; i++) {
|
||||
+ client = new TestHttpClient();
|
||||
+ get = new HttpGet(DefaultServer.getDefaultServerURL());
|
||||
+
|
||||
+ int thisNonceCount = nonceCount++;
|
||||
+ String authorization = createAuthorizationLine("userOne", "passwordOne", "GET", "/badUri", nonce, thisNonceCount,
|
||||
+ clientNonce, opaque);
|
||||
+
|
||||
+ get.addHeader(AUTHORIZATION.toString(), authorization);
|
||||
+ result = client.execute(get);
|
||||
+ assertEquals(StatusCodes.BAD_REQUEST, result.getStatusLine().getStatusCode());
|
||||
+
|
||||
+ }
|
||||
+ }
|
||||
/**
|
||||
* Test for a failed authentication where a bad username is provided.
|
||||
*/
|
||||
diff --git a/servlet/src/test/java/io/undertow/servlet/test/security/digest/DigestAuthTestCase.java b/servlet/src/test/java/io/undertow/servlet/test/security/digest/DigestAuthTestCase.java
|
||||
index d2ac85e0c6..7164634c47 100644
|
||||
--- a/servlet/src/test/java/io/undertow/servlet/test/security/digest/DigestAuthTestCase.java
|
||||
+++ b/servlet/src/test/java/io/undertow/servlet/test/security/digest/DigestAuthTestCase.java
|
||||
@@ -119,7 +119,8 @@ public void testAuthType() throws Exception {
|
||||
|
||||
public void testCall(final String path, final String expectedResponse) throws Exception {
|
||||
TestHttpClient client = new TestHttpClient();
|
||||
- String url = DefaultServer.getDefaultServerURL() + "/servletContext/secured/" + path;
|
||||
+ String servletPath = "/servletContext/secured/" + path;
|
||||
+ String url = DefaultServer.getDefaultServerURL() + servletPath;
|
||||
HttpGet get = new HttpGet(url);
|
||||
HttpResponse result = client.execute(get);
|
||||
assertEquals(StatusCodes.UNAUTHORIZED, result.getStatusLine().getStatusCode());
|
||||
@@ -134,7 +135,7 @@ public void testCall(final String path, final String expectedResponse) throws Ex
|
||||
|
||||
String nonce = parsedHeader.get(DigestWWWAuthenticateToken.NONCE);
|
||||
|
||||
- String clientResponse = createResponse("user1", REALM_NAME, "password1", "GET", "/", nonce);
|
||||
+ String clientResponse = createResponse("user1", REALM_NAME, "password1", "GET", servletPath, nonce);
|
||||
|
||||
client = new TestHttpClient();
|
||||
get = new HttpGet(url);
|
||||
@@ -143,7 +144,7 @@ public void testCall(final String path, final String expectedResponse) throws Ex
|
||||
sb.append(DigestAuthorizationToken.USERNAME.getName()).append("=").append("\"user1\"").append(",");
|
||||
sb.append(DigestAuthorizationToken.REALM.getName()).append("=\"").append(REALM_NAME).append("\",");
|
||||
sb.append(DigestAuthorizationToken.NONCE.getName()).append("=\"").append(nonce).append("\",");
|
||||
- sb.append(DigestAuthorizationToken.DIGEST_URI.getName()).append("=\"/\",");
|
||||
+ sb.append(DigestAuthorizationToken.DIGEST_URI.getName()).append("=\"" + servletPath + "\",");
|
||||
sb.append(DigestAuthorizationToken.RESPONSE.getName()).append("=\"").append(clientResponse).append("\"");
|
||||
|
||||
get.addHeader(AUTHORIZATION.toString(), sb.toString());
|
||||
38
CVE-2017-12196-2.patch
Normal file
38
CVE-2017-12196-2.patch
Normal file
@ -0,0 +1,38 @@
|
||||
From 8804170ce3186bdd83b486959399ec7ac0f59d0f Mon Sep 17 00:00:00 2001
|
||||
From: Stuart Douglas <stuart.w.douglas@gmail.com>
|
||||
Date: Mon, 11 Dec 2017 10:51:51 +1100
|
||||
Subject: [PATCH] UNDERTOW-1190 handle absolute URI in the digest mechanism
|
||||
|
||||
---
|
||||
.../impl/DigestAuthenticationMechanism.java | 18 ++++++++++++++----
|
||||
1 file changed, 14 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/core/src/main/java/io/undertow/security/impl/DigestAuthenticationMechanism.java b/core/src/main/java/io/undertow/security/impl/DigestAuthenticationMechanism.java
|
||||
index e01724b44b..972a0cb0e4 100644
|
||||
--- a/core/src/main/java/io/undertow/security/impl/DigestAuthenticationMechanism.java
|
||||
+++ b/core/src/main/java/io/undertow/security/impl/DigestAuthenticationMechanism.java
|
||||
@@ -239,10 +239,20 @@ private AuthenticationMechanismOutcome handleDigestHeader(HttpServerExchange exc
|
||||
requestURI = requestURI + "?" + exchange.getQueryString();
|
||||
}
|
||||
if(!uri.equals(requestURI)) {
|
||||
- //just end the auth process
|
||||
- exchange.setStatusCode(StatusCodes.BAD_REQUEST);
|
||||
- exchange.endExchange();
|
||||
- return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
+ //it is possible we were given an absolute URI
|
||||
+ //we reconstruct the URI from the host header to make sure they match up
|
||||
+ //I am not sure if this is overly strict, however I think it is better
|
||||
+ //to be safe than sorry
|
||||
+ requestURI = exchange.getRequestURL();
|
||||
+ if(!exchange.getQueryString().isEmpty()) {
|
||||
+ requestURI = requestURI + "?" + exchange.getQueryString();
|
||||
+ }
|
||||
+ if(!uri.equals(requestURI)) {
|
||||
+ //just end the auth process
|
||||
+ exchange.setStatusCode(StatusCodes.BAD_REQUEST);
|
||||
+ exchange.endExchange();
|
||||
+ return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
+ }
|
||||
}
|
||||
} else {
|
||||
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
23
CVE-2019-10184-pre.patch
Normal file
23
CVE-2019-10184-pre.patch
Normal file
@ -0,0 +1,23 @@
|
||||
From 16833ca1ea7e2235e40129078f94f935b3f7e446 Mon Sep 17 00:00:00 2001
|
||||
From: Stuart Douglas <stuart.w.douglas@gmail.com>
|
||||
Date: Thu, 15 Sep 2016 09:50:02 +1000
|
||||
Subject: [PATCH] UNDERTOW-837 Incorrect welcome file rewrite outside of the
|
||||
root directory
|
||||
|
||||
---
|
||||
.../io/undertow/servlet/handlers/ServletInitialHandler.java | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/servlet/src/main/java/io/undertow/servlet/handlers/ServletInitialHandler.java b/servlet/src/main/java/io/undertow/servlet/handlers/ServletInitialHandler.java
|
||||
index 3873ea6c32..0e8e3ecb15 100644
|
||||
--- a/servlet/src/main/java/io/undertow/servlet/handlers/ServletInitialHandler.java
|
||||
+++ b/servlet/src/main/java/io/undertow/servlet/handlers/ServletInitialHandler.java
|
||||
@@ -170,7 +170,7 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
|
||||
//this can only happen if the path ends with a /
|
||||
//otherwise there would be a redirect instead
|
||||
exchange.setRelativePath(info.getRewriteLocation());
|
||||
- exchange.setRequestPath(exchange.getRequestPath() + info.getRewriteLocation());
|
||||
+ exchange.setRequestPath(exchange.getResolvedPath() + info.getRewriteLocation());
|
||||
}
|
||||
|
||||
final HttpServletResponseImpl response = new HttpServletResponseImpl(exchange, servletContext);
|
||||
345
CVE-2019-10184.patch
Normal file
345
CVE-2019-10184.patch
Normal file
@ -0,0 +1,345 @@
|
||||
From d2715e3afa13f50deaa19643676816ce391551e9 Mon Sep 17 00:00:00 2001
|
||||
From: Lin Gao <aoingl@gmail.com>
|
||||
Date: Wed, 24 Jul 2019 22:03:01 +0800
|
||||
Subject: [PATCH] [UNDERTOW-1578] 401 Unauthorized should be returned when
|
||||
requesting a protected directory without trailing slash
|
||||
|
||||
---
|
||||
.../servlet/core/DeploymentManagerImpl.java | 2 +
|
||||
.../servlet/handlers/RedirectDirHandler.java | 71 ++++++++
|
||||
.../handlers/ServletInitialHandler.java | 29 +--
|
||||
.../SecurityRedirectTestCase.java | 168 ++++++++++++++++++
|
||||
4 files changed, 244 insertions(+), 26 deletions(-)
|
||||
create mode 100644 servlet/src/main/java/io/undertow/servlet/handlers/RedirectDirHandler.java
|
||||
create mode 100644 servlet/src/test/java/io/undertow/servlet/test/defaultservlet/SecurityRedirectTestCase.java
|
||||
|
||||
diff --git a/servlet/src/main/java/io/undertow/servlet/core/DeploymentManagerImpl.java b/servlet/src/main/java/io/undertow/servlet/core/DeploymentManagerImpl.java
|
||||
index 1906c4eaa0..9c49c4ad53 100644
|
||||
--- a/servlet/src/main/java/io/undertow/servlet/core/DeploymentManagerImpl.java
|
||||
+++ b/servlet/src/main/java/io/undertow/servlet/core/DeploymentManagerImpl.java
|
||||
@@ -71,6 +71,7 @@
|
||||
import io.undertow.servlet.api.ThreadSetupHandler;
|
||||
import io.undertow.servlet.api.WebResourceCollection;
|
||||
import io.undertow.servlet.handlers.CrawlerSessionManagerHandler;
|
||||
+import io.undertow.servlet.handlers.RedirectDirHandler;
|
||||
import io.undertow.servlet.handlers.ServletDispatchingHandler;
|
||||
import io.undertow.servlet.handlers.ServletHandler;
|
||||
import io.undertow.servlet.handlers.ServletInitialHandler;
|
||||
@@ -218,6 +219,7 @@ public Void call(HttpServerExchange exchange, Object ignore) throws Exception {
|
||||
|
||||
HttpHandler wrappedHandlers = ServletDispatchingHandler.INSTANCE;
|
||||
wrappedHandlers = wrapHandlers(wrappedHandlers, deploymentInfo.getInnerHandlerChainWrappers());
|
||||
+ wrappedHandlers = new RedirectDirHandler(wrappedHandlers, deployment.getServletPaths());
|
||||
if(!deploymentInfo.isSecurityDisabled()) {
|
||||
HttpHandler securityHandler = setupSecurityHandlers(wrappedHandlers);
|
||||
wrappedHandlers = new PredicateHandler(DispatcherTypePredicate.REQUEST, securityHandler, wrappedHandlers);
|
||||
diff --git a/servlet/src/main/java/io/undertow/servlet/handlers/RedirectDirHandler.java b/servlet/src/main/java/io/undertow/servlet/handlers/RedirectDirHandler.java
|
||||
new file mode 100644
|
||||
index 0000000000..576eb11a87
|
||||
--- /dev/null
|
||||
+++ b/servlet/src/main/java/io/undertow/servlet/handlers/RedirectDirHandler.java
|
||||
@@ -0,0 +1,71 @@
|
||||
+/*
|
||||
+ * JBoss, Home of Professional Open Source.
|
||||
+ * Copyright 2019 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.servlet.handlers;
|
||||
+
|
||||
+import io.undertow.server.HttpHandler;
|
||||
+import io.undertow.server.HttpServerExchange;
|
||||
+import io.undertow.util.Headers;
|
||||
+import io.undertow.util.Methods;
|
||||
+import io.undertow.util.RedirectBuilder;
|
||||
+import io.undertow.util.StatusCodes;
|
||||
+
|
||||
+/**
|
||||
+ * Handler that redirects the directory requests without trailing slash to the one append trailing slash.
|
||||
+ *
|
||||
+ * @author Lin Gao
|
||||
+ */
|
||||
+public class RedirectDirHandler implements HttpHandler {
|
||||
+
|
||||
+ private static final String HTTP2_UPGRADE_PREFIX = "h2";
|
||||
+
|
||||
+ private final HttpHandler next;
|
||||
+ private final ServletPathMatches paths;
|
||||
+
|
||||
+ public RedirectDirHandler(HttpHandler next, ServletPathMatches paths) {
|
||||
+ this.next = next;
|
||||
+ this.paths = paths;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
+ final String path = exchange.getRelativePath();
|
||||
+ final ServletPathMatch info = paths.getServletHandlerByPath(path);
|
||||
+ // https://issues.jboss.org/browse/WFLY-3439
|
||||
+ // if the request is an upgrade request then we don't want to redirect
|
||||
+ // as there is a good chance the web socket client won't understand the redirect
|
||||
+ // we make an exception for HTTP2 upgrade requests, as this would have already be handled at
|
||||
+ // the connector level if it was going to be handled.
|
||||
+ String upgradeString = exchange.getRequestHeaders().getFirst(Headers.UPGRADE);
|
||||
+ boolean isUpgradeRequest = upgradeString != null && !upgradeString.startsWith(HTTP2_UPGRADE_PREFIX);
|
||||
+ if (info.getType() == ServletPathMatch.Type.REDIRECT && !isUpgradeRequest) {
|
||||
+ // UNDERTOW-89
|
||||
+ // we redirect on GET requests to the root context to add an / to the end
|
||||
+ if (exchange.getRequestMethod().equals(Methods.GET) || exchange.getRequestMethod().equals(Methods.HEAD)) {
|
||||
+ exchange.setStatusCode(StatusCodes.FOUND);
|
||||
+ } else {
|
||||
+ exchange.setStatusCode(StatusCodes.TEMPORARY_REDIRECT);
|
||||
+ }
|
||||
+ exchange.getResponseHeaders().put(Headers.LOCATION,
|
||||
+ RedirectBuilder.redirect(exchange, exchange.getRelativePath() + "/", true));
|
||||
+ return;
|
||||
+ }
|
||||
+ next.handleRequest(exchange);
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
diff --git a/servlet/src/main/java/io/undertow/servlet/handlers/ServletInitialHandler.java b/servlet/src/main/java/io/undertow/servlet/handlers/ServletInitialHandler.java
|
||||
index 82a3ad7360..7edc7e493c 100644
|
||||
--- a/servlet/src/main/java/io/undertow/servlet/handlers/ServletInitialHandler.java
|
||||
+++ b/servlet/src/main/java/io/undertow/servlet/handlers/ServletInitialHandler.java
|
||||
@@ -39,11 +39,8 @@
|
||||
import io.undertow.servlet.spec.HttpServletResponseImpl;
|
||||
import io.undertow.servlet.spec.RequestDispatcherImpl;
|
||||
import io.undertow.servlet.spec.ServletContextImpl;
|
||||
-import io.undertow.util.Headers;
|
||||
import io.undertow.util.HttpString;
|
||||
-import io.undertow.util.Methods;
|
||||
import io.undertow.util.Protocols;
|
||||
-import io.undertow.util.RedirectBuilder;
|
||||
import io.undertow.util.StatusCodes;
|
||||
import org.xnio.ChannelListener;
|
||||
import org.xnio.Option;
|
||||
@@ -80,8 +77,6 @@
|
||||
*/
|
||||
public class ServletInitialHandler implements HttpHandler, ServletDispatcher {
|
||||
|
||||
- private static final String HTTP2_UPGRADE_PREFIX = "h2";
|
||||
-
|
||||
private static final RuntimePermission PERMISSION = new RuntimePermission("io.undertow.servlet.CREATE_INITIAL_HANDLER");
|
||||
|
||||
private final HttpHandler next;
|
||||
@@ -149,30 +144,12 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
|
||||
return;
|
||||
}
|
||||
final ServletPathMatch info = paths.getServletHandlerByPath(path);
|
||||
- //https://issues.jboss.org/browse/WFLY-3439
|
||||
- //if the request is an upgrade request then we don't want to redirect
|
||||
- //as there is a good chance the web socket client won't understand the redirect
|
||||
- //we make an exception for HTTP2 upgrade requests, as this would have already be handled at
|
||||
- //the connector level if it was going to be handled.
|
||||
- String upgradeString = exchange.getRequestHeaders().getFirst(Headers.UPGRADE);
|
||||
- boolean isUpgradeRequest = upgradeString != null && !upgradeString.startsWith(HTTP2_UPGRADE_PREFIX);
|
||||
- if (info.getType() == ServletPathMatch.Type.REDIRECT && !isUpgradeRequest) {
|
||||
- //UNDERTOW-89
|
||||
- //we redirect on GET requests to the root context to add an / to the end
|
||||
- if(exchange.getRequestMethod().equals(Methods.GET) || exchange.getRequestMethod().equals(Methods.HEAD)) {
|
||||
- exchange.setStatusCode(StatusCodes.FOUND);
|
||||
- } else {
|
||||
- exchange.setStatusCode(StatusCodes.TEMPORARY_REDIRECT);
|
||||
- }
|
||||
- exchange.getResponseHeaders().put(Headers.LOCATION, RedirectBuilder.redirect(exchange, exchange.getRelativePath() + "/", true));
|
||||
- return;
|
||||
- } else if (info.getType() == ServletPathMatch.Type.REWRITE) {
|
||||
- //this can only happen if the path ends with a /
|
||||
- //otherwise there would be a redirect instead
|
||||
+ if (info.getType() == ServletPathMatch.Type.REWRITE) {
|
||||
+ // this can only happen if the path ends with a /
|
||||
+ // otherwise there would be a redirect instead
|
||||
exchange.setRelativePath(info.getRewriteLocation());
|
||||
exchange.setRequestPath(exchange.getResolvedPath() + info.getRewriteLocation());
|
||||
}
|
||||
-
|
||||
final HttpServletResponseImpl response = new HttpServletResponseImpl(exchange, servletContext);
|
||||
final HttpServletRequestImpl request = new HttpServletRequestImpl(exchange, servletContext);
|
||||
final ServletRequestContext servletRequestContext = new ServletRequestContext(servletContext.getDeployment(), request, response, info);
|
||||
diff --git a/servlet/src/test/java/io/undertow/servlet/test/defaultservlet/SecurityRedirectTestCase.java b/servlet/src/test/java/io/undertow/servlet/test/defaultservlet/SecurityRedirectTestCase.java
|
||||
new file mode 100644
|
||||
index 0000000000..8fa9323177
|
||||
--- /dev/null
|
||||
+++ b/servlet/src/test/java/io/undertow/servlet/test/defaultservlet/SecurityRedirectTestCase.java
|
||||
@@ -0,0 +1,168 @@
|
||||
+/*
|
||||
+ * JBoss, Home of Professional Open Source.
|
||||
+ * Copyright 2019 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.servlet.test.defaultservlet;
|
||||
+
|
||||
+import static io.undertow.util.Headers.AUTHORIZATION;
|
||||
+import static io.undertow.util.Headers.BASIC;
|
||||
+import static io.undertow.util.Headers.LOCATION;
|
||||
+import static io.undertow.util.Headers.WWW_AUTHENTICATE;
|
||||
+import static org.junit.Assert.assertEquals;
|
||||
+
|
||||
+import java.io.IOException;
|
||||
+
|
||||
+import javax.servlet.ServletException;
|
||||
+
|
||||
+import org.apache.http.Header;
|
||||
+import org.apache.http.HttpResponse;
|
||||
+import org.apache.http.client.HttpClient;
|
||||
+import org.apache.http.client.methods.HttpGet;
|
||||
+import org.apache.http.impl.client.HttpClientBuilder;
|
||||
+import org.junit.Assert;
|
||||
+import org.junit.BeforeClass;
|
||||
+import org.junit.Test;
|
||||
+import org.junit.runner.RunWith;
|
||||
+
|
||||
+import io.undertow.server.handlers.PathHandler;
|
||||
+import io.undertow.servlet.api.DeploymentInfo;
|
||||
+import io.undertow.servlet.api.DeploymentManager;
|
||||
+import io.undertow.servlet.api.LoginConfig;
|
||||
+import io.undertow.servlet.api.SecurityConstraint;
|
||||
+import io.undertow.servlet.api.ServletContainer;
|
||||
+import io.undertow.servlet.api.WebResourceCollection;
|
||||
+import io.undertow.servlet.test.path.ServletPathMappingTestCase;
|
||||
+import io.undertow.servlet.test.security.constraint.ServletIdentityManager;
|
||||
+import io.undertow.servlet.test.util.TestClassIntrospector;
|
||||
+import io.undertow.servlet.test.util.TestResourceLoader;
|
||||
+import io.undertow.testutils.DefaultServer;
|
||||
+import io.undertow.testutils.HttpClientUtils;
|
||||
+import io.undertow.util.FlexBase64;
|
||||
+import io.undertow.util.StatusCodes;
|
||||
+
|
||||
+/**
|
||||
+ * TestCase on redirect with or without trailing slash when requesting protected path.
|
||||
+ *
|
||||
+ * @author Lin Gao
|
||||
+ */
|
||||
+@RunWith(DefaultServer.class)
|
||||
+public class SecurityRedirectTestCase {
|
||||
+
|
||||
+ @BeforeClass
|
||||
+ public static void setup() throws ServletException {
|
||||
+
|
||||
+ final PathHandler root = new PathHandler();
|
||||
+ final ServletContainer container = ServletContainer.Factory.newInstance();
|
||||
+
|
||||
+ ServletIdentityManager identityManager = new ServletIdentityManager();
|
||||
+ identityManager.addUser("user1", "password1", "role1");
|
||||
+
|
||||
+ DeploymentInfo builder = new DeploymentInfo()
|
||||
+ .setClassIntrospecter(TestClassIntrospector.INSTANCE)
|
||||
+ .setClassLoader(ServletPathMappingTestCase.class.getClassLoader())
|
||||
+ .setContextPath("/servletContext")
|
||||
+ .setDeploymentName("servletContext.war")
|
||||
+ .setResourceManager(new TestResourceLoader(SecurityRedirectTestCase.class))
|
||||
+ .addWelcomePages("index.html")
|
||||
+ .setIdentityManager(identityManager)
|
||||
+ .setLoginConfig(new LoginConfig("BASIC", "Test Realm"))
|
||||
+ .addSecurityConstraint(new SecurityConstraint()
|
||||
+ .addRoleAllowed("role1")
|
||||
+ .addWebResourceCollection(new WebResourceCollection()
|
||||
+ .addUrlPatterns("/index.html", "/filterpath/*")));
|
||||
+
|
||||
+ DeploymentManager manager = container.addDeployment(builder);
|
||||
+ manager.deploy();
|
||||
+ root.addPrefixPath(builder.getContextPath(), manager.start());
|
||||
+ DefaultServer.setRootHandler(root);
|
||||
+ }
|
||||
+
|
||||
+ @SuppressWarnings("deprecation")
|
||||
+ @Test
|
||||
+ public void testSecurityWithWelcomeFileRedirect() throws IOException {
|
||||
+ // disable following redirect
|
||||
+ HttpClient client = HttpClientBuilder.create().disableRedirectHandling().build();
|
||||
+ try {
|
||||
+ HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext");
|
||||
+ HttpResponse result = client.execute(get);
|
||||
+ Assert.assertEquals(StatusCodes.FOUND, result.getStatusLine().getStatusCode());
|
||||
+ Header[] values = result.getHeaders(LOCATION.toString());
|
||||
+ assertEquals(1, values.length);
|
||||
+ assertEquals(DefaultServer.getDefaultServerURL() + "/servletContext/", values[0].getValue());
|
||||
+ HttpClientUtils.readResponse(result);
|
||||
+
|
||||
+ get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/");
|
||||
+ result = client.execute(get);
|
||||
+ Assert.assertEquals(StatusCodes.UNAUTHORIZED, result.getStatusLine().getStatusCode());
|
||||
+
|
||||
+ values = result.getHeaders(WWW_AUTHENTICATE.toString());
|
||||
+ assertEquals(1, values.length);
|
||||
+ assertEquals(BASIC + " realm=\"Test Realm\"", values[0].getValue());
|
||||
+ HttpClientUtils.readResponse(result);
|
||||
+
|
||||
+ get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/");
|
||||
+ get.addHeader(AUTHORIZATION.toString(), BASIC + " " + FlexBase64.encodeString("user1:password1".getBytes(), false));
|
||||
+ result = client.execute(get);
|
||||
+ String response = HttpClientUtils.readResponse(result);
|
||||
+ Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
|
||||
+ Assert.assertTrue(response.contains("Redirected home page"));
|
||||
+ } finally {
|
||||
+ client.getConnectionManager().shutdown();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @SuppressWarnings("deprecation")
|
||||
+ @Test
|
||||
+ public void testSecurityWithoutWelcomeFileRedirect() throws IOException {
|
||||
+ // disable following redirect
|
||||
+ HttpClient client = HttpClientBuilder.create().disableRedirectHandling().build();
|
||||
+ try {
|
||||
+ HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/filterpath");
|
||||
+ HttpResponse result = client.execute(get);
|
||||
+ Assert.assertEquals(StatusCodes.UNAUTHORIZED, result.getStatusLine().getStatusCode());
|
||||
+ HttpClientUtils.readResponse(result);
|
||||
+
|
||||
+ get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/filterpath/");
|
||||
+ result = client.execute(get);
|
||||
+ Assert.assertEquals(StatusCodes.UNAUTHORIZED, result.getStatusLine().getStatusCode());
|
||||
+
|
||||
+ Header[] values = result.getHeaders(WWW_AUTHENTICATE.toString());
|
||||
+ assertEquals(1, values.length);
|
||||
+ assertEquals(BASIC + " realm=\"Test Realm\"", values[0].getValue());
|
||||
+ HttpClientUtils.readResponse(result);
|
||||
+
|
||||
+ get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/filterpath");
|
||||
+ get.addHeader(AUTHORIZATION.toString(), BASIC + " " + FlexBase64.encodeString("user1:password1".getBytes(), false));
|
||||
+ result = client.execute(get);
|
||||
+ Assert.assertEquals(StatusCodes.FOUND, result.getStatusLine().getStatusCode());
|
||||
+ values = result.getHeaders(LOCATION.toString());
|
||||
+ assertEquals(1, values.length);
|
||||
+ assertEquals(DefaultServer.getDefaultServerURL() + "/servletContext/filterpath/", values[0].getValue());
|
||||
+ HttpClientUtils.readResponse(result);
|
||||
+
|
||||
+ get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/filterpath/filtered.txt");
|
||||
+ get.addHeader(AUTHORIZATION.toString(), BASIC + " " + FlexBase64.encodeString("user1:password1".getBytes(), false));
|
||||
+ result = client.execute(get);
|
||||
+ String response = HttpClientUtils.readResponse(result);
|
||||
+ Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
|
||||
+ Assert.assertTrue(response.equals("Stuart"));
|
||||
+ } finally {
|
||||
+ client.getConnectionManager().shutdown();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
26
CVE-2019-10212.patch
Normal file
26
CVE-2019-10212.patch
Normal file
@ -0,0 +1,26 @@
|
||||
From 8b63e258502f9f55b33b2e0b02a2e24cf5d2f1c1 Mon Sep 17 00:00:00 2001
|
||||
From: Paramvir Jindal <pjindal@pjindal.pnq.csb>
|
||||
Date: Fri, 11 Oct 2019 11:51:22 +0530
|
||||
Subject: [PATCH] UNDERTOW-1576: BASIC auth password is output as plain text at
|
||||
DEBUG level logging in BasicAuthenticationMechanism
|
||||
|
||||
---
|
||||
.../undertow/security/impl/BasicAuthenticationMechanism.java | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/core/src/main/java/io/undertow/security/impl/BasicAuthenticationMechanism.java b/core/src/main/java/io/undertow/security/impl/BasicAuthenticationMechanism.java
|
||||
index 7042e8ff66..94e786427e 100644
|
||||
--- a/core/src/main/java/io/undertow/security/impl/BasicAuthenticationMechanism.java
|
||||
+++ b/core/src/main/java/io/undertow/security/impl/BasicAuthenticationMechanism.java
|
||||
@@ -151,9 +151,9 @@ public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange,
|
||||
}
|
||||
|
||||
plainChallenge = new String(decode.array(), decode.arrayOffset(), decode.limit(), charset);
|
||||
- UndertowLogger.SECURITY_LOGGER.debugf("Found basic auth header %s (decoded using charset %s) in %s", plainChallenge, charset, exchange);
|
||||
+ UndertowLogger.SECURITY_LOGGER.debugf("Found basic auth header (decoded using charset %s) in %s", charset, exchange);
|
||||
} catch (IOException e) {
|
||||
- UndertowLogger.SECURITY_LOGGER.debugf(e, "Failed to decode basic auth header %s in %s", base64Challenge, exchange);
|
||||
+ UndertowLogger.SECURITY_LOGGER.debugf(e, "Failed to decode basic auth header in %s", exchange);
|
||||
}
|
||||
int colonPos;
|
||||
if (plainChallenge != null && (colonPos = plainChallenge.indexOf(COLON)) > -1) {
|
||||
@ -2,7 +2,7 @@
|
||||
%global namedversion %{version}%{?namedreltag}
|
||||
Name: undertow
|
||||
Version: 1.4.0
|
||||
Release: 9
|
||||
Release: 10
|
||||
Summary: Java web server using non-blocking IO
|
||||
License: ASL 2.0
|
||||
URL: http://undertow.io/
|
||||
@ -17,6 +17,11 @@ Patch5: CVE-2021-3690.patch
|
||||
Patch6: CVE-2023-1973.patch
|
||||
Patch7: CVE-2023-5379.patch
|
||||
Patch8: CVE-2024-4109.patch
|
||||
Patch9: CVE-2017-12196-1.patch
|
||||
Patch10: CVE-2017-12196-2.patch
|
||||
Patch11: CVE-2019-10184-pre.patch
|
||||
Patch12: CVE-2019-10184.patch
|
||||
Patch13: CVE-2019-10212.patch
|
||||
BuildArch: noarch
|
||||
Epoch: 1
|
||||
BuildRequires: maven-local mvn(junit:junit) mvn(org.eclipse.jetty.alpn:alpn-api)
|
||||
@ -78,6 +83,9 @@ export CXXFLAGS="${RPM_OPT_FLAGS}"
|
||||
%license LICENSE.txt
|
||||
|
||||
%changelog
|
||||
* Tue Mar 04 2025 yaoxin <1024769339@qq.com> - 1:1.4.0-10
|
||||
- Fix CVE-2017-12196,CVE-2019-10184 and CVE-2019-10212
|
||||
|
||||
* Tue Dec 17 2024 liyajie <liyajie15@h-partners.com> - 1:1.4.0-9
|
||||
- Fix CVE-2024-4109
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user