From 28f4a9c4069c5e21295124f3e049b5367b83dd39 Mon Sep 17 00:00:00 2001 From: ayunami2000 Date: Sat, 23 Jul 2022 20:06:46 -0400 Subject: [PATCH 1/5] Implement authentication --- .../main/java/net/md_5/bungee/BungeeCord.java | 8 +- .../net/md_5/bungee/ServerConnection.java | 6 +- .../bungee/api/config/AuthServiceInfo.java | 22 +-- .../bungee/command/CommandChangePassword.java | 34 ++++ .../net/md_5/bungee/config/YamlConfig.java | 7 +- .../bungee/connection/InitialHandler.java | 24 ++- .../bungee/connection/UpstreamBridge.java | 2 +- .../md_5/bungee/eaglercraft/AuthHandler.java | 149 ++++++++++++++++++ .../md_5/bungee/eaglercraft/AuthSystem.java | 136 ++++++++++++++++ .../bungee/eaglercraft/PluginEaglerAuth.java | 52 ------ .../packet/AbstractPacketHandler.java | 3 + .../packet/Packet0DPositionAndLook.java | 114 ++++++++++++++ 12 files changed, 468 insertions(+), 89 deletions(-) create mode 100644 eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandChangePassword.java create mode 100644 eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java create mode 100644 eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java delete mode 100644 eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/PluginEaglerAuth.java create mode 100644 eaglercraftbungee/src/main/java/net/md_5/bungee/protocol/packet/Packet0DPositionAndLook.java diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/BungeeCord.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/BungeeCord.java index 4623187..d4fa3e3 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/BungeeCord.java @@ -62,6 +62,7 @@ import net.md_5.bungee.api.scheduler.TaskScheduler; import net.md_5.bungee.api.tab.CustomTabList; import net.md_5.bungee.command.CommandAlert; import net.md_5.bungee.command.CommandBungee; +import net.md_5.bungee.command.CommandChangePassword; import net.md_5.bungee.command.CommandClearRatelimit; import net.md_5.bungee.command.CommandConfirmCode; import net.md_5.bungee.command.CommandDomain; @@ -87,6 +88,7 @@ import net.md_5.bungee.command.CommandServer; import net.md_5.bungee.command.ConsoleCommandSender; import net.md_5.bungee.config.Configuration; import net.md_5.bungee.config.YamlConfig; +import net.md_5.bungee.eaglercraft.AuthSystem; import net.md_5.bungee.eaglercraft.BanList; import net.md_5.bungee.eaglercraft.DomainBlacklist; import net.md_5.bungee.eaglercraft.PluginEaglerSkins; @@ -126,6 +128,7 @@ public class BungeeCord extends ProxyServer { private ConsoleReader consoleReader; private final Logger logger; private Collection banCommands; + public AuthSystem authSystem; public static BungeeCord getInstance() { return (BungeeCord) ProxyServer.getInstance(); @@ -234,7 +237,10 @@ public class BungeeCord extends ProxyServer { this.pluginManager.detectPlugins(this.pluginsFolder); this.pluginManager.addInternalPlugin(new PluginEaglerSkins()); this.pluginManager.addInternalPlugin(new PluginEaglerVoice(this.config.getVoiceEnabled())); - //if(this.config.getAuthInfo().isEnabled()) this.pluginManager.addInternalPlugin(new PluginEaglerAuth()); + if (this.config.getAuthInfo().isEnabled()) { + this.authSystem = new AuthSystem(this.config.getAuthInfo()); + this.getPluginManager().registerCommand(null, new CommandChangePassword(this.authSystem)); + } if (this.reconnectHandler == null) { this.reconnectHandler = new SQLReconnectHandler(); } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/ServerConnection.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/ServerConnection.java index 95f2089..b8ac4ef 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/ServerConnection.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/ServerConnection.java @@ -28,7 +28,7 @@ public class ServerConnection implements Server { @Override public synchronized void disconnect(final String reason) { - if (!this.ch.isClosed()) { + if (this.ch != null && !this.ch.isClosed()) { this.unsafe().sendPacket(new PacketFFKick(reason)); this.ch.getHandle().eventLoop().schedule((Runnable) new Runnable() { @Override @@ -41,7 +41,7 @@ public class ServerConnection implements Server { @Override public InetSocketAddress getAddress() { - return this.getInfo().getAddress(); + return this.getInfo() == null ? null : this.getInfo().getAddress(); } @Override @@ -54,7 +54,7 @@ public class ServerConnection implements Server { this.unsafe = new Connection.Unsafe() { @Override public void sendPacket(final DefinedPacket packet) { - ServerConnection.this.ch.write(packet); + if (ServerConnection.this.ch != null) ServerConnection.this.ch.write(packet); } }; this.ch = ch; diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java index fd52e63..a971f7c 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java @@ -1,35 +1,27 @@ package net.md_5.bungee.api.config; -import java.io.File; - public class AuthServiceInfo { private final boolean enabled; - private final String limbo; - private final File authfile; - private final int timeout; + private final String authfile; + private final int ipLimit; - public AuthServiceInfo(boolean enabled, String limbo, File authfile, int timeout) { + public AuthServiceInfo(boolean enabled, String authfile, int timeout) { this.enabled = enabled; - this.limbo = limbo; this.authfile = authfile; - this.timeout = timeout; + this.ipLimit = timeout; } public boolean isEnabled() { return enabled; } - public String getLimbo() { - return limbo; - } - - public File getAuthfile() { + public String getAuthfile() { return authfile; } - public int getTimeout() { - return timeout; + public int getIpLimit() { + return ipLimit; } } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandChangePassword.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandChangePassword.java new file mode 100644 index 0000000..aad99cb --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/command/CommandChangePassword.java @@ -0,0 +1,34 @@ +package net.md_5.bungee.command; + +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.eaglercraft.AuthSystem; + +public class CommandChangePassword extends Command { + private final AuthSystem authSystem; + + public CommandChangePassword(AuthSystem authSystem) { + super("changepassword", "bungeecord.command.eag.changepassword", new String[] { "changepwd", "changepasswd", "changepass" }); + this.authSystem = authSystem; + } + + @Override + public void execute(final CommandSender sender, final String[] args) { + if (!(sender instanceof ProxiedPlayer)) { + return; + } + String username = sender.getName(); + if (args.length == 0 || args.length == 1) { + sender.sendMessage("\u00A7cUsage: /changepassword "); + } else if (this.authSystem.login(username, args[0])) { + if (this.authSystem.changePass(username, args[1])) { + sender.sendMessage("\u00A7cPassword changed successfully!"); + } else { + sender.sendMessage("\u00A7cUnable to change your password..."); + } + } else { + sender.sendMessage("\u00A7cThe old password specified is incorrect!"); + } + } +} \ No newline at end of file diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java index e48d582..57d12f2 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java @@ -70,7 +70,7 @@ public class YamlConfig implements ConfigurationAdapter { } final Map permissions = this.get("permissions", new HashMap()); if (permissions.isEmpty()) { - permissions.put("default", Arrays.asList("bungeecord.command.server", "bungeecord.command.list", "bungeecord.command.eag.domain")); + permissions.put("default", Arrays.asList("bungeecord.command.server", "bungeecord.command.list", "bungeecord.command.eag.domain", "bungeecord.command.eag.changepassword")); permissions.put("admin", Arrays.asList("bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload", "bungeecord.command.eag.ban", "bungeecord.command.eag.banwildcard", "bungeecord.command.eag.banip", "bungeecord.command.eag.banregex", "bungeecord.command.eag.reloadban", "bungeecord.command.eag.banned", "bungeecord.command.eag.banlist", "bungeecord.command.eag.unban", "bungeecord.command.eag.ratelimit", @@ -274,9 +274,8 @@ public class YamlConfig implements ConfigurationAdapter { @Override public AuthServiceInfo getAuthSettings() { - //final Map auth = this.get("authservice", new HashMap()); - //return new AuthServiceInfo(this.get("enabled", true, auth), this.get("limbo", "lobby", auth), new File(this.get("authfile", "passwords.yml", auth)), this.get("timeout", 30, auth)); - return null; + final Map auth = this.get("authservice", new HashMap()); + return new AuthServiceInfo(this.get("enabled", false, auth), this.get("authfile", "auth.uwu", auth), this.get("ip_limit", 0, auth)); } @Override diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 9786915..e09318a 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -35,6 +35,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.api.event.ProxyPingEvent; +import net.md_5.bungee.eaglercraft.AuthHandler; import net.md_5.bungee.eaglercraft.BanList; import net.md_5.bungee.eaglercraft.BanList.BanCheck; import net.md_5.bungee.eaglercraft.BanList.BanState; @@ -47,15 +48,7 @@ import net.md_5.bungee.netty.PacketDecoder; import net.md_5.bungee.netty.PacketHandler; import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.protocol.Forge; -import net.md_5.bungee.protocol.packet.DefinedPacket; -import net.md_5.bungee.protocol.packet.Packet1Login; -import net.md_5.bungee.protocol.packet.Packet2Handshake; -import net.md_5.bungee.protocol.packet.PacketCDClientStatus; -import net.md_5.bungee.protocol.packet.PacketFAPluginMessage; -import net.md_5.bungee.protocol.packet.PacketFCEncryptionResponse; -import net.md_5.bungee.protocol.packet.PacketFDEncryptionRequest; -import net.md_5.bungee.protocol.packet.PacketFEPing; -import net.md_5.bungee.protocol.packet.PacketFFKick; +import net.md_5.bungee.protocol.packet.*; public class InitialHandler extends PacketHandler implements PendingConnection { private final ProxyServer bungee; @@ -259,10 +252,15 @@ public class InitialHandler extends PacketHandler implements PendingConnection { userCon.getAttachment().put("origin", origin); } userCon.init(); - this.bungee.getPluginManager().callEvent(new PostLoginEvent(userCon)); - ((HandlerBoss) this.ch.getHandle().pipeline().get((Class) HandlerBoss.class)).setHandler(new UpstreamBridge(this.bungee, userCon)); - final ServerInfo server = this.bungee.getReconnectHandler().getServer(userCon); - userCon.connect(server, true); + HandlerBoss handlerBoss = ((HandlerBoss) this.ch.getHandle().pipeline().get((Class) HandlerBoss.class)); + if (BungeeCord.getInstance().config.getAuthInfo().isEnabled()) { + handlerBoss.setHandler(new AuthHandler(this.bungee, userCon, handlerBoss)); + } else { + this.bungee.getPluginManager().callEvent(new PostLoginEvent(userCon)); + handlerBoss.setHandler(new UpstreamBridge(this.bungee, userCon)); + final ServerInfo server = this.bungee.getReconnectHandler().getServer(userCon); + userCon.connect(server, true); + } this.thisState = State.FINISHED; throw new CancelSendSignal(); } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java index 6e550f0..59d5a76 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java @@ -55,7 +55,7 @@ public class UpstreamBridge extends PacketHandler { @Override public void handle(final byte[] buf) throws Exception { EntityMap.rewrite(buf, this.con.getClientEntityId(), this.con.getServerEntityId()); - if (this.con.getServer() != null) { + if (this.con.getServer() != null && this.con.getServer().getCh() != null) { this.con.getServer().getCh().write(buf); } } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java new file mode 100644 index 0000000..6789d77 --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java @@ -0,0 +1,149 @@ +package net.md_5.bungee.eaglercraft; + +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.ServerConnection; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.Util; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.connection.UpstreamBridge; +import net.md_5.bungee.netty.ChannelWrapper; +import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.netty.PacketHandler; +import net.md_5.bungee.protocol.packet.Packet1Login; +import net.md_5.bungee.protocol.packet.Packet9Respawn; +import net.md_5.bungee.protocol.packet.Packet0DPositionAndLook; +import net.md_5.bungee.protocol.packet.Packet3Chat; +import net.md_5.bungee.protocol.packet.Packet0KeepAlive; +import net.md_5.bungee.protocol.packet.PacketCCSettings; + +public class AuthHandler extends PacketHandler { + private static final AuthSystem authSystem = BungeeCord.getInstance().authSystem; + + private final ProxyServer bungee; + private final UserConnection con; + private final HandlerBoss handlerBoss; + private final String username; + + private boolean loggedIn = false; + + public AuthHandler(final ProxyServer bungee, final UserConnection con, final HandlerBoss handlerBoss) { + this.bungee = bungee; + this.con = con; + this.handlerBoss = handlerBoss; + + this.username = this.con.getName(); + + new Thread(() -> { + for (int i = 0; i < 120; i++) { + if (this.loggedIn) break; + try { + Thread.sleep(250); + } catch (InterruptedException ignored) { } + } + if (this.loggedIn) return; + this.con.disconnect("You did not login in time!"); + }).start(); + + this.con.unsafe().sendPacket(new Packet1Login(0, "END", (byte) 2, 1, (byte) 0, (byte) 0, (byte) 1)); + this.con.unsafe().sendPacket(new Packet9Respawn(1, (byte) 0, (byte) 2, (short) 255, "END")); + this.con.unsafe().sendPacket(new Packet0DPositionAndLook(0, 0, 0, 0, 0f, 0f, true)); + + if (authSystem.isRegistered(this.username)) { + this.con.sendMessage("\u00A7cPlease login to continue! /login "); + } else { + this.con.sendMessage("\u00A7cPlease register to continue! /register "); + } + } + + @Override + public void exception(final Throwable t) throws Exception { + this.con.disconnect(Util.exception(t)); + } + + @Override + public void disconnected(final ChannelWrapper channel) throws Exception { + this.loggedIn = true; + } + + @Override + public void handle(final Packet0KeepAlive alive) throws Exception { + if (alive.getRandomId() == this.con.getSentPingId()) { + final int newPing = (int) (System.currentTimeMillis() - this.con.getSentPingTime()); + this.con.setPing(newPing); + } + } + + @Override + public void handle(final Packet3Chat chat) throws Exception { + String message = chat.getMessage(); + if (message.startsWith("/")) { + String[] args = message.substring(1).trim().split(" "); + switch (args[0]) { + case "login": + case "l": + if (args.length == 1) { + this.con.sendMessage("\u00A7cYou must specify a password to login! /login "); + } else if (!authSystem.isRegistered(this.username)) { + this.con.sendMessage("\u00A7cThis username is not registered on this server!"); + } else if (authSystem.login(this.username, args[1])) { + this.onLogin(); + } else { + this.con.sendMessage("\u00A7cThat password is invalid!"); + } + break; + case "register": + case "reg": + if (args.length == 1 || args.length == 2) { + this.con.sendMessage("\u00A7cUsage: /" + args[0].toLowerCase() + " "); + } else if (!args[1].equals(args[2])) { + this.con.sendMessage("\u00A7cThose passwords do not match!"); + } else if (authSystem.isRegistered(this.username)) { + this.con.sendMessage("\u00A7cThis username is already registered!"); + } else if (authSystem.register(this.username, args[1], this.con.getAddress().toString())) { + this.onLogin(); + } else { + this.con.sendMessage("\u00A7cUnable to register..."); + } + break; + case "changepassword": + case "changepasswd": + case "changepwd": + case "changepass": + if (args.length == 1 || args.length == 2) { + this.con.sendMessage("\u00A7cUsage: /" + args[0].toLowerCase() + " "); + } else if (authSystem.login(this.username, args[1])) { + if (authSystem.changePass(this.username, args[2])) { + this.con.sendMessage("\u00A7cPassword changed successfully!"); + } else { + this.con.sendMessage("\u00A7cUnable to change your password..."); + } + } else { + this.con.sendMessage("\u00A7cThe old password specified is incorrect!"); + } + break; + default: + } + } + } + + private void onLogin() { + this.loggedIn = true; + this.bungee.getPluginManager().callEvent(new PostLoginEvent(this.con)); + handlerBoss.setHandler(new UpstreamBridge(this.bungee, this.con)); + final ServerInfo server = this.bungee.getReconnectHandler().getServer(this.con); + this.con.setServer(new ServerConnection(null, null)); + this.con.connect(server, true); + } + + @Override + public void handle(final PacketCCSettings settings) throws Exception { + this.con.setSettings(settings); + } + + @Override + public String toString() { + return "[" + this.con.getName() + "] -> AuthHandler"; + } +} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java new file mode 100644 index 0000000..f26dede --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java @@ -0,0 +1,136 @@ +package net.md_5.bungee.eaglercraft; + +import com.google.common.hash.Hashing; +import net.md_5.bungee.api.config.AuthServiceInfo; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class AuthSystem { + private final String authFileName; + private final int ipLimit; + + public AuthSystem(AuthServiceInfo authInfo) { + this.authFileName = authInfo.getAuthfile(); + this.ipLimit = authInfo.getIpLimit(); + + this.readDatabase(); + } + + private static class AuthData { + public String passHash; + public Set ips; + + public AuthData(String p, Set i) { + passHash = p; + ips = i; + } + } + + private final Map database = new HashMap<>(); + + public boolean register(String username, String password, String ip) { + synchronized (database) { + AuthData authData = database.get(username); + if (authData != null) return false; + if (isIpAtTheLimit(ip)) return false; + String hash = Hashing.sha256().hashString(password).toString(); + Set initIps = new HashSet<>(); + initIps.add(ip); + database.put(username, new AuthData(hash, initIps)); + writeDatabase(); + return true; + } + } + + public boolean isRegistered(String username) { + synchronized (database) { + return database.containsKey(username); + } + } + + public boolean changePass(String username, String password) { + synchronized (database) { + AuthData authData = database.get(username); + authData.passHash = Hashing.sha256().hashString(password).toString(); + writeDatabase(); + return true; + } + } + + public boolean login(String username, String password) { + synchronized (database) { + AuthData authData = database.get(username); + if (authData == null) return false; + return authData.passHash.equals(Hashing.sha256().hashString(password).toString()); + } + } + + private boolean isIpAtTheLimit(String ip) { + synchronized (database) { + if (this.ipLimit <= 0) return false; + int num = 0; + for (AuthData authData : database.values()) { + if (authData.ips.contains(ip)) num++; + if (num >= this.ipLimit) { + return true; + } + } + return false; + } + } + + // only use once, on load + public void readDatabase() { + synchronized (database) { + try { + File authFile = new File(this.authFileName); + if (!authFile.exists()) authFile.createNewFile(); + + Map cache = new HashMap<>(); + + String[] lines = new String(Files.readAllBytes(authFile.toPath())).trim().split("\n"); + if (lines.length == 1 && lines[0].isEmpty()) return; + for (String line : lines) { + String[] pieces = line.split("\u0000"); + cache.put(pieces[0], new AuthData(pieces[2], new HashSet<>(Arrays.asList(pieces[1].split("§"))))); + } + + database.clear(); + database.putAll(cache); + cache.clear(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private void writeDatabase() { + synchronized (database) { + StringBuilder out = new StringBuilder(); + + for (String username : database.keySet()) { + AuthData entry = database.get(username); + out.append(username); + out.append("\u0000"); + out.append(String.join("§", entry.ips)); + out.append("\u0000"); + out.append(entry.passHash); + out.append("\n"); + } + + try { + Files.write(Paths.get(this.authFileName), out.toString().getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/PluginEaglerAuth.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/PluginEaglerAuth.java deleted file mode 100644 index 14bd16a..0000000 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/PluginEaglerAuth.java +++ /dev/null @@ -1,52 +0,0 @@ -package net.md_5.bungee.eaglercraft; - -import java.util.Collections; -import java.util.HashSet; - -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.api.event.ServerConnectEvent; -import net.md_5.bungee.api.plugin.Listener; -import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.api.plugin.PluginDescription; -import net.md_5.bungee.event.EventHandler; - -public class PluginEaglerAuth extends Plugin implements Listener { - - public PluginEaglerAuth() { - super(new PluginDescription("EaglerAuth", PluginEaglerAuth.class.getName(), "1.0.0", "LAX1DUDE", Collections.emptySet(), null)); - } - - @Override - public void onLoad() { - - } - - @Override - public void onEnable() { - getProxy().getPluginManager().registerListener(this, this); - } - - @Override - public void onDisable() { - - } - - private final HashSet playersInLimbo = new HashSet(); - - @EventHandler - public void onPostLogin(PostLoginEvent event) { - //playersInLimbo.add(event.getPlayer()); - } - - @EventHandler - public void onServerConnect(ServerConnectEvent event) { - ProxiedPlayer player = event.getPlayer(); - //if(playersInLimbo.contains(player)) { - // event.setCancelled(true); - // player.unsafe().sendPacket(new Packet1Login(0, "END", (byte) 1, 1, (byte) 0, (byte) 0, (byte) 1)); - // player.unsafe().sendPacket(new Packet9Respawn(1, (byte) 0, (byte) 1, (short) 255, "END")); - //} - } - -} diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/protocol/packet/AbstractPacketHandler.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/protocol/packet/AbstractPacketHandler.java index 855560a..83ad08e 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/protocol/packet/AbstractPacketHandler.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/protocol/packet/AbstractPacketHandler.java @@ -55,4 +55,7 @@ public abstract class AbstractPacketHandler { public void handle(final PacketFFKick kick) throws Exception { } + + public void handle(final Packet0DPositionAndLook positionAndLook) throws Exception { + } } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/protocol/packet/Packet0DPositionAndLook.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/protocol/packet/Packet0DPositionAndLook.java new file mode 100644 index 0000000..0911cd2 --- /dev/null +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/protocol/packet/Packet0DPositionAndLook.java @@ -0,0 +1,114 @@ +package net.md_5.bungee.protocol.packet; + +import io.netty.buffer.ByteBuf; + +public class Packet0DPositionAndLook extends DefinedPacket { + private double x; + private double y; + private double stance; + private double z; + private float yaw; + private float pitch; + private boolean onGround; + + private Packet0DPositionAndLook() { + super(13); + } + + public Packet0DPositionAndLook(final int x, final int y, final double stance, final double z, final float yaw, final float pitch, final boolean onGround) { + this(); + this.x = x; + this.y = y; + this.stance = stance; + this.z = z; + this.yaw = yaw; + this.pitch = pitch; + this.onGround = onGround; + } + + @Override + public void read(final ByteBuf buf) { + this.x = buf.readDouble(); + this.y = buf.readDouble(); + this.stance = buf.readDouble(); + this.z = buf.readDouble(); + this.yaw = buf.readFloat(); + this.pitch = buf.readFloat(); + this.onGround = buf.readBoolean(); + } + + @Override + public void write(final ByteBuf buf) { + buf.writeDouble(this.x); + buf.writeDouble(this.y); + buf.writeDouble(this.stance); + buf.writeDouble(this.z); + buf.writeFloat(this.yaw); + buf.writeFloat(this.pitch); + buf.writeBoolean(this.onGround); + } + + @Override + public void handle(final AbstractPacketHandler handler) throws Exception { + handler.handle(this); + } + + @Override + public String toString() { + return "Packet0DPositionAndLook(tooLazyToFillThisInLOL)"; + } + + @Override + public boolean equals(final Object o) { + if (o == this) { + return true; + } + if (!(o instanceof Packet0DPositionAndLook)) { + return false; + } + final Packet0DPositionAndLook other = (Packet0DPositionAndLook) o; + if (!other.canEqual(this)) { + return false; + } + if (this.x != other.x) { + return false; + } + if (this.y != other.y) { + return false; + } + if (this.stance != other.stance) { + return false; + } + if (this.z != other.z) { + return false; + } + if (this.yaw != other.yaw) { + return false; + } + if (this.pitch != other.pitch) { + return false; + } + if (this.onGround != other.onGround) { + return false; + } + return false; + } + + @Override + public int hashCode() { + final int PRIME = 31; + int result = 1; + result = result * 31 + Double.hashCode(this.x); + result = result * 31 + Double.hashCode(this.y); + result = result * 31 + Double.hashCode(this.stance); + result = result * 31 + Double.hashCode(this.z); + result = result * 31 + Float.hashCode(this.yaw); + result = result * 31 + Float.hashCode(this.pitch); + result = result * 31 + Boolean.hashCode(this.onGround); + return result; + } + + public boolean canEqual(final Object other) { + return other instanceof Packet0DPositionAndLook; + } +} From 17e612a708f76dacb0749f97cf935e0de6db4d14 Mon Sep 17 00:00:00 2001 From: ayunami2000 Date: Sat, 23 Jul 2022 21:51:10 -0400 Subject: [PATCH 2/5] Escape section symbols so nothing freaks out --- .../src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java index f26dede..0f57b64 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java @@ -100,7 +100,7 @@ public class AuthSystem { if (lines.length == 1 && lines[0].isEmpty()) return; for (String line : lines) { String[] pieces = line.split("\u0000"); - cache.put(pieces[0], new AuthData(pieces[2], new HashSet<>(Arrays.asList(pieces[1].split("§"))))); + cache.put(pieces[0], new AuthData(pieces[2], new HashSet<>(Arrays.asList(pieces[1].split("\u00A7"))))); } database.clear(); @@ -120,7 +120,7 @@ public class AuthSystem { AuthData entry = database.get(username); out.append(username); out.append("\u0000"); - out.append(String.join("§", entry.ips)); + out.append(String.join("\u00A7", entry.ips)); out.append("\u0000"); out.append(entry.passHash); out.append("\n"); From ab5b8f6cb5a9b061c36ef70ca1478f6cf1b22943 Mon Sep 17 00:00:00 2001 From: ayunami2000 Date: Sun, 24 Jul 2022 12:53:57 -0400 Subject: [PATCH 3/5] Now compatible with AuthMe databases! --- .../net/md_5/bungee/config/YamlConfig.java | 2 +- .../md_5/bungee/eaglercraft/AuthHandler.java | 6 +- .../md_5/bungee/eaglercraft/AuthSystem.java | 100 +++++++++++++----- 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java index 57d12f2..06a5d89 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java @@ -275,7 +275,7 @@ public class YamlConfig implements ConfigurationAdapter { @Override public AuthServiceInfo getAuthSettings() { final Map auth = this.get("authservice", new HashMap()); - return new AuthServiceInfo(this.get("enabled", false, auth), this.get("authfile", "auth.uwu", auth), this.get("ip_limit", 0, auth)); + return new AuthServiceInfo(this.get("enabled", false, auth), this.get("authfile", "auths.db", auth), this.get("ip_limit", 0, auth)); } @Override diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java index 6789d77..d94c281 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java @@ -63,7 +63,7 @@ public class AuthHandler extends PacketHandler { } @Override - public void disconnected(final ChannelWrapper channel) throws Exception { + public void disconnected(final ChannelWrapper channel) { this.loggedIn = true; } @@ -88,6 +88,7 @@ public class AuthHandler extends PacketHandler { } else if (!authSystem.isRegistered(this.username)) { this.con.sendMessage("\u00A7cThis username is not registered on this server!"); } else if (authSystem.login(this.username, args[1])) { + this.con.sendMessage("\u00A7cLogging in..."); this.onLogin(); } else { this.con.sendMessage("\u00A7cThat password is invalid!"); @@ -101,7 +102,8 @@ public class AuthHandler extends PacketHandler { this.con.sendMessage("\u00A7cThose passwords do not match!"); } else if (authSystem.isRegistered(this.username)) { this.con.sendMessage("\u00A7cThis username is already registered!"); - } else if (authSystem.register(this.username, args[1], this.con.getAddress().toString())) { + } else if (authSystem.register(this.username, args[1], this.con.getAddress().getAddress().getHostAddress())) { + this.con.sendMessage("\u00A7cSuccessfully registered and logging in..."); this.onLogin(); } else { this.con.sendMessage("\u00A7cUnable to register..."); diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java index 0f57b64..cbd881b 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java @@ -1,17 +1,18 @@ package net.md_5.bungee.eaglercraft; -import com.google.common.hash.Hashing; +import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.config.AuthServiceInfo; import java.io.File; import java.io.IOException; +import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.Arrays; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; public class AuthSystem { private final String authFileName; @@ -25,12 +26,16 @@ public class AuthSystem { } private static class AuthData { - public String passHash; - public Set ips; + public String salt; + public String hash; + public String ip; + public long timestamp; - public AuthData(String p, Set i) { - passHash = p; - ips = i; + public AuthData(String salt, String hash, String ip, long timestamp) { + this.salt = salt; + this.hash = hash; + this.ip = ip; + this.timestamp = timestamp; } } @@ -41,10 +46,9 @@ public class AuthSystem { AuthData authData = database.get(username); if (authData != null) return false; if (isIpAtTheLimit(ip)) return false; - String hash = Hashing.sha256().hashString(password).toString(); - Set initIps = new HashSet<>(); - initIps.add(ip); - database.put(username, new AuthData(hash, initIps)); + String salt = createSalt(16); + String hash = getSaltedHash(password, salt); + database.put(username, new AuthData(salt, hash, ip, System.currentTimeMillis())); writeDatabase(); return true; } @@ -59,7 +63,8 @@ public class AuthSystem { public boolean changePass(String username, String password) { synchronized (database) { AuthData authData = database.get(username); - authData.passHash = Hashing.sha256().hashString(password).toString(); + authData.salt = createSalt(16); + authData.hash = getSaltedHash(password, authData.salt); writeDatabase(); return true; } @@ -69,7 +74,7 @@ public class AuthSystem { synchronized (database) { AuthData authData = database.get(username); if (authData == null) return false; - return authData.passHash.equals(Hashing.sha256().hashString(password).toString()); + return authData.hash.equals(getSaltedHash(password, authData.salt)); } } @@ -78,7 +83,7 @@ public class AuthSystem { if (this.ipLimit <= 0) return false; int num = 0; for (AuthData authData : database.values()) { - if (authData.ips.contains(ip)) num++; + if (authData.ip.equals(ip)) num++; if (num >= this.ipLimit) { return true; } @@ -94,18 +99,24 @@ public class AuthSystem { File authFile = new File(this.authFileName); if (!authFile.exists()) authFile.createNewFile(); - Map cache = new HashMap<>(); + database.clear(); String[] lines = new String(Files.readAllBytes(authFile.toPath())).trim().split("\n"); if (lines.length == 1 && lines[0].isEmpty()) return; + boolean alreadyLogged = false; for (String line : lines) { - String[] pieces = line.split("\u0000"); - cache.put(pieces[0], new AuthData(pieces[2], new HashSet<>(Arrays.asList(pieces[1].split("\u00A7"))))); + String[] pieces = line.split(":"); + if (!pieces[1].startsWith("$SHA$")) { + if (!alreadyLogged) { + alreadyLogged = true; + BungeeCord.getInstance().getLogger().warning("One or more entries in the auth file are hashed in an unsupported format! (not SHA-256!)"); + } + // continue; + } + String[] saltHash = pieces[1].substring(pieces[1].substring(1).indexOf('$') + 2).split("\\$"); + database.put(pieces[0], new AuthData(saltHash[0], saltHash[1], pieces[2], Long.parseLong(pieces[3]))); } - database.clear(); - database.putAll(cache); - cache.clear(); } catch (IOException e) { e.printStackTrace(); } @@ -119,10 +130,14 @@ public class AuthSystem { for (String username : database.keySet()) { AuthData entry = database.get(username); out.append(username); - out.append("\u0000"); - out.append(String.join("\u00A7", entry.ips)); - out.append("\u0000"); - out.append(entry.passHash); + out.append(":$SHA$"); + out.append(entry.salt); + out.append("$"); + out.append(entry.hash); + out.append(":"); + out.append(entry.ip); + out.append(":"); + out.append(entry.timestamp); out.append("\n"); } @@ -133,4 +148,37 @@ public class AuthSystem { } } } + + // hashing used is based on hashing from AuthMe + + private static final SecureRandom rnd = new SecureRandom(); + + private static String getSHA256(String message) { + try { + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + sha256.reset(); + sha256.update(message.getBytes()); + byte[] digest = sha256.digest(); + return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest)); + } catch (NoSuchAlgorithmException e) { + return ""; + } + } + + private static String getSaltedHash(String message, String salt) { + return getSHA256(getSHA256(message) + salt); + } + + private static String createSalt(int length) { + try { + byte[] msg = new byte[40]; + rnd.nextBytes(msg); + MessageDigest sha1 = MessageDigest.getInstance("SHA1"); + sha1.reset(); + byte[] digest = sha1.digest(msg); + return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest)).substring(0, length); + } catch (NoSuchAlgorithmException e) { + return ""; + } + } } \ No newline at end of file From 7c1a55918d76ea35784d435e52b27dd5cac2775a Mon Sep 17 00:00:00 2001 From: ayunami2000 Date: Sun, 24 Jul 2022 13:19:06 -0400 Subject: [PATCH 4/5] Add change password permission to old configs... ...and add optional custom join messages when authing. --- .../net/md_5/bungee/api/config/AuthServiceInfo.java | 10 +++++++++- .../main/java/net/md_5/bungee/config/YamlConfig.java | 9 +++++++-- .../java/net/md_5/bungee/eaglercraft/AuthHandler.java | 2 ++ .../java/net/md_5/bungee/eaglercraft/AuthSystem.java | 9 +++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java index a971f7c..454026b 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java @@ -1,15 +1,19 @@ package net.md_5.bungee.api.config; +import java.util.List; + public class AuthServiceInfo { private final boolean enabled; private final String authfile; private final int ipLimit; + private final List joinMessages; - public AuthServiceInfo(boolean enabled, String authfile, int timeout) { + public AuthServiceInfo(boolean enabled, String authfile, int timeout, List joinMessages) { this.enabled = enabled; this.authfile = authfile; this.ipLimit = timeout; + this.joinMessages = joinMessages; } public boolean isEnabled() { @@ -24,4 +28,8 @@ public class AuthServiceInfo { return ipLimit; } + public List getJoinMessages() { + return joinMessages; + } + } diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java index 06a5d89..e57144e 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java @@ -18,6 +18,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; @@ -68,13 +69,15 @@ public class YamlConfig implements ConfigurationAdapter { } catch (IOException ex) { throw new RuntimeException("Could not load configuration!", ex); } - final Map permissions = this.get("permissions", new HashMap()); + final Map> permissions = this.get("permissions", new HashMap>()); if (permissions.isEmpty()) { permissions.put("default", Arrays.asList("bungeecord.command.server", "bungeecord.command.list", "bungeecord.command.eag.domain", "bungeecord.command.eag.changepassword")); permissions.put("admin", Arrays.asList("bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload", "bungeecord.command.eag.ban", "bungeecord.command.eag.banwildcard", "bungeecord.command.eag.banip", "bungeecord.command.eag.banregex", "bungeecord.command.eag.reloadban", "bungeecord.command.eag.banned", "bungeecord.command.eag.banlist", "bungeecord.command.eag.unban", "bungeecord.command.eag.ratelimit", "bungeecord.command.eag.blockdomain", "bungeecord.command.eag.blockdomainname", "bungeecord.command.eag.unblockdomain")); + } else if (this.get("authservice", new HashMap()).isEmpty() && permissions.containsKey("default") && !permissions.get("default").contains("bungeecord.command.eag.changepassword")) { + permissions.get("default").add("bungeecord.command.eag.changepassword"); } this.get("groups", new HashMap()); } @@ -275,7 +278,9 @@ public class YamlConfig implements ConfigurationAdapter { @Override public AuthServiceInfo getAuthSettings() { final Map auth = this.get("authservice", new HashMap()); - return new AuthServiceInfo(this.get("enabled", false, auth), this.get("authfile", "auths.db", auth), this.get("ip_limit", 0, auth)); + final List defaultJoinMessages = new ArrayList(); + defaultJoinMessages.add("&3Welcome to my &aEaglercraftBungee &3server!"); + return new AuthServiceInfo(this.get("enabled", false, auth), this.get("authfile", "auths.db", auth), this.get("ip_limit", 0, auth), this.get("join_messages", defaultJoinMessages, auth)); } @Override diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java index d94c281..60f0597 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java @@ -50,6 +50,8 @@ public class AuthHandler extends PacketHandler { this.con.unsafe().sendPacket(new Packet9Respawn(1, (byte) 0, (byte) 2, (short) 255, "END")); this.con.unsafe().sendPacket(new Packet0DPositionAndLook(0, 0, 0, 0, 0f, 0f, true)); + this.con.sendMessages(authSystem.joinMessages); + if (authSystem.isRegistered(this.username)) { this.con.sendMessage("\u00A7cPlease login to continue! /login "); } else { diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java index cbd881b..1c8f40d 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java @@ -1,6 +1,7 @@ package net.md_5.bungee.eaglercraft; import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.config.AuthServiceInfo; import java.io.File; @@ -12,15 +13,23 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.HashMap; +import java.util.List; import java.util.Map; public class AuthSystem { private final String authFileName; private final int ipLimit; + public final String[] joinMessages; public AuthSystem(AuthServiceInfo authInfo) { this.authFileName = authInfo.getAuthfile(); this.ipLimit = authInfo.getIpLimit(); + List listJoinMessages = authInfo.getJoinMessages(); + String[] arrayJoinMessages = new String[listJoinMessages.size()]; + for (int i = 0; i < listJoinMessages.size(); i++) { + arrayJoinMessages[i] = ChatColor.translateAlternateColorCodes('&', listJoinMessages.get(i)); + } + this.joinMessages = arrayJoinMessages; this.readDatabase(); } From 1396344f37ac1e111b3a9fe0b9981ddb7e8a9f29 Mon Sep 17 00:00:00 2001 From: ayunami2000 Date: Sun, 24 Jul 2022 15:02:42 -0400 Subject: [PATCH 5/5] Fix tablist when auth is enabled. --- .../src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java index 60f0597..65b2dfc 100644 --- a/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java +++ b/eaglercraftbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java @@ -46,7 +46,7 @@ public class AuthHandler extends PacketHandler { this.con.disconnect("You did not login in time!"); }).start(); - this.con.unsafe().sendPacket(new Packet1Login(0, "END", (byte) 2, 1, (byte) 0, (byte) 0, (byte) 1)); + this.con.unsafe().sendPacket(new Packet1Login(0, "END", (byte) 2, 1, (byte) 0, (byte) 0, (byte) this.con.getPendingConnection().getListener().getTabListSize())); this.con.unsafe().sendPacket(new Packet9Respawn(1, (byte) 0, (byte) 2, (short) 255, "END")); this.con.unsafe().sendPacket(new Packet0DPositionAndLook(0, 0, 0, 0, 0f, 0f, true));