This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new 6565a6cb64 Fix the handling of invalid users with DIGEST authentication
6565a6cb64 is described below
commit 6565a6cb6499e56fe2f34457cec99f9d1c4f39e9
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Apr 23 12:54:42 2026 +0100
Fix the handling of invalid users with DIGEST authentication
---
java/org/apache/catalina/realm/RealmBase.java | 11 ++-
.../TestDigestAuthenticatorAlgorithms.java | 6 +-
.../authenticator/TestDigestAuthenticatorB.java | 106 +++------------------
webapps/docs/changelog.xml | 3 +
4 files changed, 29 insertions(+), 97 deletions(-)
diff --git a/java/org/apache/catalina/realm/RealmBase.java
b/java/org/apache/catalina/realm/RealmBase.java
index 44bf47671e..ae9108f2ef 100644
--- a/java/org/apache/catalina/realm/RealmBase.java
+++ b/java/org/apache/catalina/realm/RealmBase.java
@@ -1146,12 +1146,19 @@ public abstract class RealmBase extends
LifecycleMBeanBase implements Realm {
* @return the digest for the specified user
*/
protected String getDigest(String username, String realmName, String
algorithm) {
+ String password = getPassword(username);
+
+ // Short-cut null password case
+ if (password == null) {
+ return null;
+ }
+
if (hasMessageDigest(algorithm)) {
// Use pre-generated digest
- return getPassword(username);
+ return password;
}
- String digestValue = username + ":" + realmName + ":" +
getPassword(username);
+ String digestValue = username + ":" + realmName + ":" + password;
byte[] valueBytes;
try {
diff --git
a/test/org/apache/catalina/authenticator/TestDigestAuthenticatorAlgorithms.java
b/test/org/apache/catalina/authenticator/TestDigestAuthenticatorAlgorithms.java
index 279c68490e..d5027c4b4c 100644
---
a/test/org/apache/catalina/authenticator/TestDigestAuthenticatorAlgorithms.java
+++
b/test/org/apache/catalina/authenticator/TestDigestAuthenticatorAlgorithms.java
@@ -194,21 +194,21 @@ public class TestDigestAuthenticatorAlgorithms extends
TomcatBaseTest {
}
- protected static String getNonce(String authHeader) {
+ private static String getNonce(String authHeader) {
int start = authHeader.indexOf("nonce=\"") + 7;
int end = authHeader.indexOf('\"', start);
return authHeader.substring(start, end);
}
- protected static String getOpaque(String authHeader) {
+ private static String getOpaque(String authHeader) {
int start = authHeader.indexOf("opaque=\"") + 8;
int end = authHeader.indexOf('\"', start);
return authHeader.substring(start, end);
}
- private static String buildDigestResponse(String user, String pwd, String
uri, String realm, AuthDigest algorithm,
+ static String buildDigestResponse(String user, String pwd, String uri,
String realm, AuthDigest algorithm,
List<String> authHeaders, String nc, String cnonce, String qop) {
// Find auth header with correct algorithm
diff --git
a/test/org/apache/catalina/authenticator/TestDigestAuthenticatorB.java
b/test/org/apache/catalina/authenticator/TestDigestAuthenticatorB.java
index 9210b1780b..ae64f34a83 100644
--- a/test/org/apache/catalina/authenticator/TestDigestAuthenticatorB.java
+++ b/test/org/apache/catalina/authenticator/TestDigestAuthenticatorB.java
@@ -35,11 +35,9 @@ import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
-import org.apache.tomcat.util.security.ConcurrentMessageDigest;
@RunWith(Parameterized.class)
public class TestDigestAuthenticatorB extends TomcatBaseTest {
@@ -55,8 +53,10 @@ public class TestDigestAuthenticatorB extends TomcatBaseTest
{
@Parameterized.Parameters(name = "{index}")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
- parameterSets.add(new Object[] { validRole, validUser, validPassword
});
- parameterSets.add(new Object[] { "**", validUser, validPassword });
+ parameterSets.add(new Object[] { validRole, validUser, validPassword,
Boolean.TRUE });
+ parameterSets.add(new Object[] { "**", validUser, validPassword,
Boolean.TRUE });
+ parameterSets.add(new Object[] { "**", validUser, "null",
Boolean.FALSE });
+ parameterSets.add(new Object[] { "**", "invalid", "null",
Boolean.FALSE });
return parameterSets;
}
@@ -69,6 +69,9 @@ public class TestDigestAuthenticatorB extends TomcatBaseTest {
@Parameter(2)
public String clientPassword;
+ @Parameter(3)
+ public boolean validCredentials;
+
@Test
public void testDigestAuthentication() throws Exception {
@@ -115,99 +118,18 @@ public class TestDigestAuthenticatorB extends
TomcatBaseTest {
// Second request should
List<String> auth = new ArrayList<>();
- auth.add(buildDigestResponse(clientUserName, clientPassword,
targetURI, realmName, AuthDigest.SHA_256,
- respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME),
"00000001", clientNonce, DigestAuthenticator.QOP));
+
auth.add(TestDigestAuthenticatorAlgorithms.buildDigestResponse(clientUserName,
clientPassword, targetURI,
+ realmName, AuthDigest.SHA_256,
respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME), "00000001",
+ clientNonce, DigestAuthenticator.QOP));
Map<String,List<String>> reqHeaders = new HashMap<>();
reqHeaders.put("authorization", auth);
rc = getUrl("http://localhost:" + getPort() + targetURI, bc,
reqHeaders, null);
- Assert.assertEquals(200, rc);
- Assert.assertEquals("OK", bc.toString());
- }
-
-
- protected static String getNonce(String authHeader) {
- int start = authHeader.indexOf("nonce=\"") + 7;
- int end = authHeader.indexOf('\"', start);
- return authHeader.substring(start, end);
- }
-
-
- protected static String getOpaque(String authHeader) {
- int start = authHeader.indexOf("opaque=\"") + 8;
- int end = authHeader.indexOf('\"', start);
- return authHeader.substring(start, end);
- }
-
-
- private static String buildDigestResponse(String user, String pwd, String
uri, String realm, AuthDigest algorithm,
- List<String> authHeaders, String nc, String cnonce, String qop) {
-
- // Find auth header with correct algorithm
- String nonce = null;
- String opaque = null;
- for (String authHeader : authHeaders) {
- nonce = getNonce(authHeader);
- opaque = getOpaque(authHeader);
- if (authHeader.contains("algorithm=" + algorithm.getRfcName())) {
- break;
- }
- }
- if (nonce == null || opaque == null) {
- Assert.fail();
- }
-
- String a1 = user + ":" + realm + ":" + pwd;
- String a2 = "GET:" + uri;
-
- String digestA1 = digest(algorithm.getJavaName(), a1);
- String digestA2 = digest(algorithm.getJavaName(), a2);
-
- String response;
- if (qop == null) {
- response = digestA1 + ":" + nonce + ":" + digestA2;
+ if (validCredentials) {
+ Assert.assertEquals(200, rc);
+ Assert.assertEquals("OK", bc.toString());
} else {
- response = digestA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":"
+ qop + ":" + digestA2;
- }
-
- String digestResponse = digest(algorithm.getJavaName(), response);
-
- StringBuilder auth = new StringBuilder();
- auth.append("Digest username=\"");
- auth.append(user);
- auth.append("\", realm=\"");
- auth.append(realm);
- auth.append("\", algorithm=");
- auth.append(algorithm.getRfcName());
- auth.append(", nonce=\"");
- auth.append(nonce);
- auth.append("\", uri=\"");
- auth.append(uri);
- auth.append("\", opaque=\"");
- auth.append(opaque);
- auth.append("\", response=\"");
- auth.append(digestResponse);
- auth.append("\"");
- if (qop != null) {
- auth.append(", qop=");
- auth.append(qop);
- auth.append("");
+ Assert.assertEquals(401, rc);
}
- if (nc != null) {
- auth.append(", nc=");
- auth.append(nc);
- }
- if (cnonce != null) {
- auth.append(", cnonce=\"");
- auth.append(cnonce);
- auth.append("\"");
- }
-
- return auth.toString();
- }
-
-
- private static String digest(String algorithm, String input) {
- return HexUtils.toHexString(ConcurrentMessageDigest.digest(algorithm,
input.getBytes()));
}
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 563bf4a0f5..73ccc8c9e3 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -169,6 +169,9 @@
<code>false</code>, meaning user names are treated in a case
insensitive
manner. (markt)
</add>
+ <fix>
+ Correct the handling of invalid users with DIGEST authentication.
(markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]