diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch index 2c71b2620a..81e353c18e 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch @@ -56,7 +56,7 @@ private static final int MAX_TICKS_BEFORE_LOGIN = 600; private final byte[] challenge; final MinecraftServer server; -@@ -57,9 +86,10 @@ +@@ -57,9 +86,11 @@ @Nullable String requestedUsername; @Nullable @@ -65,10 +65,11 @@ private final String serverId; private final boolean transferred; + private ServerPlayer player; // CraftBukkit ++ public boolean iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation = false; // Paper - username validation overriding public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) { this.state = ServerLoginPacketListenerImpl.State.HELLO; -@@ -72,10 +102,24 @@ +@@ -72,10 +103,24 @@ @Override public void tick() { @@ -93,7 +94,7 @@ if (this.state == ServerLoginPacketListenerImpl.State.WAITING_FOR_DUPE_DISCONNECT && !this.isPlayerAlreadyInWorld((GameProfile) Objects.requireNonNull(this.authenticatedProfile))) { this.finishLoginAndWaitForClient(this.authenticatedProfile); } -@@ -86,6 +130,13 @@ +@@ -86,6 +131,13 @@ } @@ -107,7 +108,22 @@ @Override public boolean isAcceptingMessages() { return this.connection.isConnected(); -@@ -131,7 +182,26 @@ +@@ -120,7 +172,13 @@ + @Override + public void handleHello(ServerboundHelloPacket packet) { + Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet", new Object[0]); +- Validate.validState(StringUtil.isValidPlayerName(packet.name()), "Invalid characters in username", new Object[0]); ++ // Paper start - Validate usernames ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ++ && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation ++ && !this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation) { ++ Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username", new Object[0]); ++ } ++ // Paper end - Validate usernames + this.requestedUsername = packet.name(); + GameProfile gameprofile = this.server.getSingleplayerProfile(); + +@@ -131,7 +189,26 @@ this.state = ServerLoginPacketListenerImpl.State.KEY; this.connection.send(new ClientboundHelloPacket("", this.server.getKeyPair().getPublic().getEncoded(), this.challenge, true)); } else { @@ -135,7 +151,7 @@ } } -@@ -144,10 +214,24 @@ +@@ -144,10 +221,24 @@ private void verifyLoginAndFinishConnectionSetup(GameProfile profile) { PlayerList playerlist = this.server.getPlayerList(); @@ -163,7 +179,7 @@ } else { if (this.server.getCompressionThreshold() >= 0 && !this.connection.isMemoryConnection()) { this.connection.send(new ClientboundLoginCompressionPacket(this.server.getCompressionThreshold()), PacketSendListener.thenRun(() -> { -@@ -155,12 +239,12 @@ +@@ -155,12 +246,12 @@ })); } @@ -178,7 +194,7 @@ } } -@@ -195,7 +279,8 @@ +@@ -195,7 +286,8 @@ throw new IllegalStateException("Protocol error", cryptographyexception); } @@ -188,7 +204,7 @@ public void run() { String s1 = (String) Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized"); -@@ -205,11 +290,17 @@ +@@ -205,11 +297,17 @@ if (profileresult != null) { GameProfile gameprofile = profileresult.profile(); @@ -207,7 +223,7 @@ } else { ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username")); ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", s1); -@@ -217,11 +308,16 @@ +@@ -217,11 +315,16 @@ } catch (AuthenticationUnavailableException authenticationunavailableexception) { if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) { ServerLoginPacketListenerImpl.LOGGER.warn("Authentication servers are down but will let them in anyway!"); @@ -226,7 +242,7 @@ } } -@@ -232,11 +328,54 @@ +@@ -232,11 +335,54 @@ return ServerLoginPacketListenerImpl.this.server.getPreventProxyConnections() && socketaddress instanceof InetSocketAddress ? ((InetSocketAddress) socketaddress).getAddress() : null; } @@ -284,7 +300,7 @@ @Override public void handleCustomQueryPacket(ServerboundCustomQueryAnswerPacket packet) { -@@ -245,10 +384,11 @@ +@@ -245,10 +391,11 @@ @Override public void handleLoginAcknowledgement(ServerboundLoginAcknowledgedPacket packet) { @@ -297,7 +313,7 @@ this.connection.setupInboundProtocol(ConfigurationProtocols.SERVERBOUND, serverconfigurationpacketlistenerimpl); serverconfigurationpacketlistenerimpl.startConfiguration(); -@@ -264,12 +404,44 @@ +@@ -264,12 +411,44 @@ @Override public void handleCookieResponse(ServerboundCookieResponsePacket packet) { diff --git a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch index 907a3b2c66..899986dd38 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch @@ -527,7 +527,7 @@ + + for (int i = 0; i < this.players.size(); ++i) { + entityplayer = (ServerPlayer) this.players.get(i); -+ if (entityplayer.getUUID().equals(uuid)) { ++ if (entityplayer.getUUID().equals(uuid) || (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() && entityplayer.getGameProfile().getName().equalsIgnoreCase(gameprofile.getName()))) { // Paper - validate usernames + list.add(entityplayer); + } + } diff --git a/paper-server/patches/sources/net/minecraft/util/StringUtil.java.patch b/paper-server/patches/sources/net/minecraft/util/StringUtil.java.patch new file mode 100644 index 0000000000..d0f9b93e86 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/util/StringUtil.java.patch @@ -0,0 +1,28 @@ +--- a/net/minecraft/util/StringUtil.java ++++ b/net/minecraft/util/StringUtil.java +@@ -67,6 +67,25 @@ + return name.length() <= 16 && name.chars().filter(c -> c <= 32 || c >= 127).findAny().isEmpty(); + } + ++ // Paper start - Username validation ++ public static boolean isReasonablePlayerName(final String name) { ++ if (name.isEmpty() || name.length() > 16) { ++ return false; ++ } ++ ++ for (int i = 0, len = name.length(); i < len; ++i) { ++ final char c = name.charAt(i); ++ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_' || c == '.')) { ++ continue; ++ } ++ ++ return false; ++ } ++ ++ return true; ++ } ++ // Paper end - Username validation ++ + public static String filterText(String string) { + return filterText(string, false); + }