From 3ebfa8cc645e5b35c8e6985ece3ef090858a9cb2 Mon Sep 17 00:00:00 2001 From: Shauren Date: Wed, 18 Mar 2020 23:20:11 +0100 Subject: Core/PacketIO: Add a validating string helper class for use in packet classes (cherry picked from commit f2f47f774f3d9c5da1a38b5f20cbfe59c2ff66af) --- src/common/Utilities/Tuples.h | 34 ++++++++ src/server/game/Server/Packets/PacketUtilities.cpp | 39 +++++++++ src/server/game/Server/Packets/PacketUtilities.h | 96 ++++++++++++++++++++++ src/server/game/Server/WorldSession.cpp | 16 ++++ src/server/shared/Packets/ByteBuffer.cpp | 12 +-- src/server/shared/Packets/ByteBuffer.h | 2 +- 6 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 src/common/Utilities/Tuples.h (limited to 'src') diff --git a/src/common/Utilities/Tuples.h b/src/common/Utilities/Tuples.h new file mode 100644 index 00000000000..73337ebd9f5 --- /dev/null +++ b/src/common/Utilities/Tuples.h @@ -0,0 +1,34 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef Tuples_h__ +#define Tuples_h__ + +#include + +namespace Trinity +{ + template + struct has_type; + + template + struct has_type> : std::disjunction...> + { + }; +} + +#endif // Tuples_h__ diff --git a/src/server/game/Server/Packets/PacketUtilities.cpp b/src/server/game/Server/Packets/PacketUtilities.cpp index 99f3decd0e1..426b3be9b8f 100644 --- a/src/server/game/Server/Packets/PacketUtilities.cpp +++ b/src/server/game/Server/Packets/PacketUtilities.cpp @@ -16,9 +16,48 @@ */ #include "PacketUtilities.h" +#include "Hyperlinks.h" #include "Errors.h" +#include #include +WorldPackets::InvalidStringValueException::InvalidStringValueException(std::string const& value) : ByteBufferInvalidValueException("string", value.c_str()) +{ +} + +WorldPackets::InvalidUtf8ValueException::InvalidUtf8ValueException(std::string const& value) : InvalidStringValueException(value) +{ +} + +WorldPackets::InvalidHyperlinkException::InvalidHyperlinkException(std::string const& value) : InvalidStringValueException(value) +{ +} + +WorldPackets::IllegalHyperlinkException::IllegalHyperlinkException(std::string const& value) : InvalidStringValueException(value) +{ +} + +bool WorldPackets::Strings::Utf8::Validate(std::string const& value) +{ + if (!utf8::is_valid(value.begin(), value.end())) + throw InvalidUtf8ValueException(value); + return true; +} + +bool WorldPackets::Strings::Hyperlinks::Validate(std::string const& value) +{ + if (!Trinity::Hyperlinks::CheckAllLinks(value)) + throw InvalidHyperlinkException(value); + return true; +} + +bool WorldPackets::Strings::NoHyperlinks::Validate(std::string const& value) +{ + if (value.find('|') != std::string::npos) + throw IllegalHyperlinkException(value); + return true; +} + WorldPackets::PacketArrayMaxCapacityException::PacketArrayMaxCapacityException(std::size_t requestedSize, std::size_t sizeLimit) { std::ostringstream builder; diff --git a/src/server/game/Server/Packets/PacketUtilities.h b/src/server/game/Server/Packets/PacketUtilities.h index 8b119cf1f61..3bb3443ff06 100644 --- a/src/server/game/Server/Packets/PacketUtilities.h +++ b/src/server/game/Server/Packets/PacketUtilities.h @@ -20,11 +20,107 @@ #include "ByteBuffer.h" #include "Duration.h" +#include "Tuples.h" #include #include namespace WorldPackets { + class InvalidStringValueException : public ByteBufferInvalidValueException + { + public: + InvalidStringValueException(std::string const& value); + + std::string const& GetInvalidValue() const { return _value; } + + private: + std::string _value; + }; + + class InvalidUtf8ValueException : public InvalidStringValueException + { + public: + InvalidUtf8ValueException(std::string const& value); + }; + + class InvalidHyperlinkException : public InvalidStringValueException + { + public: + InvalidHyperlinkException(std::string const& value); + }; + + class IllegalHyperlinkException : public InvalidStringValueException + { + public: + IllegalHyperlinkException(std::string const& value); + }; + + namespace Strings + { + struct RawBytes { static bool Validate(std::string const& /*value*/) { return true; } }; + template + struct ByteSize { static bool Validate(std::string const& value) { return value.size() <= MaxBytesWithoutNullTerminator; } }; + struct Utf8 { static bool Validate(std::string const& value); }; + struct Hyperlinks { static bool Validate(std::string const& value); }; + struct NoHyperlinks { static bool Validate(std::string const& value); }; + } + + /** + * Utility class for automated prevention of invalid strings in client packets + */ + template + class String + { + using ValidatorList = std::conditional_t>::value, + std::tuple, Strings::Utf8, Validators...>, + std::tuple, Validators...>>; + + public: + bool empty() const { return _storage.empty(); } + char const* c_str() const { return _storage.c_str(); } + + operator std::string&() { return _storage; } + operator std::string const&() const { return _storage; } + + std::string&& Move() { return std::move(_storage); } + + friend ByteBuffer& operator>>(ByteBuffer& data, String& value) + { + std::string string = data.ReadCString(false); + Validate(string); + value._storage = std::move(string); + return data; + } + + String& operator=(std::string const& value) + { + Validate(value); + _storage = value; + return *this; + } + + String& operator=(std::string&& value) + { + Validate(value); + _storage = std::move(value); + return *this; + } + + private: + static bool Validate(std::string const& value) + { + return ValidateNth(value, std::make_index_sequence>{}); + } + + template + static bool ValidateNth(std::string const& value, std::index_sequence) + { + return (std::tuple_element_t::Validate(value) && ...); + } + + std::string _storage; + }; + class PacketArrayMaxCapacityException : public ByteBufferException { public: diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 3a14cf48785..942b29be1dc 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -425,6 +425,22 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) break; } } + catch (WorldPackets::InvalidHyperlinkException const& ihe) + { + TC_LOG_ERROR("network", "%s sent %s with an invalid link:\n%s", GetPlayerInfo().c_str(), + GetOpcodeNameForLogging(static_cast(packet->GetOpcode())).c_str(), ihe.GetInvalidValue().c_str()); + + if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK)) + KickPlayer(); + } + catch (WorldPackets::IllegalHyperlinkException const& ihe) + { + TC_LOG_ERROR("network", "%s sent %s which illegally contained a hyperlink:\n%s", GetPlayerInfo().c_str(), + GetOpcodeNameForLogging(static_cast(packet->GetOpcode())).c_str(), ihe.GetInvalidValue().c_str()); + + if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK)) + KickPlayer(); + } catch (WorldPackets::PacketArrayMaxCapacityException const& pamce) { TC_LOG_ERROR("network", "PacketArrayMaxCapacityException: %s while parsing %s from %s.", diff --git a/src/server/shared/Packets/ByteBuffer.cpp b/src/server/shared/Packets/ByteBuffer.cpp index 6c1ed3842cc..f8470332893 100644 --- a/src/server/shared/Packets/ByteBuffer.cpp +++ b/src/server/shared/Packets/ByteBuffer.cpp @@ -39,16 +39,16 @@ ByteBufferPositionException::ByteBufferPositionException(size_t pos, size_t size message().assign(ss.str()); } -ByteBufferInvalidValueException::ByteBufferInvalidValueException(char const* type, size_t pos) +ByteBufferInvalidValueException::ByteBufferInvalidValueException(char const* type, char const* value) { - message().assign(Trinity::StringFormat("Invalid %s value found in ByteBuffer at pos " SZFMTD, type, pos)); + message().assign(Trinity::StringFormat("Invalid %s value (%s) found in ByteBuffer", type, value)); } ByteBuffer& ByteBuffer::operator>>(float& value) { value = read(); if (!std::isfinite(value)) - throw ByteBufferInvalidValueException("float", _rpos - sizeof(float)); + throw ByteBufferInvalidValueException("float", "infinity"); return *this; } @@ -56,7 +56,7 @@ ByteBuffer& ByteBuffer::operator>>(double& value) { value = read(); if (!std::isfinite(value)) - throw ByteBufferInvalidValueException("double", _rpos - sizeof(double)); + throw ByteBufferInvalidValueException("double", "infinity"); return *this; } @@ -71,7 +71,7 @@ std::string ByteBuffer::ReadCString(bool requireValidUtf8 /*= true*/) value += c; } if (requireValidUtf8 && !utf8::is_valid(value.begin(), value.end())) - throw ByteBufferInvalidValueException("string", _rpos - value.length() - 1); + throw ByteBufferInvalidValueException("string", value.c_str()); return value; } @@ -87,7 +87,7 @@ std::string ByteBuffer::ReadString(uint32 length, bool requireValidUtf8 /*= true std::string value(reinterpret_cast(&_storage[_rpos]), length); _rpos += length; if (requireValidUtf8 && !utf8::is_valid(value.begin(), value.end())) - throw ByteBufferInvalidValueException("string", _rpos - value.length() - 1); + throw ByteBufferInvalidValueException("string", value.c_str()); return value; } diff --git a/src/server/shared/Packets/ByteBuffer.h b/src/server/shared/Packets/ByteBuffer.h index 553c1a4c4ff..32f6bcbaee9 100644 --- a/src/server/shared/Packets/ByteBuffer.h +++ b/src/server/shared/Packets/ByteBuffer.h @@ -53,7 +53,7 @@ public: class TC_SHARED_API ByteBufferInvalidValueException : public ByteBufferException { public: - ByteBufferInvalidValueException(char const* type, size_t pos); + ByteBufferInvalidValueException(char const* type, char const* value); ~ByteBufferInvalidValueException() noexcept = default; }; -- cgit v1.2.3