diff --git a/src/main/java/net/elytrium/limboauth/LimboAuth.java b/src/main/java/net/elytrium/limboauth/LimboAuth.java index 04f2d21d..cbdfb28c 100644 --- a/src/main/java/net/elytrium/limboauth/LimboAuth.java +++ b/src/main/java/net/elytrium/limboauth/LimboAuth.java @@ -112,6 +112,7 @@ import net.elytrium.limboauth.listener.BackendEndpointsListener; import net.elytrium.limboauth.model.RegisteredPlayer; import net.elytrium.limboauth.model.SQLRuntimeException; +import net.elytrium.limboauth.model.UnsafePlayer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.ComponentSerializer; import net.kyori.adventure.title.Title; @@ -153,6 +154,7 @@ public class LimboAuth { private final Map bruteforceCache = new ConcurrentHashMap<>(); private final Map postLoginTasks = new ConcurrentHashMap<>(); private final Set unsafePasswords = new HashSet<>(); + private final Set unsafePlayerPassSet = new HashSet<>(); private final Set forcedPreviously = Collections.synchronizedSet(new HashSet<>()); private final Set pendingLogins = ConcurrentHashMap.newKeySet(); @@ -309,6 +311,30 @@ public void reload() { } } + try { + this.unsafePlayerPassSet.clear(); + Path craftrisePath = Paths.get(this.dataDirectoryFile.getAbsolutePath(), Settings.IMP.MAIN.UNSAFE_SET_PASSWORDS_FILE); + if (!craftrisePath.toFile().exists()) { + Files.copy(Objects.requireNonNull(this.getClass().getResourceAsStream("/unsafe_pass_user_set.txt")), craftrisePath); + } + + List stream = Files.readAllLines(craftrisePath); + stream.forEach(account -> { + String[] parts = account.split(":"); + if (parts.length == 2) { + String username = parts[0]; + String password = parts[1]; + UnsafePlayer unsafePlayer = new UnsafePlayer(username, password); + this.unsafePlayerPassSet.add(unsafePlayer); + } + }); + + this.server.sendMessage(Component.text("Loadded " + this.unsafePlayerPassSet.size() + " unsafe user & password set!")); + + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + this.cachedAuthChecks.clear(); this.premiumCache.clear(); this.bruteforceCache.clear(); @@ -947,6 +973,10 @@ public Set getUnsafePasswords() { return this.unsafePasswords; } + public Set getUnsafePlayerPassSet() { + return this.unsafePlayerPassSet; + } + public ProxyServer getServer() { return this.server; } diff --git a/src/main/java/net/elytrium/limboauth/Settings.java b/src/main/java/net/elytrium/limboauth/Settings.java index fc2344e8..49430a15 100644 --- a/src/main/java/net/elytrium/limboauth/Settings.java +++ b/src/main/java/net/elytrium/limboauth/Settings.java @@ -73,6 +73,7 @@ public static class MAIN { @Comment("Max password length for the BCrypt hashing algorithm, which is used in this plugin, can't be higher than 71. You can set a lower value than 71.") public int MAX_PASSWORD_LENGTH = 71; public boolean CHECK_PASSWORD_STRENGTH = true; + public String UNSAFE_SET_PASSWORDS_FILE = "unsafe_pass_user_set.txt"; public String UNSAFE_PASSWORDS_FILE = "unsafe_passwords.txt"; @Comment({ "Players with premium nicknames should register/auth if this option is enabled", @@ -437,6 +438,7 @@ public static class STRINGS { public String REGISTER_PASSWORD_TOO_SHORT = "{PRFX} &cYou entered a too short password, use a different one!"; public String REGISTER_PASSWORD_TOO_LONG = "{PRFX} &cYou entered a too long password, use a different one!"; public String REGISTER_PASSWORD_UNSAFE = "{PRFX} &cYour password is unsafe, use a different one!"; + public String REGISTER_PASSWORD_UNSAFE_PASS_USER_SET = "{PRFX} &cYour password found in a database leak. You need to change your password to play in this server!"; public String REGISTER_SUCCESSFUL = "{PRFX} &aSuccessfully registered!"; @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String REGISTER_TITLE = "{PRFX}"; diff --git a/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java index fd32fdd6..fe92ef96 100644 --- a/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java +++ b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java @@ -33,6 +33,7 @@ import java.text.MessageFormat; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -49,6 +50,7 @@ import net.elytrium.limboauth.migration.MigrationHash; import net.elytrium.limboauth.model.RegisteredPlayer; import net.elytrium.limboauth.model.SQLRuntimeException; +import net.elytrium.limboauth.model.UnsafePlayer; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.text.Component; import net.kyori.adventure.title.Title; @@ -85,6 +87,7 @@ public class AuthSessionHandler implements LimboSessionHandler { private static Component registerPasswordTooLong; private static Component registerPasswordTooShort; private static Component registerPasswordUnsafe; + private static Component registerPasswordUnsafeUserPassSet; private static Component loginSuccessful; private static Component sessionExpired; @Nullable @@ -212,7 +215,8 @@ public void onChat(String message) { Command command = Command.parse(args[0]); if (command == Command.REGISTER && !this.totpState && this.playerInfo == null) { String password = args[1]; - if (this.checkPasswordsRepeat(args) && this.checkPasswordLength(password) && this.checkPasswordStrength(password)) { + if (this.checkPasswordsRepeat(args) && this.checkPasswordLength(password) + && this.checkPasswordStrength(password) && this.checkUnsafePassSet(this.proxyPlayer.getUsername(), password)) { this.saveTempPassword(password); RegisteredPlayer registeredPlayer = new RegisteredPlayer(this.proxyPlayer).setPassword(password); @@ -408,6 +412,19 @@ private boolean checkPasswordStrength(String password) { } } + private boolean checkUnsafePassSet(String username, String password) { + Set unsafePlayers = this.plugin.getUnsafePlayerPassSet(); + + for (UnsafePlayer unsafePlayer : unsafePlayers) { + if (username.equalsIgnoreCase(unsafePlayer.getUsername()) && unsafePlayer.getPassword().equalsIgnoreCase(password)) { + this.proxyPlayer.sendMessage(registerPasswordUnsafeUserPassSet); + return false; + } + } + + return true; + } + public void finishLogin() { this.proxyPlayer.sendMessage(loginSuccessful); if (loginSuccessfulTitle != null) { @@ -511,6 +528,7 @@ public static void reload() { registerPasswordTooLong = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_TOO_LONG); registerPasswordTooShort = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_TOO_SHORT); registerPasswordUnsafe = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_UNSAFE); + registerPasswordUnsafeUserPassSet = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_UNSAFE_PASS_USER_SET); loginSuccessful = serializer.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL); sessionExpired = serializer.deserialize(Settings.IMP.MAIN.STRINGS.MOD_SESSION_EXPIRED); if (Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_TITLE.isEmpty() && Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_SUBTITLE.isEmpty()) { diff --git a/src/main/java/net/elytrium/limboauth/model/UnsafePlayer.java b/src/main/java/net/elytrium/limboauth/model/UnsafePlayer.java new file mode 100644 index 00000000..0ced362b --- /dev/null +++ b/src/main/java/net/elytrium/limboauth/model/UnsafePlayer.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 - 2024 Elytrium + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +package net.elytrium.limboauth.model; + +public class UnsafePlayer { + + private final String username; + private final String password; + + public UnsafePlayer(String username, String password) { + this.username = username; + this.password = password; + } + + public String getUsername() { + return this.username; + } + + public String getPassword() { + return this.password; + } + +} diff --git a/src/main/resources/unsafe_pass_user_set.txt b/src/main/resources/unsafe_pass_user_set.txt new file mode 100644 index 00000000..0e8e9bd2 --- /dev/null +++ b/src/main/resources/unsafe_pass_user_set.txt @@ -0,0 +1,3 @@ +testUsername:TestPassword! +testUsername2:TestPassword1233 +testUsername3:TestPassword123 \ No newline at end of file