Author: markt Date: Wed Jan 23 13:57:50 2019 New Revision: 1851916 URL: http://svn.apache.org/viewvc?rev=1851916&view=rev Log: Make the removal of leading and trailing whitespace from credentials passed to BASIC authentication configurable via a new attribute, trimCredentials on the BasicAuthenticator.
Modified: tomcat/trunk/java/org/apache/catalina/authenticator/BasicAuthenticator.java tomcat/trunk/java/org/apache/catalina/authenticator/mbeans-descriptors.xml 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=1851916&r1=1851915&r2=1851916&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/authenticator/BasicAuthenticator.java (original) +++ tomcat/trunk/java/org/apache/catalina/authenticator/BasicAuthenticator.java Wed Jan 23 13:57:50 2019 @@ -49,6 +49,7 @@ public class BasicAuthenticator extends private Charset charset = StandardCharsets.ISO_8859_1; private String charsetString = null; + private boolean trimCredentials = true; public String getCharset() { @@ -69,6 +70,17 @@ public class BasicAuthenticator extends } + + public boolean getTrimCredentials() { + return trimCredentials; + } + + + public void setTrimCredentials(boolean trimCredentials) { + this.trimCredentials = trimCredentials; + } + + @Override protected boolean doAuthenticate(Request request, HttpServletResponse response) throws IOException { @@ -87,7 +99,7 @@ public class BasicAuthenticator extends ByteChunk authorizationBC = authorization.getByteChunk(); BasicCredentials credentials = null; try { - credentials = new BasicCredentials(authorizationBC, charset); + credentials = new BasicCredentials(authorizationBC, charset, trimCredentials); String username = credentials.getUsername(); String password = credentials.getPassword(); @@ -138,6 +150,7 @@ public class BasicAuthenticator extends private static final String METHOD = "basic "; private final Charset charset; + private final boolean trimCredentials; private final ByteChunk authorization; private final int initialOffset; private int base64blobOffset; @@ -145,6 +158,7 @@ public class BasicAuthenticator extends 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 @@ -156,11 +170,35 @@ public class BasicAuthenticator extends * * @throws IllegalArgumentException If the header does not conform * to RFC 2617 + * @deprecated Unused. Will be removed in Tomcat 10. Use {@link + * BasicCredentials#BasicCredentials(ByteChunk, Charset, + * boolean)} */ + @Deprecated public BasicCredentials(ByteChunk input, Charset charset) throws IllegalArgumentException { + this(input, charset, true); + } + + /** + * 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 charset The character set to use to convert the bytes + * to a string + * @param trimCredentials Should leading and trailing whitespace be + * removed from the parsed credentials + * + * @throws IllegalArgumentException If the header does not conform + * to RFC 2617 + */ + public BasicCredentials(ByteChunk input, Charset charset, boolean trimCredentials) + throws IllegalArgumentException { authorization = input; initialOffset = input.getOffset(); this.charset = charset; + this.trimCredentials = trimCredentials; parseMethod(); byte[] decoded = parseBase64(); @@ -245,12 +283,12 @@ public class BasicAuthenticator extends 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) { + if (password.length() > 1 && trimCredentials) { password = password.trim(); } } // tolerate surplus white space around credentials - if (username.length() > 1) { + if (username.length() > 1 && trimCredentials) { username = username.trim(); } } Modified: tomcat/trunk/java/org/apache/catalina/authenticator/mbeans-descriptors.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/mbeans-descriptors.xml?rev=1851916&r1=1851915&r2=1851916&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/authenticator/mbeans-descriptors.xml (original) +++ tomcat/trunk/java/org/apache/catalina/authenticator/mbeans-descriptors.xml Wed Jan 23 13:57:50 2019 @@ -64,6 +64,10 @@ description="The name of the LifecycleState that this component is currently in" type="java.lang.String" writeable="false"/> + + <attribute name="trimCredentials" + description="Controls whether leading and/or trailing whitespace is removed from the parsed credentials" + type="boolean"/> </mbean> 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=1851916&r1=1851915&r2=1851916&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/authenticator/TestBasicAuthParser.java (original) +++ tomcat/trunk/test/org/apache/catalina/authenticator/TestBasicAuthParser.java Wed Jan 23 13:57:50 2019 @@ -45,7 +45,7 @@ public class TestBasicAuthParser { new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD); BasicAuthenticator.BasicCredentials credentials = new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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(), StandardCharsets.UTF_8); + AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); 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=1851916&r1=1851915&r2=1851916&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Wed Jan 23 13:57:50 2019 @@ -107,6 +107,12 @@ <bug>63078</bug>: Ensure the utility thread pool is at least two, as the deployer uses a blocking pattern. (remm, markt) </fix> + <add> + Make the removal of leading and trailing whitespace from credentials + passed to BASIC authentication configurable via a new attribute, + <code>trimCredentials</code> on 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=1851916&r1=1851915&r2=1851916&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/valve.xml (original) +++ tomcat/trunk/webapps/docs/config/valve.xml Wed Jan 23 13:57:50 2019 @@ -1271,6 +1271,12 @@ specified, the platform default provider will be used.</p> </attribute> + <attribute name="trimCredentials" required="false"> + <p>Controls whether leading and/or trailing whitespace is removed from + the parsed credentials. If not specified, the default value is + <code>true</code>.</p> + </attribute> + <attribute name="jaspicCallbackHandlerClass" required="false"> <p>Name of the Java class of the <code>javax.security.auth.callback.CallbackHandler</code> implementation --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org