Author: markt
Date: Fri Sep 1 19:51:42 2017
New Revision: 1807004
URL: http://svn.apache.org/viewvc?rev=1807004&view=rev
Log:
Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=61280
Add RFC 7617 support to the BasicAuthenticator
Modified:
tomcat/trunk/java/org/apache/catalina/authenticator/BasicAuthenticator.java
tomcat/trunk/java/org/apache/catalina/authenticator/LocalStrings.properties
tomcat/trunk/test/org/apache/catalina/authenticator/TestBasicAuthParser.java
tomcat/trunk/webapps/docs/changelog.xml
tomcat/trunk/webapps/docs/config/valve.xml
Modified:
tomcat/trunk/java/org/apache/catalina/authenticator/BasicAuthenticator.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/BasicAuthenticator.java?rev=1807004&r1=1807003&r2=1807004&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/authenticator/BasicAuthenticator.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/authenticator/BasicAuthenticator.java
Fri Sep 1 19:51:42 2017
@@ -20,6 +20,7 @@ package org.apache.catalina.authenticato
import java.io.IOException;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
@@ -43,10 +44,30 @@ import org.apache.tomcat.util.codec.bina
* @author Craig R. McClanahan
*/
public class BasicAuthenticator extends AuthenticatorBase {
+
private static final Log log = LogFactory.getLog(BasicAuthenticator.class);
+ private Charset charset = StandardCharsets.UTF_8;
+ private String charsetString = "UTF-8";
+
+
+ public String getCharset() {
+ return charsetString;
+ }
+
+
+ public void setCharset(String charsetString) {
+ // Only acceptable options are null, "" or "UTF-8" (case insensitive)
+ if (charsetString == null || charsetString.isEmpty()) {
+ charset = StandardCharsets.ISO_8859_1;
+ } else if ("UTF-8".equalsIgnoreCase(charsetString)) {
+ charset = StandardCharsets.UTF_8;
+ } else {
+ throw new
IllegalArgumentException(sm.getString("basicAuthenticator.invalidCharset"));
+ }
+ this.charsetString = charsetString;
+ }
- // --------------------------------------------------------- Public Methods
@Override
protected boolean doAuthenticate(Request request, HttpServletResponse
response)
@@ -66,7 +87,7 @@ public class BasicAuthenticator extends
ByteChunk authorizationBC = authorization.getByteChunk();
BasicCredentials credentials = null;
try {
- credentials = new BasicCredentials(authorizationBC);
+ credentials = new BasicCredentials(authorizationBC, charset);
String username = credentials.getUsername();
String password = credentials.getPassword();
@@ -89,6 +110,10 @@ public class BasicAuthenticator extends
value.append("Basic realm=\"");
value.append(getRealmName(context));
value.append('\"');
+ if (charsetString != null && !charsetString.isEmpty()) {
+ value.append(", charset=");
+ value.append(charsetString);
+ }
response.setHeader(AUTH_HEADER_NAME, value.toString());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
@@ -112,28 +137,31 @@ public class BasicAuthenticator extends
// note: we include single white space as its delimiter
private static final String METHOD = "basic ";
- private ByteChunk authorization;
- private int initialOffset;
+ private final Charset charset;
+ private final ByteChunk authorization;
+ private final int initialOffset;
private int base64blobOffset;
private int base64blobLength;
private String username = null;
private String password = null;
-
/**
* Parse the HTTP Authorization header for BASIC authentication
* as per RFC 2617 section 2, and the Base64 encoded credentials
* as per RFC 2045 section 6.8.
*
- * @param input The header value to parse in-place
+ * @param input The header value to parse in-place
+ * @param charset The character set to use to convert the bytes to a
+ * string
*
* @throws IllegalArgumentException If the header does not conform
* to RFC 2617
*/
- public BasicCredentials(ByteChunk input)
- throws IllegalArgumentException {
+ public BasicCredentials(ByteChunk input, Charset charset) throws
IllegalArgumentException {
authorization = input;
initialOffset = input.getOffset();
+ this.charset = charset;
+
parseMethod();
byte[] decoded = parseBase64();
parseCredentials(decoded);
@@ -210,15 +238,12 @@ public class BasicAuthenticator extends
}
if (colon < 0) {
- username = new String(decoded, StandardCharsets.ISO_8859_1);
+ username = new String(decoded, charset);
// password will remain null!
}
else {
- username = new String(
- decoded, 0, colon, StandardCharsets.ISO_8859_1);
- password = new String(
- decoded, colon + 1, decoded.length - colon - 1,
- StandardCharsets.ISO_8859_1);
+ username = new String(decoded, 0, colon, charset);
+ password = new String(decoded, colon + 1, decoded.length -
colon - 1, charset);
// tolerate surplus white space around credentials
if (password.length() > 1) {
password = password.trim();
Modified:
tomcat/trunk/java/org/apache/catalina/authenticator/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/LocalStrings.properties?rev=1807004&r1=1807003&r2=1807004&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/authenticator/LocalStrings.properties
(original)
+++ tomcat/trunk/java/org/apache/catalina/authenticator/LocalStrings.properties
Fri Sep 1 19:51:42 2017
@@ -32,6 +32,8 @@ authenticator.sessionExpired=The time al
authenticator.unauthorized=Cannot authenticate with the provided credentials
authenticator.tomcatPrincipalLogoutFail=Logout with TomcatPrincipal instance
has failed
+basicAuthenticator.invalidCharset=The only permitted values are null, the
empty string or UTF-8
+
digestAuthenticator.cacheRemove=A valid entry has been removed from client
nonce cache to make room for new entries. A replay attack is now possible. To
prevent the possibility of replay attacks, reduce nonceValidity or increase
cnonceCacheSize. Further warnings of this type will be suppressed for 5 minutes.
formAuthenticator.forwardErrorFail=Unexpected error forwarding to error page
Modified:
tomcat/trunk/test/org/apache/catalina/authenticator/TestBasicAuthParser.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/authenticator/TestBasicAuthParser.java?rev=1807004&r1=1807003&r2=1807004&view=diff
==============================================================================
---
tomcat/trunk/test/org/apache/catalina/authenticator/TestBasicAuthParser.java
(original)
+++
tomcat/trunk/test/org/apache/catalina/authenticator/TestBasicAuthParser.java
Fri Sep 1 19:51:42 2017
@@ -45,7 +45,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -56,7 +56,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, null);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertNull(credentials.getPassword());
}
@@ -68,7 +68,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -80,7 +80,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertNull(credentials.getPassword());
}
@@ -93,7 +93,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD1, credentials.getPassword());
}
@@ -119,7 +119,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_LONG, credentials.getUsername());
}
@@ -141,7 +141,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_LONG, credentials.getUsername());
}
@@ -157,7 +157,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(METHOD, USER_NAME, PASSWORD);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -174,7 +174,7 @@ public class TestBasicAuthParser {
BasicAuthenticator.BasicCredentials credentials = null;
try {
credentials = new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.fail("IllegalArgumentException expected");
}
catch (Exception e) {
@@ -197,7 +197,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD + " ", USER_NAME, PASSWORD);
final BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -213,7 +213,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, PWD_WRONG);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertNotSame(PASSWORD, credentials.getPassword());
}
@@ -225,7 +225,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, EMPTY_USER_NAME, PASSWORD);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(EMPTY_USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -237,7 +237,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, SHORT_USER_NAME, PASSWORD);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(SHORT_USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -249,7 +249,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, SHORT_PASSWORD);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(SHORT_PASSWORD, credentials.getPassword());
}
@@ -261,7 +261,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_SPACE);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD_SPACE, credentials.getPassword());
}
@@ -273,7 +273,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_COLON);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD_COLON, credentials.getPassword());
}
@@ -285,7 +285,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_COLON);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD_COLON, credentials.getPassword());
}
@@ -297,7 +297,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_COLON);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD_COLON, credentials.getPassword());
}
@@ -315,7 +315,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD, " ");
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -334,7 +334,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, " " + USER_NAME + " ",
PASSWORD);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -353,7 +353,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, USER_NAME, " " + PASSWORD + "
");
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -378,7 +378,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertNotSame(PASSWORD, credentials.getPassword());
Assert.assertEquals(TRUNCATED_PWD, credentials.getPassword());
@@ -396,7 +396,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertNotSame(PASSWORD, credentials.getPassword());
}
@@ -416,7 +416,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(POSSIBLY_DAMAGED_PWD, credentials.getPassword());
}
@@ -433,7 +433,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
@@ -452,7 +452,7 @@ public class TestBasicAuthParser {
new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new BasicAuthenticator.BasicCredentials(
- AUTH_HEADER.getHeader());
+ AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
Assert.assertEquals(USER_NAME, credentials.getUsername());
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
Modified: tomcat/trunk/webapps/docs/changelog.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1807004&r1=1807003&r2=1807004&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Fri Sep 1 19:51:42 2017
@@ -77,6 +77,10 @@
added in Java 9 to only disable the caching for JAR URL connections.
(markt)
</add>
+ <add>
+ <bug>61280</bug>: Add RFC 7617 support to the
+ <code>BasicAuthenticator</code>. (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
Modified: tomcat/trunk/webapps/docs/config/valve.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/valve.xml?rev=1807004&r1=1807003&r2=1807004&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/valve.xml (original)
+++ tomcat/trunk/webapps/docs/config/valve.xml Fri Sep 1 19:51:42 2017
@@ -1098,6 +1098,19 @@
used.</p>
</attribute>
+ <attribute name="charset" required="false">
+ <p>Controls if the <code>WWW-Authenticate</code> HTTP header includes a
+ <code>charset</code> authentication parameter as per RFC 7617. The only
+ permitted options are <code>null</code>, the empty string and
+ <code>UTF-8</code>. If <code>UTF-8</code> is specified then the
+ <code>charset</code> authentication parameter will be sent with that
+ value and the provided user name and optional password will be
converted
+ from bytes to characters using UTF-8. Otherwise, no
<code>charset</code>
+ authentication parameter will be sent and the provided user name and
+ optional password will be converted from bytes to characters using
+ ISO-8859-1. The default value is <code>UTF-8</code></p>
+ </attribute>
+
<attribute name="className" required="true">
<p>Java class name of the implementation to use. This MUST be set to
<strong>org.apache.catalina.authenticator.BasicAuthenticator</strong>.</p>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]