This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 9a5075448fcbad3243b097f84c42ba39b232c490 Author: Tung Van TRAN <vtt...@linagora.com> AuthorDate: Wed Dec 14 17:22:02 2022 +0700 JAMES-3868 Refactor AuthCmdHandler.doPlainAuthPass when parsing token && add more test cases --- .../protocols/smtp/core/esmtp/AuthCmdHandler.java | 85 ++++++++-------------- .../apache/james/smtpserver/SMTPServerTest.java | 43 +++++++++++ 2 files changed, 75 insertions(+), 53 deletions(-) diff --git a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/esmtp/AuthCmdHandler.java b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/esmtp/AuthCmdHandler.java index c476c95139..0a39893cbe 100644 --- a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/esmtp/AuthCmdHandler.java +++ b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/esmtp/AuthCmdHandler.java @@ -23,14 +23,15 @@ package org.apache.james.protocols.smtp.core.esmtp; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Optional; -import java.util.StringTokenizer; import java.util.function.Function; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.james.core.Username; @@ -54,6 +55,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -241,62 +243,39 @@ public class AuthCmdHandler * @param line the initial response line passed in with the AUTH command */ private Response doPlainAuthPass(SMTPSession session, String line) { - String user = null; - String pass = null; try { - String userpass = decodeBase64(line); - if (userpass != null) { - /* See: RFC 2595, Section 6 - The mechanism consists of a single message from the client to the - server. The client sends the authorization identity (identity to - login as), followed by a US-ASCII NUL character, followed by the - authentication identity (identity whose password will be used), - followed by a US-ASCII NUL character, followed by the clear-text - password. The client may leave the authorization identity empty to - indicate that it is the same as the authentication identity. - - The server will verify the authentication identity and password with - the system authentication database and verify that the authentication - credentials permit the client to login as the authorization identity. - If both steps succeed, the user is logged in. - */ - StringTokenizer authTokenizer = new StringTokenizer(userpass, "\0"); - String authorizeId = authTokenizer.nextToken(); // Authorization Identity - user = authTokenizer.nextToken(); // Authentication Identity - try { - pass = authTokenizer.nextToken(); // Password - } catch (java.util.NoSuchElementException ignored) { - // If we got here, this is what happened. RFC 2595 - // says that "the client may leave the authorization - // identity empty to indicate that it is the same as - // the authentication identity." As noted above, - // that would be represented as a decoded string of - // the form: "\0authenticate-id\0password". The - // first call to nextToken will skip the empty - // authorize-id, and give us the authenticate-id, - // which we would store as the authorize-id. The - // second call will give us the password, which we - // think is the authenticate-id (user). Then when - // we ask for the password, there are no more - // elements, leading to the exception we just - // caught. So we need to move the user to the - // password, and the authorize_id to the user. - pass = user; - user = authorizeId; - } - - authTokenizer = null; + List<String> tokens = Optional.ofNullable(decodeBase64(line)) + .map(userpass1 -> Arrays.stream(userpass1.split("\0")) + .filter(token -> !token.isBlank()) + .collect(Collectors.toList())) + .orElse(List.of()); + Preconditions.checkArgument(tokens.size() == 2 || tokens.size() == 3); + Response response = null; + if (tokens.size() == 2) { + // If we got here, this is what happened. RFC 2595 + // says that "the client may leave the authorization + // identity empty to indicate that it is the same as + // the authentication identity." As noted above, + // that would be represented as a decoded string of + // the form: "\0authenticate-id\0password". The + // first call to nextToken will skip the empty + // authorize-id, and give us the authenticate-id, + // which we would store as the authorize-id. The + // second call will give us the password, which we + // think is the authenticate-id (user). Then when + // we ask for the password, there are no more + // elements, leading to the exception we just + // caught. So we need to move the user to the + // password, and the authorize_id to the user. + response = doAuthTest(session, Username.of(tokens.get(0)), tokens.get(1), AUTH_TYPE_PLAIN); + } else { + response = doAuthTest(session, Username.of(tokens.get(1)), tokens.get(2), AUTH_TYPE_PLAIN); } + session.popLineHandler(); + return response; } catch (Exception e) { - // Ignored - this exception in parsing will be dealt - // with in the if clause below + return new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_ARGUMENTS,"Could not decode parameters for AUTH PLAIN"); } - // Authenticate user - Response response = doAuthTest(session, Username.of(user), pass, "PLAIN"); - - session.popLineHandler(); - - return response; } private String decodeBase64(String line) { diff --git a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java index 01a880a147..fbde0a3f89 100644 --- a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java +++ b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java @@ -1380,6 +1380,49 @@ public class SMTPServerTest { .isNotNull(); } + @Test + public void testAuthShouldSucceedWhenPasswordHasMoreThan255Characters() throws Exception { + smtpConfiguration.setAuthorizedAddresses("128.0.0.1/8"); + smtpConfiguration.setAuthorizingAnnounce(); + init(smtpConfiguration); + + SMTPClient smtpProtocol = new SMTPClient(); + InetSocketAddress bindedAddress = new ProtocolServerUtils(smtpServer).retrieveBindedAddress(); + smtpProtocol.connect(bindedAddress.getAddress().getHostAddress(), bindedAddress.getPort()); + + smtpProtocol.sendCommand("ehlo", InetAddress.getLocalHost().toString()); + String userName = USER_LOCALHOST; + String userPassword = "1".repeat(300); + usersRepository.addUser(Username.of(userName), userPassword); + + smtpProtocol.sendCommand("AUTH PLAIN"); + smtpProtocol.sendCommand(Base64.getEncoder().encodeToString(("\0" + userName + "\0" + userPassword + "\0").getBytes(UTF_8))); + assertThat(smtpProtocol.getReplyCode()) + .as("authenticated") + .isEqualTo(235); + } + + @Test + public void testAuthShouldFailedWhenUserPassIsNotBase64Decoded() throws Exception { + smtpConfiguration.setAuthorizedAddresses("128.0.0.1/8"); + smtpConfiguration.setAuthorizingAnnounce(); + init(smtpConfiguration); + + SMTPClient smtpProtocol = new SMTPClient(); + InetSocketAddress bindedAddress = new ProtocolServerUtils(smtpServer).retrieveBindedAddress(); + smtpProtocol.connect(bindedAddress.getAddress().getHostAddress(), bindedAddress.getPort()); + + smtpProtocol.sendCommand("ehlo", InetAddress.getLocalHost().toString()); + String userName = USER_LOCALHOST; + String userPassword = "1".repeat(300); + usersRepository.addUser(Username.of(userName), userPassword); + + smtpProtocol.sendCommand("AUTH PLAIN"); + smtpProtocol.sendCommand("canNotDecode"); + assertThat(smtpProtocol.getReplyString()) + .contains("501 Could not decode parameters for AUTH PLAIN"); + } + @Test public void testAuthSendMailFromAlias() throws Exception { smtpConfiguration.setAuthorizedAddresses("128.0.0.1/8"); --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org