From f2d653106331a5a8ed5d0f15d5e9c97fca463cff Mon Sep 17 00:00:00 2001 From: js6pak Date: Fri, 8 Jan 2021 18:42:41 +0100 Subject: [PATCH] Add CheckName/SetName/CheckColor/SetColor anticheat checks (#278) * Add CheckName/SetName/CheckColor/SetColor anticheat checks * Disable expected checks for the host --- .../Net/Inner/Objects/InnerPlayerControl.cs | 116 +++++++++++++++++- .../Net/Inner/Objects/InnerPlayerInfo.cs | 4 + 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs index c71fcf7..1fe1dc8 100644 --- a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs +++ b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Impostor.Api; using Impostor.Api.Events.Managers; using Impostor.Api.Innersloth; +using Impostor.Api.Innersloth.Customization; using Impostor.Api.Net; using Impostor.Api.Net.Messages; using Impostor.Server.Events.Player; @@ -158,12 +159,34 @@ namespace Impostor.Server.Net.Inner.Objects // Validates the player name at the host. case RpcCalls.CheckName: { + if (!sender.IsOwner(this)) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CheckName)} to an unowned {nameof(InnerPlayerControl)}"); + } + if (target == null || !target.IsHost) { throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CheckName)} to the wrong player"); } var name = reader.ReadString(); + + if (name.Length > 10) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CheckName)} with name exceeding 10 characters"); + } + + if (string.IsNullOrWhiteSpace(name) || !name.All(TextBox.IsCharAllowed)) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CheckName)} with name containing illegal characters"); + } + + if (sender.Client.Name != name) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetName)} with name not matching his name from handshake"); + } + + PlayerInfo.RequestedPlayerName = name; break; } @@ -180,19 +203,78 @@ namespace Impostor.Server.Net.Inner.Objects throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetName)} to a specific player instead of broadcast"); } - PlayerInfo.PlayerName = reader.ReadString(); + var name = reader.ReadString(); + + if (sender.IsOwner(this)) + { + if (_game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.PlayerName == name)) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetName)} with a name that is already used"); + } + + if (sender.Client.Name != name) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetName)} with name not matching his name from handshake"); + } + } + else + { + if (PlayerInfo.RequestedPlayerName == null) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetName)} for a player that didn't request it"); + } + + var expected = PlayerInfo.RequestedPlayerName!; + + if (_game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.PlayerName == expected)) + { + var i = 1; + while (true) + { + string text = expected + " " + i; + + if (_game.Players.All(x => x.Character == null || x.Character == this || x.Character.PlayerInfo.PlayerName != text)) + { + expected = text; + break; + } + + i++; + } + } + + if (name != expected) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetName)} with incorrect name"); + } + } + + PlayerInfo.PlayerName = name; + PlayerInfo.RequestedPlayerName = null; break; } // Validates the color at the host. case RpcCalls.CheckColor: { + if (!sender.IsOwner(this)) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CheckColor)} to an unowned {nameof(InnerPlayerControl)}"); + } + if (target == null || !target.IsHost) { throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CheckColor)} to the wrong player"); } var color = reader.ReadByte(); + + if (color > Enum.GetValues().Length) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CheckColor)} with invalid color"); + } + + PlayerInfo.RequestedColorId = color; break; } @@ -209,7 +291,37 @@ namespace Impostor.Server.Net.Inner.Objects throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetColor)} to a specific player instead of broadcast"); } - PlayerInfo.ColorId = reader.ReadByte(); + var color = reader.ReadByte(); + + if (sender.IsOwner(this)) + { + if (_game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.ColorId == color)) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetColor)} with a color that is already used"); + } + } + else + { + if (PlayerInfo.RequestedColorId == null) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetColor)} for a player that didn't request it"); + } + + var expected = PlayerInfo.RequestedColorId!.Value; + + while (_game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.ColorId == expected)) + { + expected = (byte)((expected + 1) % Enum.GetValues().Length); + } + + if (color != expected) + { + throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetColor)} with incorrect color"); + } + } + + PlayerInfo.ColorId = color; + PlayerInfo.RequestedColorId = null; break; } diff --git a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerInfo.cs b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerInfo.cs index f248994..f1409f9 100644 --- a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerInfo.cs +++ b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerInfo.cs @@ -19,8 +19,12 @@ namespace Impostor.Server.Net.Inner.Objects public string PlayerName { get; internal set; } + public string? RequestedPlayerName { get; internal set; } + public byte ColorId { get; internal set; } + public byte? RequestedColorId { get; internal set; } + public uint HatId { get; internal set; } public uint PetId { get; internal set; } -- 2.39.5