From 36e4012a5693f1605be6c1a049807dfcad782dc2 Mon Sep 17 00:00:00 2001 From: Jelle Smits <110663902+smitsjelle@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:58:19 +0100 Subject: [PATCH] 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.4.x` & `6.3.x`** Signed-off-by: Artem Bilan --- .../integration/smb/session/SmbConfig.java | 26 ++++++++++++------- .../smb/SmbMessageHistoryTests.java | 17 +++++++++--- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbConfig.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbConfig.java index 22fe581ae4..bbc8512d61 100644 --- a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbConfig.java +++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbConfig.java @@ -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 @@ import java.net.URI; import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import jcifs.DialectVersion; @@ -34,6 +36,7 @@ * @author Prafull Kumar Soni * @author Artem Bilan * @author Gregory Bragg + * @author Jelle Smits * * @since 6.0 */ @@ -163,16 +166,19 @@ public void setSmbMaxVersion(DialectVersion _smbMaxVersion) { 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 final String rawUrl() { } /** - * 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); diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests.java index 65549313e3..90a30b1cf9 100644 --- a/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests.java +++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbMessageHistoryTests.java @@ -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 @@ * @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 void testMessageHistory() throws URISyntaxException { 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/"); } }