From e0080fb1a0c2472d4ce6aeb8a85fe86743d2870f Mon Sep 17 00:00:00 2001 From: Justin Crawford Date: Thu, 26 Mar 2020 21:43:34 -0700 Subject: [PATCH] Added hover text and click to copy for /mkpasswd - Added hover text and click to copy so you can now copy your hash to the clipboard. - Added default values for the configuration in case none were set for some reason. - Validate permissions from the config (something I forgot to do) - Tell the user permission denied if they don't have permission to run the command. - Added a default pseudo-user for global permissions and rules. - Updated some verbage in the config comments --- pom.xml | 16 +++++----- .../sshd/ConfigPasswordAuthenticator.java | 2 +- .../ryanmichela/sshd/ConsoleShellFactory.java | 27 +++++++++++++---- .../com/ryanmichela/sshd/MkpasswdCommand.java | 17 +++++++---- .../com/ryanmichela/sshd/PermissionUtil.java | 23 +++++++++++++++ .../sshd/PublicKeyAuthenticator.java | 5 ++-- .../java/com/ryanmichela/sshd/SshdPlugin.java | 12 ++++---- .../implementations/SSHDCommandSender.java | 4 +++ src/main/resources/config.yml | 29 +++++++++++++++---- 9 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 src/main/java/com/ryanmichela/sshd/PermissionUtil.java diff --git a/pom.xml b/pom.xml index 2d9ad9c..c62be7a 100644 --- a/pom.xml +++ b/pom.xml @@ -34,15 +34,15 @@ - org.bukkit - bukkit - 1.14.4-R0.1-SNAPSHOT + org.spigotmc + spigot-api + 1.15.2-R0.1-SNAPSHOT org.apache.sshd sshd-core - 2.3.0 + 2.4.0 compile jar @@ -50,19 +50,19 @@ org.apache.sshd sshd-mina - 2.3.0 + 2.4.0 org.apache.sshd sshd-contrib - 2.3.0 + 2.4.0 org.apache.sshd sshd-common - 2.3.0 + 2.4.0 compile jar @@ -70,7 +70,7 @@ org.apache.sshd sshd-sftp - 2.3.0 + 2.4.0 diff --git a/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java b/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java index 74d8753..832222d 100644 --- a/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java +++ b/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java @@ -70,7 +70,7 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator } SshdPlugin.instance.getLogger().info("Failed login for " + username + " using " + HashType + "-based password authentication."); - Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries"); + Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries", 3); try { diff --git a/src/main/java/com/ryanmichela/sshd/ConsoleShellFactory.java b/src/main/java/com/ryanmichela/sshd/ConsoleShellFactory.java index 987cf19..b554080 100644 --- a/src/main/java/com/ryanmichela/sshd/ConsoleShellFactory.java +++ b/src/main/java/com/ryanmichela/sshd/ConsoleShellFactory.java @@ -2,13 +2,13 @@ package com.ryanmichela.sshd; import com.ryanmichela.sshd.ConsoleCommandCompleter; import com.ryanmichela.sshd.ConsoleLogFormatter; +import com.ryanmichela.sshd.PermissionUtil; import com.ryanmichela.sshd.FlushyOutputStream; import com.ryanmichela.sshd.FlushyStreamHandler; import com.ryanmichela.sshd.SshTerminal; import com.ryanmichela.sshd.SshdPlugin; import com.ryanmichela.sshd.StreamHandlerAppender; import com.ryanmichela.sshd.implementations.SSHDCommandSender; -import com.ryanmichela.sshd.ConsoleLogFormatter; import jline.console.ConsoleReader; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Logger; @@ -29,6 +29,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.util.StringTokenizer; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.StreamHandler; @@ -100,6 +101,17 @@ public class ConsoleShellFactory implements ShellFactory { try { + String username = env.getEnv().get(Environment.ENV_USER); + Optional optcred = PermissionUtil.GetCredential(username, "console"); + // They don't have access. + if (optcred.isPresent() && !optcred.get().contains("R")) + { + cs.close(true); + return; + } + else + SshdPlugin.instance.getLogger().warning("There is no $default pseudo-user under credential, allowing unrestricted access..."); + this.ConsoleReader = new ConsoleReader(in, new FlushyOutputStream(out), new SshTerminal()); this.ConsoleReader.setExpandEvents(true); this.ConsoleReader.addCompleter(new ConsoleCommandCompleter()); @@ -110,14 +122,15 @@ public class ConsoleShellFactory implements ShellFactory ((Logger)LogManager.getRootLogger()).addAppender(this.streamHandlerAppender); this.environment = env; - this.Username = env.getEnv().get(Environment.ENV_USER); + this.Username = username; this.SshdCommandSender = new SSHDCommandSender(); this.SshdCommandSender.console = this; - thread = new Thread(this, "SSHD ConsoleShell " + this.Username); + thread = new Thread(this, "SSHD ConsoleShell " + username); thread.start(); } catch (Exception e) { + e.printStackTrace(); throw new IOException("Error starting shell", e); } } @@ -129,7 +142,7 @@ public class ConsoleShellFactory implements ShellFactory { try { - if (!SshdPlugin.instance.getConfig().getString("Mode").equals("RPC")) + if (!SshdPlugin.instance.getConfig().getString("Mode", "DEFAULT").equals("RPC")) printPreamble(this.ConsoleReader); while (true) { @@ -153,11 +166,15 @@ public class ConsoleShellFactory implements ShellFactory } // Hide the mkpasswd command input from other users. Boolean mkpasswd = command.split(" ")[0].equals("mkpasswd"); + Optional optcred = PermissionUtil.GetCredential(this.Username, "console"); + + if (optcred.isPresent() && !optcred.get().contains("W")) + continue; Bukkit.getScheduler().runTask( SshdPlugin.instance, () -> { - if (SshdPlugin.instance.getConfig().getString("Mode").equals("RPC") && command.startsWith("rpc")) + if (SshdPlugin.instance.getConfig().getString("Mode", "DEFAULT").equals("RPC") && command.startsWith("rpc")) { // NO ECHO NO PREAMBLE AND SHIT String cmd = command.substring("rpc".length() + 1, command.length()); diff --git a/src/main/java/com/ryanmichela/sshd/MkpasswdCommand.java b/src/main/java/com/ryanmichela/sshd/MkpasswdCommand.java index f2b0be5..bb3943a 100644 --- a/src/main/java/com/ryanmichela/sshd/MkpasswdCommand.java +++ b/src/main/java/com/ryanmichela/sshd/MkpasswdCommand.java @@ -7,6 +7,11 @@ import org.bukkit.entity.Player; import java.util.Arrays; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; + import com.ryanmichela.sshd.Cryptography; import com.ryanmichela.sshd.SshdPlugin; @@ -30,10 +35,6 @@ class MkpasswdCommand implements CommandExecutor @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - // If we're not mkpasswd, just fuck off. - if (!label.equalsIgnoreCase("mkpasswd")) - return false; - String algoritm, password; try { @@ -77,7 +78,11 @@ class MkpasswdCommand implements CommandExecutor return true; } - sender.sendMessage("\u00A79Your Hash: " + hash + "\u00A7r"); + TextComponent msg = new TextComponent("\u00A79Your Hash: " + hash + "\u00A7r"); + msg.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, hash)); + msg.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("Click to copy the hash!").create())); + + sender.spigot().sendMessage(msg); } catch (Exception e) { @@ -86,6 +91,8 @@ class MkpasswdCommand implements CommandExecutor sender.sendMessage("\u00A7cAn error occured. Please check console for details.\u00A7r"); } } + else + sender.sendMessage("\u00A7cPermission Denied.\u00A7r"); return true; } diff --git a/src/main/java/com/ryanmichela/sshd/PermissionUtil.java b/src/main/java/com/ryanmichela/sshd/PermissionUtil.java new file mode 100644 index 0000000..f872114 --- /dev/null +++ b/src/main/java/com/ryanmichela/sshd/PermissionUtil.java @@ -0,0 +1,23 @@ +package com.ryanmichela.sshd; + +import java.util.Optional; + +import com.ryanmichela.sshd.SshdPlugin; + +public class PermissionUtil +{ + public static Optional GetCredential(String username, String credential) + { + String Default = SshdPlugin.instance.getConfig().getString("Credentials.$default." + credential); + String cred = SshdPlugin.instance.getConfig().getString("Credentials." + username + "." + credential, Default); + + if (cred == null) + return Optional.empty(); + + else if (cred.isEmpty()) + return Optional.empty(); + + else + return Optional.of(cred); + } +}; \ No newline at end of file diff --git a/src/main/java/com/ryanmichela/sshd/PublicKeyAuthenticator.java b/src/main/java/com/ryanmichela/sshd/PublicKeyAuthenticator.java index 36b5027..20323dc 100644 --- a/src/main/java/com/ryanmichela/sshd/PublicKeyAuthenticator.java +++ b/src/main/java/com/ryanmichela/sshd/PublicKeyAuthenticator.java @@ -27,7 +27,7 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator { byte[] keyBytes = key.getEncoded(); File keyFile = new File(authorizedKeysDir, username); - Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries"); + Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries", 3); if (keyFile.exists()) { @@ -68,7 +68,8 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator } catch (Exception e) { - SshdPlugin.instance.getLogger().severe("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage()); + e.printStackTrace(); + SshdPlugin.instance.getLogger().severe("Failed to process public key " + keyFile.getAbsolutePath()); } } else diff --git a/src/main/java/com/ryanmichela/sshd/SshdPlugin.java b/src/main/java/com/ryanmichela/sshd/SshdPlugin.java index b822e5e..79b816d 100644 --- a/src/main/java/com/ryanmichela/sshd/SshdPlugin.java +++ b/src/main/java/com/ryanmichela/sshd/SshdPlugin.java @@ -20,7 +20,7 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; -import java.util.List; +import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.logging.Level; @@ -92,11 +92,12 @@ public class SshdPlugin extends JavaPlugin sshd.setPasswordAuthenticator(new ConfigPasswordAuthenticator()); sshd.setPublickeyAuthenticator(new PublicKeyAuthenticator(authorizedKeys)); - if (getConfig().getBoolean("EnableSFTP")) + if (getConfig().getBoolean("EnableSFTP", false)) { // Handle access control for SFTP. SftpSubsystemFactory.Builder builder = new SftpSubsystemFactory.Builder(); - builder.addSftpEventListener(new SimpleAccessControlSftpEventListener() { + builder.addSftpEventListener(new SimpleAccessControlSftpEventListener() + { protected boolean isAccessAllowed(ServerSession session, String remote, Path localpath) { try @@ -140,10 +141,11 @@ public class SshdPlugin extends JavaPlugin { try { + boolean defaultbool = getConfig().getBoolean("Credentials.$default.sftp.enabled", false); ConfigurationSection UsernameNamespace = getConfig().getConfigurationSection("Credentials." + session.getUsername() + ".sftp"); // They don't have SFTP enabled so deny them. - if (UsernameNamespace == null || !UsernameNamespace.getBoolean("enabled")) + if (UsernameNamespace == null || !UsernameNamespace.getBoolean("enabled", defaultbool)) return false; // Check a list of files against a path trying to be accessed. @@ -165,7 +167,7 @@ public class SshdPlugin extends JavaPlugin } } - return UsernameNamespace.getString("default").equalsIgnoreCase("allow"); + return UsernameNamespace.getString("default", "deny").equalsIgnoreCase("allow"); } catch (Exception e) { diff --git a/src/main/java/com/ryanmichela/sshd/implementations/SSHDCommandSender.java b/src/main/java/com/ryanmichela/sshd/implementations/SSHDCommandSender.java index 29ce605..9befe10 100644 --- a/src/main/java/com/ryanmichela/sshd/implementations/SSHDCommandSender.java +++ b/src/main/java/com/ryanmichela/sshd/implementations/SSHDCommandSender.java @@ -171,4 +171,8 @@ public class SSHDCommandSender implements ConsoleCommandSender, CommandSender return Bukkit.getServer(); } + public CommandSender.Spigot spigot() + { + return ((CommandSender)this).spigot(); + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 69bf66a..fceebe3 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,9 +1,8 @@ # The IP addresses(s) the SSH server will listen on. Use a comma separated list for multiple addresses. # Leave as "all" for all addresses. ListenAddress: all -# The port the SSH server will listen on. Note that anything above 1024 will require you to run -# the whole minecraft server with elevated privileges, this is not recommended and you should -# use iptables to route packets from a lower port. +# The port the SSH server will listen on. Note that anything *below* 1024 will require you to run +# the whole minecraft server with elevated privileges (NOT RECOMMENDED). Port: 1025 # Operational mode. Don't touch if you don't know what you're doing. Can be either DEFAULT or RPC @@ -37,8 +36,26 @@ PasswordType: bcrypt # Associate each username with a password hash (or the password if the PasswordType is set to PLAIN) Credentials: - # Username (should match SSH key if using key-based authentication) - justasic: + # The defaults for any user who does not have a specific section. + # Specific user permissions override the $default pseudo-user. + $default: + # Whether they can read or write to the console + console: RW + # SFTP access for anyone. + sftp: + # Whether sftp is allowed at all. + enabled: true + # Whether to allow or deny by default + default: allow + # specific rules for directories + rules: + # Deny the SSHD config folder by default as an example. + "*SSHD/*": + readable: false + writeable: false + + # Username (should match SSH key if using key-based authentication) + justasic: # Password hash from /mkpasswd command password: $2a$10$Oqk83FrypRrMF35EDeoQDuidJOQEWBE0joEQ7MJFi/Oeg26wQ3fm2 # Whether they can read, write, or have read/write permissions to console. @@ -47,7 +64,7 @@ Credentials: sftp: # Whether SFTP is enabled for this user. enabled: true - # Whether to deny by default or allow by default + # Whether to deny access by default or allow access by default default: allow # Rules regarding their SFTP access. # These rules are relative to the server root.