diff --git a/src/main/java/net/minecraftforge/fml/client/ClientHooks.java b/src/main/java/net/minecraftforge/fml/client/ClientHooks.java index ea796b0d3..0558145c1 100644 --- a/src/main/java/net/minecraftforge/fml/client/ClientHooks.java +++ b/src/main/java/net/minecraftforge/fml/client/ClientHooks.java @@ -128,6 +128,11 @@ public class ClientHooks if (fmlver > FMLNetworkConstants.FMLNETVERSION) { extraReason = "fml.menu.multiplayer.clientoutdated"; } + + if (!packet.getForgeData().isPatchAdvertised()) { + extraReason = "fml.menu.multiplayer.serverunpatched"; + } + target.forgeData = new ExtendedServerListData("FML", extraServerMods.isEmpty() && fmlNetMatches && channelsMatch && modsMatch, mods.size(), extraReason); } else { target.forgeData = new ExtendedServerListData("VANILLA", NetworkRegistry.canConnectToVanillaServer(),0, null); @@ -143,11 +148,19 @@ public class ClientHooks switch (target.forgeData.type) { case "FML": if (target.forgeData.isCompatible) { - idx = 0; - tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.compatible", target.forgeData.numberOfMods); + // HACK: Allow connections to unpatched servers, but show a warning + if (target.forgeData.extraReason != null && target.forgeData.extraReason.equals("fml.menu.multiplayer.serverunpatched")) { + idx = 96; + tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.incompatible.extra", ForgeI18n.parseMessage(target.forgeData.extraReason)); + } else { + idx = 0; + tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.compatible", target.forgeData.numberOfMods); + } } else { idx = 16; if(target.forgeData.extraReason != null) { + if (target.forgeData.extraReason.equals("fml.menu.multiplayer.serverunpatched")) + idx = 96; String extraReason = ForgeI18n.parseMessage(target.forgeData.extraReason); tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.incompatible.extra", extraReason); } else { diff --git a/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java b/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java index 897f5ed86..8c50bf4ea 100644 --- a/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java +++ b/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java @@ -34,7 +34,8 @@ public class FMLNetworkConstants { public static final String FMLNETMARKER = "FML"; public static final int FMLNETVERSION = 2; - public static final String NETVERSION = FMLNETMARKER + FMLNETVERSION; + // HACK: Unpatched servers won't see this leading 0 - but we will. + public static final String NETVERSION = FMLNETMARKER + "0" + FMLNETVERSION; public static final String NOVERSION = "NONE"; static final Marker NETWORK = MarkerManager.getMarker("FMLNETWORK"); diff --git a/src/main/java/net/minecraftforge/fml/network/FMLStatusPing.java b/src/main/java/net/minecraftforge/fml/network/FMLStatusPing.java index ad59bb4a9..f6e849b1f 100644 --- a/src/main/java/net/minecraftforge/fml/network/FMLStatusPing.java +++ b/src/main/java/net/minecraftforge/fml/network/FMLStatusPing.java @@ -67,6 +67,7 @@ public class FMLStatusPing { private transient Map> channels; private transient Map mods; private transient int fmlNetworkVer; + private transient boolean patched; public FMLStatusPing() { this.channels = NetworkRegistry.buildChannelVersionsForListPing(); this.mods = new HashMap<>(); @@ -74,12 +75,20 @@ public class FMLStatusPing { mods.put(modid, mc.getCustomExtension(ExtensionPoint.DISPLAYTEST). map(Pair::getLeft).map(Supplier::get).orElse(FMLNetworkConstants.IGNORESERVERONLY))); this.fmlNetworkVer = FMLNetworkConstants.FMLNETVERSION; + this.patched = true; } private FMLStatusPing(Map> deserialized, Map modMarkers, int fmlNetVer) { this.channels = ImmutableMap.copyOf(deserialized); this.mods = modMarkers; this.fmlNetworkVer = fmlNetVer; + this.patched = false; + } + private FMLStatusPing(Map> deserialized, Map modMarkers, int fmlNetVer, boolean p) { + this.channels = ImmutableMap.copyOf(deserialized); + this.mods = modMarkers; + this.fmlNetworkVer = fmlNetVer; + this.patched = p; } public static class Serializer { @@ -96,7 +105,8 @@ public class FMLStatusPing { collect(Collectors.toMap(jo -> JSONUtils.getString(jo, "modId"), jo->JSONUtils.getString(jo, "modmarker"))); final int remoteFMLVersion = JSONUtils.getInt(forgeData, "fmlNetworkVersion"); - return new FMLStatusPing(channels, mods, remoteFMLVersion); + + return new FMLStatusPing(channels, mods, remoteFMLVersion, JSONUtils.hasField(forgeData, "keithPatch")); } catch (JsonSyntaxException e) { LOGGER.debug(NETWORK, "Encountered an error parsing status ping data", e); return null; @@ -125,6 +135,8 @@ public class FMLStatusPing { }); obj.add("mods", modTestValues); obj.addProperty("fmlNetworkVersion", forgeData.fmlNetworkVer); + if (forgeData.patched) + obj.addProperty("keithPatch", true); return obj; } } @@ -141,4 +153,8 @@ public class FMLStatusPing { return fmlNetworkVer; } + public boolean isPatchAdvertised() { + return patched; + } + } diff --git a/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java b/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java index 8fe006c3d..683c776eb 100644 --- a/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java +++ b/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java @@ -155,6 +155,12 @@ public class ServerLifecycleHooks final ConnectionType connectionType = ConnectionType.forVersionFlag(packet.getFMLVersion()); final int versionNumber = connectionType.getFMLVersionNumber(packet.getFMLVersion()); + // HACK: Check for missing leading 0. If it's not there, we know the client isn't patched. + if (connectionType == ConnectionType.MODDED && packet.getFMLVersion().substring(FMLNetworkConstants.FMLNETMARKER.length()).charAt(0) != '0') { + rejectConnection(manager, connectionType, "Your client appears to be vulnerable to CVE-2021-44228, 45046, 45105, and/or 44832. To connect to this server, you must install ~keith's patched Forge version: https://bytes.keithhacks.cyou/keith/ForgePatch/releases/"); + return false; + } + if (connectionType == ConnectionType.MODDED && versionNumber != FMLNetworkConstants.FMLNETVERSION) { rejectConnection(manager, connectionType, "This modded server is not network compatible with your modded client. Please verify your Forge version closely matches the server. Got net version "+ versionNumber + " this server is net version "+FMLNetworkConstants.FMLNETVERSION); return false; diff --git a/src/main/resources/assets/forge/lang/en_us.json b/src/main/resources/assets/forge/lang/en_us.json index 0f72f24bd..ef4b620f7 100644 --- a/src/main/resources/assets/forge/lang/en_us.json +++ b/src/main/resources/assets/forge/lang/en_us.json @@ -34,6 +34,7 @@ "fml.menu.multiplayer.extraservermods":"Server has additional mods that may be needed on the client", "fml.menu.multiplayer.modsincompatible":"Server mod list is not compatible", "fml.menu.multiplayer.networkincompatible":"Server network message list is not compatible", + "fml.menu.multiplayer.serverunpatched": "Server is unpatched, or not advertising keith/ForgePatch", "fml.menu.loadingmods": "{0,choice,0#No mods|1#1 mod|1<{0} mods} loaded", "fml.menu.notification.title": "Startup Notification", "fml.menu.accessdenied.title": "Server Access Denied", diff --git a/src/main/resources/assets/forge/textures/gui/icons.png b/src/main/resources/assets/forge/textures/gui/icons.png index 846844a7b..4a7bac838 100644 Binary files a/src/main/resources/assets/forge/textures/gui/icons.png and b/src/main/resources/assets/forge/textures/gui/icons.png differ