3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-17 05:20:14 +01:00

Reimplement packet length checks

Dieser Commit ist enthalten in:
Andrew Steinborn 2021-05-14 09:24:39 -04:00
Ursprung 14be98c88c
Commit 0af9d9d77b
13 geänderte Dateien mit 105 neuen und 67 gelöschten Zeilen

Datei anzeigen

@ -398,14 +398,15 @@ public enum ProtocolUtils {
* @param buf the buffer to read from * @param buf the buffer to read from
* @return the read byte array * @return the read byte array
*/ */
public static byte[] readByteArray17(ByteBuf buf) { public static byte[] readByteArray17(ByteBuf buf, int cap) {
// Read in a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for // Read in a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for
// Forge only) // Forge only)
// No vanilla packet should give a 3 byte packet // No vanilla packet should give a 3 byte packet
int len = readExtendedForgeShort(buf); int len = readExtendedForgeShort(buf);
checkArgument(len <= FORGE_MAX_ARRAY_LENGTH, int maximumCap = Math.min(FORGE_MAX_ARRAY_LENGTH, cap);
"Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len); checkArgument(len <= maximumCap,
"Cannot receive array longer than %s (got %s bytes)", maximumCap, len);
byte[] ret = new byte[len]; byte[] ret = new byte[len];
buf.readBytes(ret); buf.readBytes(ret);

Datei anzeigen

@ -18,13 +18,28 @@
package com.velocitypowered.proxy.network.packet; package com.velocitypowered.proxy.network.packet;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.velocitypowered.api.network.ProtocolVersion;
import io.netty.buffer.ByteBuf;
import java.util.function.LongFunction; import java.util.function.LongFunction;
public abstract class AbstractStatusPingPacket implements Packet { public abstract class AbstractStatusPingPacket implements Packet {
protected static <P extends AbstractStatusPingPacket> PacketReader<P> decoder(final LongFunction<P> factory) { protected static <P extends AbstractStatusPingPacket> PacketReader<P> decoder(final LongFunction<P> factory) {
return (buf, version) -> { return new PacketReader<P>() {
@Override
public P read(ByteBuf buf, ProtocolVersion version) {
final long randomId = buf.readLong(); final long randomId = buf.readLong();
return factory.apply(randomId); return factory.apply(randomId);
}
@Override
public int expectedMaxLength(ByteBuf buf, ProtocolVersion version) {
return 8;
}
@Override
public int expectedMinLength(ByteBuf buf, ProtocolVersion version) {
return 8;
}
}; };
} }

Datei anzeigen

@ -33,13 +33,4 @@ public interface Packet {
} }
boolean handle(PacketHandler handler); boolean handle(PacketHandler handler);
// TODO: Move this into decoder
default int expectedMinLength(ByteBuf buf, PacketDirection direction, ProtocolVersion version) {
return 0;
}
default int expectedMaxLength(ByteBuf buf, PacketDirection direction, ProtocolVersion version) {
return -1;
}
} }

Datei anzeigen

@ -24,11 +24,11 @@ import java.util.function.Supplier;
public interface PacketReader<P extends Packet> { public interface PacketReader<P extends Packet> {
P read(final ByteBuf buf, final ProtocolVersion version); P read(final ByteBuf buf, final ProtocolVersion version);
default int expectedMinLength(ByteBuf buf, PacketDirection direction, ProtocolVersion version) { default int expectedMinLength(ByteBuf buf, ProtocolVersion version) {
return 0; return 0;
} }
default int expectedMaxLength(ByteBuf buf, PacketDirection direction, ProtocolVersion version) { default int expectedMaxLength(ByteBuf buf, ProtocolVersion version) {
return -1; return -1;
} }

Datei anzeigen

@ -60,8 +60,8 @@ public class ClientboundEncryptionRequestPacket implements Packet {
publicKey = ProtocolUtils.readByteArray(buf, 256); publicKey = ProtocolUtils.readByteArray(buf, 256);
verifyToken = ProtocolUtils.readByteArray(buf, 16); verifyToken = ProtocolUtils.readByteArray(buf, 16);
} else { } else {
publicKey = ProtocolUtils.readByteArray17(buf); publicKey = ProtocolUtils.readByteArray17(buf, 256);
verifyToken = ProtocolUtils.readByteArray17(buf); verifyToken = ProtocolUtils.readByteArray17(buf, 16);
} }
} }

Datei anzeigen

@ -27,17 +27,34 @@ import com.velocitypowered.proxy.network.packet.PacketWriter;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class ServerboundEncryptionResponsePacket implements Packet { public class ServerboundEncryptionResponsePacket implements Packet {
public static final PacketReader<ServerboundEncryptionResponsePacket> DECODER = (buf, version) -> { public static final PacketReader<ServerboundEncryptionResponsePacket> DECODER = new PacketReader<>() {
@Override
public ServerboundEncryptionResponsePacket read(ByteBuf buf,
ProtocolVersion version) {
final byte[] sharedSecret; final byte[] sharedSecret;
final byte[] verifyToken; final byte[] verifyToken;
if (version.gte(ProtocolVersion.MINECRAFT_1_8)) { if (version.gte(ProtocolVersion.MINECRAFT_1_8)) {
sharedSecret = ProtocolUtils.readByteArray(buf, 256); sharedSecret = ProtocolUtils.readByteArray(buf, 128);
verifyToken = ProtocolUtils.readByteArray(buf, 128); verifyToken = ProtocolUtils.readByteArray(buf, 128);
} else { } else {
sharedSecret = ProtocolUtils.readByteArray17(buf); sharedSecret = ProtocolUtils.readByteArray17(buf, 128);
verifyToken = ProtocolUtils.readByteArray17(buf); verifyToken = ProtocolUtils.readByteArray17(buf, 128);
} }
return new ServerboundEncryptionResponsePacket(sharedSecret, verifyToken); return new ServerboundEncryptionResponsePacket(sharedSecret, verifyToken);
}
@Override
public int expectedMaxLength(ByteBuf buf, ProtocolVersion version) {
// Both arrays are always 128 bytes long (due to padding), and each array has 2 bytes of
// padding (which applies to 1.7 since the length is written as a short and applies to 1.8+
// as 128 encodes as a 2-byte VarInt).
return 260;
}
@Override
public int expectedMinLength(ByteBuf buf, ProtocolVersion version) {
return expectedMaxLength(buf, version);
}
}; };
public static final PacketWriter<ServerboundEncryptionResponsePacket> ENCODER = PacketWriter.deprecatedEncode(); public static final PacketWriter<ServerboundEncryptionResponsePacket> ENCODER = PacketWriter.deprecatedEncode();

Datei anzeigen

@ -18,23 +18,35 @@
package com.velocitypowered.proxy.network.packet.serverbound; package com.velocitypowered.proxy.network.packet.serverbound;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.ProtocolUtils; import com.velocitypowered.proxy.network.ProtocolUtils;
import com.velocitypowered.proxy.network.packet.Packet; import com.velocitypowered.proxy.network.packet.Packet;
import com.velocitypowered.proxy.network.packet.PacketHandler; import com.velocitypowered.proxy.network.packet.PacketHandler;
import com.velocitypowered.proxy.network.packet.PacketReader; import com.velocitypowered.proxy.network.packet.PacketReader;
import com.velocitypowered.proxy.network.packet.PacketWriter; import com.velocitypowered.proxy.network.packet.PacketWriter;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
import java.util.Objects; import java.util.Objects;
public class ServerboundServerLoginPacket implements Packet { public class ServerboundServerLoginPacket implements Packet {
private static final QuietDecoderException EMPTY_USERNAME = new QuietDecoderException("Empty username!"); private static final QuietDecoderException EMPTY_USERNAME = new QuietDecoderException("Empty username!");
public static final PacketReader<ServerboundServerLoginPacket> DECODER = (buf, version) -> { public static final PacketReader<ServerboundServerLoginPacket> DECODER = new PacketReader<>() {
@Override
public ServerboundServerLoginPacket read(ByteBuf buf, ProtocolVersion version) {
final String username = ProtocolUtils.readString(buf, 16); final String username = ProtocolUtils.readString(buf, 16);
if (username.isEmpty()) { if (username.isEmpty()) {
throw EMPTY_USERNAME; throw EMPTY_USERNAME;
} }
return new ServerboundServerLoginPacket(username); return new ServerboundServerLoginPacket(username);
}
@Override
public int expectedMaxLength(ByteBuf buf, ProtocolVersion version) {
// Accommodate the rare (but likely malicious) use of UTF-8 usernames, since it is technically
// legal on the protocol level.
return 1 + (16 * 4);
}
}; };
public static final PacketWriter<ServerboundServerLoginPacket> ENCODER = (buf, packet, version) -> public static final PacketWriter<ServerboundServerLoginPacket> ENCODER = (buf, packet, version) ->

Datei anzeigen

@ -22,6 +22,7 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.ProtocolUtils; import com.velocitypowered.proxy.network.ProtocolUtils;
import com.velocitypowered.proxy.network.packet.Packet; import com.velocitypowered.proxy.network.packet.Packet;
import com.velocitypowered.proxy.network.packet.PacketDirection; import com.velocitypowered.proxy.network.packet.PacketDirection;
import com.velocitypowered.proxy.network.packet.PacketReader;
import com.velocitypowered.proxy.network.registry.packet.PacketRegistryMap; import com.velocitypowered.proxy.network.registry.packet.PacketRegistryMap;
import com.velocitypowered.proxy.network.registry.protocol.ProtocolRegistry; import com.velocitypowered.proxy.network.registry.protocol.ProtocolRegistry;
import com.velocitypowered.proxy.network.registry.state.ProtocolStates; import com.velocitypowered.proxy.network.registry.state.ProtocolStates;
@ -75,17 +76,18 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
int packetId = ProtocolUtils.readVarInt(buf); int packetId = ProtocolUtils.readVarInt(buf);
Packet packet; Packet packet;
try { try {
packet = this.registry.readPacket(packetId, buf, this.version); packet = this.readPacket(packetId, buf);
} catch (Exception e) { } catch (Exception e) {
throw handleDecodeFailure(e, packetId); throw handleDecodeFailure(e, packetId);
} }
if (packet == null) { if (packet == null) {
buf.readerIndex(originalReaderIndex); buf.readerIndex(originalReaderIndex);
ctx.fireChannelRead(buf); ctx.fireChannelRead(buf);
} else { } else {
try { try {
if (buf.isReadable()) { if (buf.isReadable()) {
throw handleOverflow(packetId, buf.readerIndex(), buf.writerIndex()); throw handleOverflow(buf.readerIndex(), buf.writerIndex());
} }
ctx.fireChannelRead(packet); ctx.fireChannelRead(packet);
} finally { } finally {
@ -94,33 +96,37 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
} }
} }
// TODO: Reimplement this private Packet readPacket(int packetId, ByteBuf buf) throws Exception {
private void doLengthSanityChecks(ByteBuf buf, int packetId, Packet packet) throws Exception { PacketReader<? extends Packet> reader = this.registry.lookupReader(packetId, this.version);
int expectedMinLen = packet.expectedMinLength(buf, direction, version); if (reader == null) {
int expectedMaxLen = packet.expectedMaxLength(buf, direction, version); return null;
if (expectedMaxLen != -1 && buf.readableBytes() > expectedMaxLen) {
throw handleOverflow(packetId, expectedMaxLen, buf.readableBytes());
}
if (buf.readableBytes() < expectedMinLen) {
throw handleUnderflow(packetId, expectedMaxLen, buf.readableBytes());
}
} }
private Exception handleOverflow(int packetId, int expected, int actual) { int expectedMinLen = reader.expectedMinLength(buf, version);
int expectedMaxLen = reader.expectedMaxLength(buf, version);
if (expectedMaxLen != -1 && buf.readableBytes() > expectedMaxLen) {
throw handleOverflow(expectedMaxLen, buf.readableBytes());
}
if (buf.readableBytes() < expectedMinLen) {
throw handleUnderflow(expectedMaxLen, buf.readableBytes());
}
return reader.read(buf, version);
}
private Exception handleOverflow(int expected, int actual) {
if (DEBUG) { if (DEBUG) {
Class<? extends Packet> packetClass = this.registry.lookupPacket(packetId); return new CorruptedFrameException("Packet sent was too big (expected "
return new CorruptedFrameException("Packet sent for " + packetClass + " was too " + expected + " bytes, got " + actual + " bytes)");
+ "big (expected " + expected + " bytes, got " + actual + " bytes)");
} else { } else {
return DECODE_FAILED; return DECODE_FAILED;
} }
} }
private Exception handleUnderflow(int packetId, int expected, int actual) { private Exception handleUnderflow(int expected, int actual) {
if (DEBUG) { if (DEBUG) {
Class<? extends Packet> packetClass = this.registry.lookupPacket(packetId); return new CorruptedFrameException("Packet was too small (expected " + expected
return new CorruptedFrameException("Packet sent for " + packetClass + " was too " + " bytes, got " + actual + " bytes)");
+ "small (expected " + expected + " bytes, got " + actual + " bytes)");
} else { } else {
return DECODE_FAILED; return DECODE_FAILED;
} }

Datei anzeigen

@ -97,12 +97,13 @@ public class DensePacketRegistryMap implements PacketRegistryMap {
} }
@Override @Override
public @Nullable Packet readPacket(int id, ByteBuf buf, ProtocolVersion version) { public @Nullable PacketReader<? extends Packet> lookupReader(final int id,
ProtocolVersion version) {
if (id < 0 || id >= this.readersById.length) { if (id < 0 || id >= this.readersById.length) {
return null; return null;
} }
return this.readersById[id].read(buf, version); return this.readersById[id];
} }
@Override @Override

Datei anzeigen

@ -19,6 +19,7 @@ package com.velocitypowered.proxy.network.registry.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.Packet; import com.velocitypowered.proxy.network.packet.Packet;
import com.velocitypowered.proxy.network.packet.PacketReader;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -31,7 +32,8 @@ public class EmptyPacketRegistryMap implements PacketRegistryMap {
} }
@Override @Override
public @Nullable Packet readPacket(int id, ByteBuf buf, ProtocolVersion version) { public @Nullable PacketReader<? extends Packet> lookupReader(final int id,
ProtocolVersion version) {
return null; return null;
} }

Datei anzeigen

@ -19,11 +19,12 @@ package com.velocitypowered.proxy.network.registry.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.Packet; import com.velocitypowered.proxy.network.packet.Packet;
import com.velocitypowered.proxy.network.packet.PacketReader;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public interface PacketRegistryMap { public interface PacketRegistryMap {
@Nullable Packet readPacket(final int id, ByteBuf buf, ProtocolVersion version); @Nullable PacketReader<? extends Packet> lookupReader(final int id, ProtocolVersion version);
<P extends Packet> void writePacket(P packet, ByteBuf buf, ProtocolVersion version); <P extends Packet> void writePacket(P packet, ByteBuf buf, ProtocolVersion version);

Datei anzeigen

@ -58,12 +58,8 @@ public class RegularPacketRegistryMap implements PacketRegistryMap {
} }
@Override @Override
public @Nullable Packet readPacket(int id, ByteBuf buf, ProtocolVersion version) { public @Nullable PacketReader<?> lookupReader(int id, ProtocolVersion version) {
PacketReader<?> reader = this.readersById.get(id); return this.readersById.get(id);
if (reader == null) {
return null;
}
return reader.read(buf, version);
} }
@Override @Override

Datei anzeigen

@ -432,16 +432,12 @@ class PlayPacketRegistry implements ProtocolRegistry {
* Attempts to create a packet from the specified {@code id}. * Attempts to create a packet from the specified {@code id}.
* *
* @param id the packet ID * @param id the packet ID
* @param buf the bytebuf
* @return the packet instance, or {@code null} if the ID is not registered * @return the packet instance, or {@code null} if the ID is not registered
*/ */
@Override @Override
public @Nullable Packet readPacket(final int id, ByteBuf buf, ProtocolVersion version) { public @Nullable PacketReader<? extends Packet> lookupReader(final int id,
final PacketReader<? extends Packet> decoder = this.packetIdToReader.get(id); ProtocolVersion version) {
if (decoder == null) { return this.packetIdToReader.get(id);
return null;
}
return decoder.read(buf, version);
} }
/** /**