Repository: mina-sshd Updated Branches: refs/heads/master 7df595887 -> b5bb002f0
[SSHD-689] Allow providing a file as a welcome banner configuration option Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/b5bb002f Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/b5bb002f Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/b5bb002f Branch: refs/heads/master Commit: b5bb002f06a7977a2935cdab27be35afd96c7886 Parents: 7df5958 Author: Lyor Goldstein <[email protected]> Authored: Thu Aug 18 19:08:41 2016 +0300 Committer: Lyor Goldstein <[email protected]> Committed: Thu Aug 18 19:08:41 2016 +0300 ---------------------------------------------------------------------- .../sshd/common/PropertyResolverUtils.java | 24 +++++- .../server/ServerAuthenticationManager.java | 32 +++++++- .../java/org/apache/sshd/server/SshServer.java | 23 ++---- .../server/session/ServerUserAuthService.java | 84 ++++++++++++++++---- .../sshd/server/auth/WelcomeBannerTest.java | 1 - 5 files changed, 131 insertions(+), 33 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/b5bb002f/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java index 40b2510..d5c767b 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java @@ -19,6 +19,7 @@ package org.apache.sshd.common; +import java.nio.charset.Charset; import java.util.Collection; import java.util.Map; import java.util.NoSuchElementException; @@ -311,6 +312,28 @@ public final class PropertyResolverUtils { } } + public static Charset getCharset(PropertyResolver resolver, String name, Charset defaultValue) { + Object value = getObject(resolver, name); + return (value == null) ? defaultValue : toCharset(value); + } + + public static Charset getCharset(Map<String, ?> props, String name, Charset defaultValue) { + Object value = getObject(props, name); + return (value == null) ? defaultValue : toCharset(value); + } + + public static Charset toCharset(Object value) { + if (value == null) { + return null; + } else if (value instanceof Charset) { + return (Charset) value; + } else if (value instanceof CharSequence) { + return Charset.forName(value.toString()); + } else { + throw new IllegalArgumentException("Invalid charset conversion value: " + value); + } + } + public static String getString(PropertyResolver resolver, String name) { Object value = getObject(resolver, name); return Objects.toString(value, null); @@ -325,7 +348,6 @@ public final class PropertyResolverUtils { return resolvePropertyValue(resolver, name); } - // for symmetrical reasons... public static Object getObject(Map<String, ?> props, String name) { return resolvePropertyValue(props, name); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/b5bb002f/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java index b1978e1..188fd3f 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java @@ -63,7 +63,30 @@ public interface ServerAuthenticationManager { /** * Key used to retrieve the value of welcome banner that will be displayed * when a user connects to the server. If {@code null}/empty then no banner - * will be sent. + * will be sent. The value can be one of the following: + * <UL> + * <P><LI> + * A {@link java.io.File} or {@link java.nio.file.Path}, in which case + * its contents will be transmitted. <B>Note:</B> if the file is empty + * or does not exits, no banner will be transmitted. + * </LI></P> + * + * <P><LI> + * A {@link java.net.URI} or a string starting with "file:/", in + * which case it will be converted to a {@link java.nio.file.Path} and + * handled accordingly. + * </LI></P> + * + * <P><LI> + * A string containing a special value indicator - e.g., {@link #AUTO_WELCOME_BANNER_VALUE}, + * in which case the relevant banner content will be generated. + * </LI></P> + * + * <P><LI> + * Any other object whose {@code toString()} value yields a non empty string + * will be used as the banner contents. + * </LI></P> + * </UL> * @see <A HREF="https://www.ietf.org/rfc/rfc4252.txt">RFC-4252 section 5.4</A> */ String WELCOME_BANNER = "welcome-banner"; @@ -100,6 +123,13 @@ public interface ServerAuthenticationManager { WelcomeBannerPhase DEFAULT_BANNER_PHASE = WelcomeBannerPhase.IMMEDIATE; /** + * The charset to use if the configured welcome banner points + * to a file - if not specified (either as a string or a {@link java.nio.charset.Charset} + * then the local default is used. + */ + String WELCOME_BANNER_CHARSET = "welcome-banner-charset"; + + /** * This key is used when configuring multi-step authentications. * The value needs to be a blank separated list of comma separated list * of authentication method names. http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/b5bb002f/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java index a6e635b..f8fcf39 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java @@ -45,7 +45,6 @@ import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.ServiceFactory; import org.apache.sshd.common.config.SshConfigFileReader; -import org.apache.sshd.common.config.keys.KeyRandomArt; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.helpers.AbstractFactoryManager; import org.apache.sshd.common.io.IoAcceptor; @@ -583,7 +582,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa KeyPairProvider hostKeyProvider = setupServerKeys(sshd, hostKeyType, hostKeySize, keyFiles); sshd.setKeyPairProvider(hostKeyProvider); - // Should come AFTER key pair provider setup so auto-welcome can be generated + // Should come AFTER key pair provider setup so auto-welcome can be generated if needed setupServerBanner(sshd, options); sshd.setPort(port); @@ -608,7 +607,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa Thread.sleep(Long.MAX_VALUE); } - public static String setupServerBanner(ServerFactoryManager server, Map<String, ?> options) throws Exception { + public static Object setupServerBanner(ServerFactoryManager server, Map<String, ?> options) throws Exception { String bannerOption = GenericUtils.isEmpty(options) ? null : Objects.toString(options.remove(SshConfigFileReader.BANNER_CONFIG_PROP), null); @@ -621,30 +620,22 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa } } - String banner; + Object banner; if (GenericUtils.length(bannerOption) > 0) { if ("none".equals(bannerOption)) { return null; } if (ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE.equalsIgnoreCase(bannerOption)) { - banner = KeyRandomArt.combine(' ', server.getKeyPairProvider()); + banner = bannerOption; } else { - Path path = Paths.get(bannerOption); - long fileSize = Files.size(path); - ValidateUtils.checkTrue(fileSize > 0L, "No banner contents in file=%s", bannerOption); - - List<String> lines = Files.readAllLines(path); - banner = GenericUtils.join(lines, '\n'); + banner = Paths.get(bannerOption); } } else { banner = "Welcome to SSHD\n"; } - if (GenericUtils.length(banner) > 0) { - PropertyResolverUtils.updateProperty(server, ServerAuthenticationManager.WELCOME_BANNER, banner); - } - - return PropertyResolverUtils.getString(server, ServerAuthenticationManager.WELCOME_BANNER); + PropertyResolverUtils.updateProperty(server, ServerAuthenticationManager.WELCOME_BANNER, banner); + return banner; } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/b5bb002f/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java index b0593f6..8ea3162 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java @@ -18,9 +18,17 @@ */ package org.apache.sshd.server.session; +import java.io.File; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -368,20 +376,7 @@ public class ServerUserAuthService extends AbstractCloseable implements Service, return null; } - String welcomeBanner = PropertyResolverUtils.getString(session, ServerAuthenticationManager.WELCOME_BANNER); - if ((GenericUtils.length(welcomeBanner) > 0) - && ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE.equalsIgnoreCase(welcomeBanner)) { - try { - welcomeBanner = KeyRandomArt.combine(' ', session.getKeyPairProvider()); - } catch (Exception e) { - if (e instanceof IOException) { - throw (IOException) e; - } - - throw new IOException(e); - } - } - + String welcomeBanner = resolveWelcomeBanner(session); if (GenericUtils.isEmpty(welcomeBanner)) { return null; } @@ -401,6 +396,67 @@ public class ServerUserAuthService extends AbstractCloseable implements Service, return session.writePacket(buffer); } + protected String resolveWelcomeBanner(ServerSession session) throws IOException { + Object bannerValue = PropertyResolverUtils.getObject(session, ServerAuthenticationManager.WELCOME_BANNER); + if (bannerValue == null) { + return null; + } + + if (bannerValue instanceof CharSequence) { + String message = bannerValue.toString(); + if (GenericUtils.isEmpty(message)) { + return null; + } + + if (ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE.equalsIgnoreCase(message)) { + try { + return KeyRandomArt.combine(' ', session.getKeyPairProvider()); + } catch (Exception e) { + if (e instanceof IOException) { + throw (IOException) e; + } + + throw new IOException(e); + } + } + + if (!message.startsWith("file:/")) { + return message; + } + + try { + bannerValue = new URI(message); + } catch (URISyntaxException e) { + log.error("resolveWelcomeBanner({}) bad path URI {}: {}", session, message, e.getMessage()); + throw new IOException(e); + } + } + + if (bannerValue instanceof URI) { + bannerValue = Paths.get((URI) bannerValue); + } + + if (bannerValue instanceof File) { + bannerValue = ((File) bannerValue).toPath(); + } + + if (bannerValue instanceof Path) { + Path path = (Path) bannerValue; + if ((!Files.exists(path)) || (Files.size(path) <= 0L)) { + if (log.isDebugEnabled()) { + log.debug("resolveWelcomeBanner({}) file is empty/does not exist", session, path); + } + return null; + } + + Charset cs = PropertyResolverUtils.getCharset(session, ServerAuthenticationManager.WELCOME_BANNER_CHARSET, Charset.defaultCharset()); + Collection<String> lines = Files.readAllLines((Path) bannerValue, cs); + return GenericUtils.join(lines, '\n'); + } + + return bannerValue.toString(); + } + public ServerFactoryManager getFactoryManager() { return serverSession.getFactoryManager(); } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/b5bb002f/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java index 3126b6b..7f824ad 100644 --- a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java @@ -51,7 +51,6 @@ public class WelcomeBannerTest extends BaseTestSupport { super(); } - @BeforeClass public static void setupClientAndServer() throws Exception { sshd = Utils.setupTestServer(WelcomeBannerPhaseTest.class);
