From 1a1391a51916b111ceafe47bfbb144cf63cdc7ac Mon Sep 17 00:00:00 2001 From: "FivePB (Xer)" Date: Sun, 19 Jun 2022 18:19:32 +0200 Subject: [PATCH] Tablist-changes for 1.19 (#761) This change helps ensure player signatures are propagated correctly. Signatures should never be removed, so to compensate for legacy plugins and for the proxy api function we have to enforce this. --- .../api/proxy/player/TabListEntry.java | 2 ++ .../connection/client/ConnectedPlayer.java | 4 +-- .../proxy/crypto/IdentifiedKeyImpl.java | 17 ++++++++++ .../proxy/tablist/VelocityTabList.java | 32 ++++++++++++++++++- .../proxy/tablist/VelocityTabListLegacy.java | 5 +-- 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java b/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java index c537d477d..6d3534f5a 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java @@ -158,6 +158,8 @@ public interface TabListEntry extends KeyIdentifiable { /** * Sets the {@link IdentifiedKey} of the {@link TabListEntry}. + *

This is only intended and only works for players currently not connected to this proxy.

+ *

For any player currently connected to this proxy this will be filled automatically.

* * @param playerKey key to set * @return {@code this}, for chaining diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index a369d79c9..33cae2f9a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -176,9 +176,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, this.onlineMode = onlineMode; if (connection.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { - this.tabList = new VelocityTabList(this); + this.tabList = new VelocityTabList(this, server); } else { - this.tabList = new VelocityTabListLegacy(this); + this.tabList = new VelocityTabListLegacy(this, server); } this.playerKey = playerKey; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/crypto/IdentifiedKeyImpl.java b/proxy/src/main/java/com/velocitypowered/proxy/crypto/IdentifiedKeyImpl.java index f9eb028cb..b22371024 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/crypto/IdentifiedKeyImpl.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/crypto/IdentifiedKeyImpl.java @@ -17,6 +17,7 @@ package com.velocitypowered.proxy.crypto; +import com.google.common.base.Objects; import com.velocitypowered.api.proxy.crypto.IdentifiedKey; import java.nio.charset.StandardCharsets; import java.security.PublicKey; @@ -95,4 +96,20 @@ public class IdentifiedKeyImpl implements IdentifiedKey { + ", isSignatureValid=" + isSignatureValid + '}'; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof IdentifiedKey)) { + return false; + } + IdentifiedKey that = (IdentifiedKey) o; // This cannot be simplified because checkstyle doesn't like it. + + return Objects.equal(this.getSignedPublicKey(), that.getSignedPublicKey()) + && Objects.equal(this.getExpiryTemporal(), that.getExpiryTemporal()) + && Arrays.equals(this.getSignature(), that.getSignature()) + && Objects.equal(this.getSigner(), that.getSigner()); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java index 0f1e6e04c..5dc9f3d57 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java @@ -18,6 +18,8 @@ package com.velocitypowered.proxy.tablist; import com.google.common.base.Preconditions; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.crypto.IdentifiedKey; import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabListEntry; @@ -31,6 +33,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -41,10 +44,15 @@ public class VelocityTabList implements TabList { protected final ConnectedPlayer player; protected final MinecraftConnection connection; + protected final ProxyServer proxyServer; protected final Map entries = new ConcurrentHashMap<>(); - public VelocityTabList(final ConnectedPlayer player) { + /** + * Creates a new VelocityTabList. + */ + public VelocityTabList(final ConnectedPlayer player, final ProxyServer proxyServer) { this.player = player; + this.proxyServer = proxyServer; this.connection = player.getConnection(); } @@ -156,11 +164,29 @@ public class VelocityTabList implements TabList { if (name == null || properties == null) { throw new IllegalStateException("Got null game profile for ADD_PLAYER"); } + // Verify key + IdentifiedKey providedKey = item.getPlayerKey(); + Optional connected = proxyServer.getPlayer(uuid); + if (connected.isPresent()) { + IdentifiedKey expectedKey = connected.get().getIdentifiedKey(); + if (providedKey != null) { + if (!Objects.equals(expectedKey, providedKey)) { + throw new IllegalStateException("Server provided incorrect player key in playerlist for " + + name + " UUID: " + uuid); + } + } else { + // Substitute the key + // It shouldn't be propagated to remove the signature. + providedKey = expectedKey; + } + } + entries.putIfAbsent(item.getUuid(), (VelocityTabListEntry) TabListEntry.builder() .tabList(this) .profile(new GameProfile(uuid, name, properties)) .displayName(item.getDisplayName()) .latency(item.getLatency()) + .playerKey(providedKey) .gameMode(item.getGameMode()) .build()); break; @@ -199,6 +225,10 @@ public class VelocityTabList implements TabList { void updateEntry(int action, TabListEntry entry) { if (entries.containsKey(entry.getProfile().getId())) { PlayerListItem.Item packetItem = PlayerListItem.Item.from(entry); + + Optional existing = proxyServer.getPlayer(entry.getProfile().getId()); + existing.ifPresent(value -> packetItem.setPlayerKey(value.getIdentifiedKey())); + connection.write(new PlayerListItem(action, Collections.singletonList(packetItem))); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java index 95bfe6df1..567ae6bb5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java @@ -18,6 +18,7 @@ package com.velocitypowered.proxy.tablist; import com.google.common.collect.ImmutableList; +import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; @@ -35,8 +36,8 @@ public class VelocityTabListLegacy extends VelocityTabList { private final Map nameMapping = new ConcurrentHashMap<>(); - public VelocityTabListLegacy(final ConnectedPlayer player) { - super(player); + public VelocityTabListLegacy(final ConnectedPlayer player, final ProxyServer proxyServer) { + super(player, proxyServer); } @Deprecated