Files
PlazmaBukkitMC/patches/server/0046-Hashed-rcon-password.patch
AlphaKR93 3f15d7a684 Updated Upstream (Paper, Pufferfish, Purpur)
Upstream has released updates that appear to apply and compile correctly.

[Purpur Changes]
PurpurMC/Purpur@e86a1b6: Updated Upstream (Paper)
PurpurMC/Purpur@962ee30: Updated Upstream (Paper)
PurpurMC/Purpur@74d1b4c: Updated Upstream (Paper)
PurpurMC/Purpur@e2e8c61: Updated Upstream (Paper)
PurpurMC/Purpur@7a01fd8: Updated Upstream (Paper)
PurpurMC/Purpur@34c18f0: Updated Upstream (Paper)
PurpurMC/Purpur@ca668ab: Updated Upstream (Paper)
PurpurMC/Purpur@200178d: Updated Upstream (Paper)
PurpurMC/Purpur@9968cbb: Updated Upstream (Paper)
PurpurMC/Purpur@db09358: Fix clamp-levels option not being true by default (#1609)
PurpurMC/Purpur@f289b6a: Updated Upstream (Paper)
PurpurMC/Purpur@959c29d: Fix Tridents giving errors without having an Elytra equipped (#1612)
PurpurMC/Purpur@68c1612: Fix villagers not spawning when the `follow-emerald-blocks` option is enabled (#1611)
PurpurMC/Purpur@5b75c68: fix `bypass-mob-griefing` not being the inverse of mobgriefing gamerule, closes #1603
PurpurMC/Purpur@55d4309: Updated Upstream (Paper)
PurpurMC/Purpur@0601f87: Updated Upstream (Paper)
PurpurMC/Purpur@06dde9d: Add Ridable and Attribute options for Creaking mob (#1613)
PurpurMC/Purpur@420a1ce: Set the bee's `takes-damage-from-water` option to true by default (#1614)
PurpurMC/Purpur@2b6f273: Updated Upstream (Paper)
PurpurMC/Purpur@504f311: Updated Upstream (Paper)
PurpurMC/Purpur@2b694c9: Updated Upstream (Paper)
PurpurMC/Purpur@96d7ef7: Updated Upstream (Paper)
PurpurMC/Purpur@e141f68: Updated Upstream (Paper)
PurpurMC/Purpur@7f6f667: Updated Upstream (Pufferfish)
PurpurMC/Purpur@de20ba9: ignore `minecart.max-speed` config value if using minecart experiment, closes #1618
PurpurMC/Purpur@03062a8: fix ridable mobs not being controllable, closes #1620
PurpurMC/Purpur@0493ac3: Updated Upstream (Paper)
PurpurMC/Purpur@16ce24a: fix(ridables/creaking): override tick method in look/move control
2024-12-14 01:59:42 +09:00

237 lines
12 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: AlphaKR93 <dev@alpha93.kr>
Date: Tue, 5 Nov 2024 18:31:09 +0900
Subject: [PATCH] Hashed rcon password
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
index 3dd5c7441bb300ca17d717af37edbefb89db4b2f..df367fbb8c582614aaf2087aa37d5e47247af810 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
@@ -73,7 +73,6 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
public final int queryPort;
public final boolean enableRcon;
public final int rconPort;
- public final String rconPassword;
public final boolean hardcore;
public final boolean allowNether;
public final boolean spawnMonsters;
@@ -113,6 +112,25 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
public final String rconIp; // Paper - Configurable rcon ip
+ // Plazma start - hashed rcon password
+ private final boolean rconHash;
+ private final int rconHashIterationCount;
+ private final byte[] rconSalt;
+ private final String rconPassword;
+
+ private static final @Nullable javax.crypto.SecretKeyFactory HASH_INSTANCE;
+ static {
+ @Nullable javax.crypto.SecretKeyFactory factory;
+ try {
+ factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512/256");
+ } catch (Exception e) {
+ LOGGER.error("Failed to initialize rcon hash instance", e);
+ factory = null;
+ }
+ HASH_INSTANCE = factory;
+ }
+ // Plazma end - hashed rcon password
+
// CraftBukkit start
public DedicatedServerProperties(Properties properties, OptionSet optionset) {
super(properties, optionset);
@@ -126,7 +144,7 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
this.queryPort = this.get("query.port", 25565);
this.enableRcon = this.get("enable-rcon", false);
this.rconPort = this.get("rcon.port", 25575);
- this.rconPassword = this.get("rcon.password", "");
+ // this.rconPassword = this.get("rcon.password", ""); // Plazma - hashed rcon password
this.hardcore = this.get("hardcore", false);
this.allowNether = this.get("allow-nether", true);
this.spawnMonsters = this.get("spawn-monsters", true);
@@ -179,7 +197,82 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
final String rconIp = this.getStringRaw("rcon.ip");
this.rconIp = rconIp == null ? this.serverIp : rconIp;
// Paper end - Configurable rcon ip
+
+ // Plazma start - hashed rcon password
+ this.rconHash = this.get("rcon.hash", false);
+ this.rconHashIterationCount = this.get("rcon.hash.iteration-count", Character.MAX_VALUE);
+ this.rconSalt = this.rconSalt();
+ this.rconPassword = this.rconPassword();
+ // Plazma end - hashed rcon password
+ }
+
+ // Plazma start - hashed rcon password
+ private byte[] rconSalt() {
+ if (!this.rconHash) return org.plazmamc.plazma.constants.Null.BYTE;
+
+ String salt = this.get("rcon.hash.salt", "");
+ if (!salt.isBlank()) {
+ try {
+ return java.util.HexFormat.of().parseHex(salt);
+ } catch (Exception e) {
+ LOGGER.error("Invalid RCON salt detected! RCON will be disabled");
+ return org.plazmamc.plazma.constants.Null.BYTE;
+ }
+ }
+
+ byte[] newSalt = new byte[16];
+ (new java.security.SecureRandom()).nextBytes(newSalt);
+ salt = java.util.HexFormat.of().formatHex(newSalt);
+ this.set("rcon.hash.salt", salt);
+ return newSalt;
+ }
+
+ private String rconPassword() {
+ String hashed = this.get("rcon.hash.password", "");
+ if (!hashed.isBlank()) {
+ try {
+ java.util.HexFormat.of().parseHex(hashed);
+ } catch (Exception e) {
+ LOGGER.error("Invalid hashed RCON password detected! RCON will be disabled");
+ return "";
+ }
+ return hashed;
+ }
+
+ String decoded = this.get("rcon.password", "");
+ if (decoded.isBlank() || HASH_INSTANCE == null || this.rconSalt.length == 0) return decoded;
+
+ try {
+ final java.security.spec.KeySpec spec = new javax.crypto.spec.PBEKeySpec(decoded.toCharArray(), this.rconSalt, this.rconHashIterationCount, 256);
+ byte[] hashedByte = HASH_INSTANCE.generateSecret(spec).getEncoded();
+ hashed = java.util.HexFormat.of().formatHex(hashedByte);
+ this.set("rcon.hash.password", hashed);
+ this.remove("rcon.password");
+ return hashed;
+ } catch (Exception e) {
+ LOGGER.error("Failed to encode rcon password", e);
+ return "";
+ }
+ }
+
+ public Boolean shouldStartRCON() {
+ return this.enableRcon && !this.rconPassword.isBlank();
+ }
+
+ public Boolean matchRconPassword(String value) {
+ if (this.rconSalt.length == 0 || HASH_INSTANCE == null) return this.rconPassword.equals(value);
+
+ try {
+ final java.security.spec.KeySpec spec = new javax.crypto.spec.PBEKeySpec(value.toCharArray(), this.rconSalt, this.rconHashIterationCount, 256);
+ byte[] hashedByte = HASH_INSTANCE.generateSecret(spec).getEncoded();
+ String hashed = java.util.HexFormat.of().formatHex(hashedByte);
+ return this.rconPassword.equals(hashed);
+ } catch (Exception e) {
+ LOGGER.error("Failed to validate rcon password", e);
+ return false;
+ }
}
+ // Plazma end - hashed rcon password
// CraftBukkit start
public static DedicatedServerProperties fromFile(Path path, OptionSet optionset) {
diff --git a/src/main/java/net/minecraft/server/dedicated/Settings.java b/src/main/java/net/minecraft/server/dedicated/Settings.java
index d7bd235ef2815890e038091dd625177049d253a5..afced52731868d5726b85a03011111adeffeb074 100644
--- a/src/main/java/net/minecraft/server/dedicated/Settings.java
+++ b/src/main/java/net/minecraft/server/dedicated/Settings.java
@@ -243,6 +243,11 @@ public abstract class Settings<T extends Settings<T>> {
return v1;
}
+ // Plazma start - Settings helper
+ protected <V> void set(String key, V value) { this.properties.put(key, value); }
+ protected void remove(String key) { this.properties.remove(key); }
+ // Plazma end - Settings helper
+
protected <V> Settings<T>.MutableValue<V> getMutable(String key, Function<String, V> parser, Function<V, String> stringifier, V fallback) {
String s1 = this.getStringRaw(key);
V v1 = MoreObjects.firstNonNull(s1 != null ? parser.apply(s1) : null, fallback);
diff --git a/src/main/java/net/minecraft/server/rcon/thread/RconClient.java b/src/main/java/net/minecraft/server/rcon/thread/RconClient.java
index 1c3775c6d29a85803c87948c64ec81cbeb87aef4..4f6f46398ac0814a95d8253cc5f575b8f09f7209 100644
--- a/src/main/java/net/minecraft/server/rcon/thread/RconClient.java
+++ b/src/main/java/net/minecraft/server/rcon/thread/RconClient.java
@@ -26,13 +26,12 @@ public class RconClient extends GenericThread {
private boolean authed;
private final Socket client;
private final byte[] buf = new byte[1460];
- private final String rconPassword;
// CraftBukkit start
private final DedicatedServer serverInterface;
private final RconConsoleSource rconConsoleSource;
// CraftBukkit end
- RconClient(ServerInterface server, String password, Socket socket) {
+ RconClient(ServerInterface server, Socket socket) {// Plazma - hashed rcon password
super("RCON Client " + String.valueOf(socket.getInetAddress()));
this.serverInterface = (DedicatedServer) server; // CraftBukkit
this.client = socket;
@@ -43,7 +42,6 @@ public class RconClient extends GenericThread {
this.running = false;
}
- this.rconPassword = password;
this.rconConsoleSource = new net.minecraft.server.rcon.RconConsoleSource(this.serverInterface, socket.getRemoteSocketAddress()); // CraftBukkit
}
@@ -93,7 +91,7 @@ public class RconClient extends GenericThread {
String s1 = PktUtils.stringFromByteArray(this.buf, j, i);
int j1 = j + s1.length();
- if (!s1.isEmpty() && s1.equals(this.rconPassword)) {
+ if (!s1.isEmpty() && serverInterface.getProperties().matchRconPassword(s1)) {
this.authed = true;
this.send(l, 2, "");
continue;
diff --git a/src/main/java/net/minecraft/server/rcon/thread/RconThread.java b/src/main/java/net/minecraft/server/rcon/thread/RconThread.java
index c12d7db2b048a327c0e8f398848cd3a9bce0ebce..150cecc44a7f49740fa400e02de09671ff8b392a 100644
--- a/src/main/java/net/minecraft/server/rcon/thread/RconThread.java
+++ b/src/main/java/net/minecraft/server/rcon/thread/RconThread.java
@@ -16,15 +16,13 @@ import org.slf4j.Logger;
public class RconThread extends GenericThread {
private static final Logger LOGGER = LogUtils.getLogger();
private final ServerSocket socket;
- private final String rconPassword;
private final List<RconClient> clients = Lists.newArrayList();
private final ServerInterface serverInterface;
- private RconThread(ServerInterface server, ServerSocket listener, String password) {
+ private RconThread(ServerInterface server, ServerSocket listener) {// Plazma - hashed rcon password
super("RCON Listener");
this.serverInterface = server;
this.socket = listener;
- this.rconPassword = password;
}
private void clearClients() {
@@ -37,7 +35,7 @@ public class RconThread extends GenericThread {
while (this.running) {
try {
Socket socket = this.socket.accept();
- RconClient rconClient = new RconClient(this.serverInterface, this.rconPassword, socket);
+ RconClient rconClient = new RconClient(this.serverInterface, socket); // Plazma - hashed rcon password
rconClient.start();
this.clients.add(rconClient);
this.clearClients();
@@ -64,15 +62,14 @@ public class RconThread extends GenericThread {
int i = dedicatedServerProperties.rconPort;
if (0 < i && 65535 >= i) {
- String string2 = dedicatedServerProperties.rconPassword;
- if (string2.isEmpty()) {
+ if (!dedicatedServerProperties.shouldStartRCON()) { // Plazma - hashed rcon password
LOGGER.warn("No rcon password set in server.properties, rcon disabled!");
return null;
} else {
try {
ServerSocket serverSocket = new ServerSocket(i, 0, InetAddress.getByName(string));
serverSocket.setSoTimeout(500);
- RconThread rconThread = new RconThread(server, serverSocket, string2);
+ RconThread rconThread = new RconThread(server, serverSocket); // Plazma - hashed rcon password
if (!rconThread.start()) {
return null;
} else {