GH-9794: Encode SMB domain, username & password before SmbFile

Fixes: #9794
Issue link: https://github.com/spring-projects/spring-integration/issues/9794

The `SmbConfig.rawUrl()` method doesn't apply any URL encoding,
resulting in parts of the `domainUserPass` that may contain a `@` character to break
the URL logic in regard to determining the hostname.

* Modify `SmbConfig.getDomainUserPass(_includePassword)` to conditionally encode the variables.
Makes sure to encode the individual parts to not undesirably encode the `;` and `:` characters, breaking other logic.
* Modify `SmbConfig.rawUrl(_includePassword)` and `SmbConfig.createUri(_includePassword)`
to call the modified method with the correct `_urlEncode` variable

Signed-off-by: Jelle Smits <110663902+smitsjelle@users.noreply.github.com>

[artem.bilan@broadcom.com: some code cleanup]

* Add author to the affected classes
* Remove redundant explicit exceptions list from the `SmbMessageHistoryTests`

**Auto-cherry-pick to `6.3.x`**

Signed-off-by: Artem Bilan <artem.bilan@broadcom.com>
This commit is contained in:
Jelle Smits
2025-01-28 15:58:19 +01:00
committed by Artem Bilan
parent 4b0d0e2d14
commit fcd90a905e
2 changed files with 31 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.integration.smb.session;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import jcifs.DialectVersion;
@@ -34,6 +36,7 @@ import org.springframework.util.StringUtils;
* @author Prafull Kumar Soni
* @author Artem Bilan
* @author Gregory Bragg
* @author Jelle Smits
*
* @since 6.0
*/
@@ -163,16 +166,19 @@ public class SmbConfig {
this.smbMaxVersion = _smbMaxVersion;
}
String getDomainUserPass(boolean _includePassword) {
String getDomainUserPass(boolean _includePassword, boolean _urlEncode) {
String domainUserPass;
String username = _urlEncode ? URLEncoder.encode(this.username, StandardCharsets.UTF_8) : this.username;
String password = _urlEncode ? URLEncoder.encode(this.password, StandardCharsets.UTF_8) : this.password;
if (StringUtils.hasText(this.domain)) {
domainUserPass = String.format("%s;%s", this.domain, this.username);
String domain = _urlEncode ? URLEncoder.encode(this.domain, StandardCharsets.UTF_8) : this.domain;
domainUserPass = String.format("%s;%s", domain, username);
}
else {
domainUserPass = this.username;
domainUserPass = username;
}
if (StringUtils.hasText(this.password)) {
domainUserPass += ":" + (_includePassword ? this.password : "********");
if (StringUtils.hasText(password)) {
domainUserPass += ":" + (_includePassword ? password : "********");
}
return domainUserPass;
}
@@ -211,20 +217,22 @@ public class SmbConfig {
}
/**
* Return the url string for the share connection without encoding.
* Return the url string for the share connection without encoding
* the host and path. The {@code domainUserPass} is encoded, as
* {@link java.net.URL} requires them to be encoded otherwise its parsing fails.
* Used in the {@link SmbShare} constructor delegation.
* @param _includePassword whether password has to be masked in credentials of URL.
* @return the url string for the share connection without encoding.
* @since 6.3.8
*/
public final String rawUrl(boolean _includePassword) {
String domainUserPass = getDomainUserPass(_includePassword);
String domainUserPass = getDomainUserPass(_includePassword, true);
String path = cleanPath();
return "smb://%s@%s%s".formatted(domainUserPass, getHostPort(), path);
}
private URI createUri(boolean _includePassword) {
String domainUserPass = getDomainUserPass(_includePassword);
String domainUserPass = getDomainUserPass(_includePassword, false);
String path = cleanPath();
try {
return new URI("smb", domainUserPass, this.host, this.port, path, null, null);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,12 @@
package org.springframework.integration.smb;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Properties;
import jcifs.CIFSContext;
import jcifs.config.PropertyConfiguration;
import jcifs.context.BaseContext;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -32,11 +36,12 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Prafull Kumar Soni
* @author Artem Bilan
* @author Gregory Bragg
* @author Jelle Smits
*/
public class SmbMessageHistoryTests extends AbstractBaseTests {
@Test
public void testMessageHistory() throws URISyntaxException {
public void testMessageHistory() throws Exception {
try (ClassPathXmlApplicationContext applicationContext = getApplicationContext()) {
SourcePollingChannelAdapter adapter = applicationContext
.getBean("smbInboundChannelAdapter", SourcePollingChannelAdapter.class);
@@ -51,6 +56,12 @@ public class SmbMessageHistoryTests extends AbstractBaseTests {
assertThat(uri.getUserInfo()).isEqualTo("sambagu@est:sambag%uest");
assertThat(uri.getPath()).isEqualTo("/smb share/");
assertThat(uri.getRawPath()).isEqualTo("/smb%20share/");
CIFSContext context = new BaseContext(new PropertyConfiguration(new Properties()));
URL rawUrl = new URL(null, smbSessionFactory.rawUrl(true), context.getUrlHandler());
assertThat(rawUrl.getHost()).isEqualTo("localhost");
assertThat(rawUrl.getUserInfo()).isEqualTo("sambagu%40est:sambag%25uest");
assertThat(rawUrl.getPath()).isEqualTo("/smb share/");
}
}