From 54ff60cd8cbfc93d21622c611d32f4efdae0e155 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Thu, 1 Mar 2018 09:47:29 +0100 Subject: [PATCH 1/2] Limit JSON-RPC message size --- lib/base/netstring.cpp | 10 +++++++++- lib/base/netstring.hpp | 3 ++- lib/remote/jsonrpc.cpp | 4 ++-- lib/remote/jsonrpc.hpp | 2 +- lib/remote/jsonrpcconnection.cpp | 7 ++++++- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/base/netstring.cpp b/lib/base/netstring.cpp index 0bd110bac..e970f3b61 100644 --- a/lib/base/netstring.cpp +++ b/lib/base/netstring.cpp @@ -32,7 +32,8 @@ using namespace icinga; * @exception invalid_argument The input stream is invalid. * @see https://github.com/PeterScott/netstring-c/blob/master/netstring.c */ -StreamReadStatus NetString::ReadStringFromStream(const Stream::Ptr& stream, String *str, StreamReadContext& context, bool may_wait) +StreamReadStatus NetString::ReadStringFromStream(const Stream::Ptr& stream, String *str, StreamReadContext& context, + bool may_wait, ssize_t maxMessageLength) { if (context.Eof) return StatusEof; @@ -84,6 +85,13 @@ StreamReadStatus NetString::ReadStringFromStream(const Stream::Ptr& stream, Stri /* read the whole message */ size_t data_length = len + 1; + if (maxMessageLength >= 0 && data_length > maxMessageLength) { + std::stringstream errorMessage; + errorMessage << "Max data length exceeded: " << (maxMessageLength / 1024 / 1024) << " MB"; + + BOOST_THROW_EXCEPTION(std::invalid_argument(errorMessage.str())); + } + char *data = context.Buffer + header_length + 1; if (context.Size < header_length + 1 + data_length) { diff --git a/lib/base/netstring.hpp b/lib/base/netstring.hpp index 4ba02ccd3..1daaa979a 100644 --- a/lib/base/netstring.hpp +++ b/lib/base/netstring.hpp @@ -38,7 +38,8 @@ class String; class NetString { public: - static StreamReadStatus ReadStringFromStream(const Stream::Ptr& stream, String *message, StreamReadContext& context, bool may_wait = false); + static StreamReadStatus ReadStringFromStream(const Stream::Ptr& stream, String *message, StreamReadContext& context, + bool may_wait = false, ssize_t maxMessageLength = -1); static size_t WriteStringToStream(const Stream::Ptr& stream, const String& message); static void WriteStringToStream(std::ostream& stream, const String& message); diff --git a/lib/remote/jsonrpc.cpp b/lib/remote/jsonrpc.cpp index 94bd9cb5b..3dbd13aa3 100644 --- a/lib/remote/jsonrpc.cpp +++ b/lib/remote/jsonrpc.cpp @@ -72,10 +72,10 @@ size_t JsonRpc::SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& me return NetString::WriteStringToStream(stream, json); } -StreamReadStatus JsonRpc::ReadMessage(const Stream::Ptr& stream, String *message, StreamReadContext& src, bool may_wait) +StreamReadStatus JsonRpc::ReadMessage(const Stream::Ptr& stream, String *message, StreamReadContext& src, bool may_wait, size_t maxMessageLength) { String jsonString; - StreamReadStatus srs = NetString::ReadStringFromStream(stream, &jsonString, src, may_wait); + StreamReadStatus srs = NetString::ReadStringFromStream(stream, &jsonString, src, may_wait, maxMessageLength); if (srs != StatusNewItem) return srs; diff --git a/lib/remote/jsonrpc.hpp b/lib/remote/jsonrpc.hpp index 1d3232f95..3b0eaabf8 100644 --- a/lib/remote/jsonrpc.hpp +++ b/lib/remote/jsonrpc.hpp @@ -36,7 +36,7 @@ class JsonRpc { public: static size_t SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& message); - static StreamReadStatus ReadMessage(const Stream::Ptr& stream, String *message, StreamReadContext& src, bool may_wait = false); + static StreamReadStatus ReadMessage(const Stream::Ptr& stream, String *message, StreamReadContext& src, bool may_wait = false, size_t maxMessageLength = -1); static Dictionary::Ptr DecodeMessage(const String& message); private: diff --git a/lib/remote/jsonrpcconnection.cpp b/lib/remote/jsonrpcconnection.cpp index 978cd9658..bbb6fe180 100644 --- a/lib/remote/jsonrpcconnection.cpp +++ b/lib/remote/jsonrpcconnection.cpp @@ -250,9 +250,14 @@ void JsonRpcConnection::MessageHandler(const String& jsonString) bool JsonRpcConnection::ProcessMessage() { + ssize_t maxMessageLength = 64 * 1024; + + if (m_Endpoint) + maxMessageLength = -1; /* no limit */ + String message; - StreamReadStatus srs = JsonRpc::ReadMessage(m_Stream, &message, m_Context, false); + StreamReadStatus srs = JsonRpc::ReadMessage(m_Stream, &message, m_Context, false, maxMessageLength); if (srs != StatusNewItem) return false; From 1bdd14b42875b428d16686cb9b87f8ac0534cb2c Mon Sep 17 00:00:00 2001 From: Jean Flach Date: Mon, 5 Mar 2018 13:22:43 +0100 Subject: [PATCH 2/2] Limit anonymous connections to 25 --- lib/base/netstring.cpp | 2 +- lib/remote/apilistener.cpp | 14 +++++++++++--- lib/remote/apilistener.hpp | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/base/netstring.cpp b/lib/base/netstring.cpp index e970f3b61..7d11d8928 100644 --- a/lib/base/netstring.cpp +++ b/lib/base/netstring.cpp @@ -87,7 +87,7 @@ StreamReadStatus NetString::ReadStringFromStream(const Stream::Ptr& stream, Stri if (maxMessageLength >= 0 && data_length > maxMessageLength) { std::stringstream errorMessage; - errorMessage << "Max data length exceeded: " << (maxMessageLength / 1024 / 1024) << " MB"; + errorMessage << "Max data length exceeded: " << (maxMessageLength / 1024) << " KB"; BOOST_THROW_EXCEPTION(std::invalid_argument(errorMessage.str())); } diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index bc0df4449..c53cd103c 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -547,8 +547,12 @@ void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const Stri endpoint->AddClient(aclient); m_SyncQueue.Enqueue(std::bind(&ApiListener::SyncClient, this, aclient, endpoint, needSync)); - } else - AddAnonymousClient(aclient); + } else { + if (!AddAnonymousClient(aclient)) { + Log(LogNotice, "ApiListener", "Ignoring anonymous JSON-RPC connection. Max connections exceeded."); + aclient->Disconnect(); + } + } } else { Log(LogNotice, "ApiListener", "New HTTP client"); @@ -1360,10 +1364,14 @@ double ApiListener::CalculateZoneLag(const Endpoint::Ptr& endpoint) return 0; } -void ApiListener::AddAnonymousClient(const JsonRpcConnection::Ptr& aclient) +bool ApiListener::AddAnonymousClient(const JsonRpcConnection::Ptr& aclient) { boost::mutex::scoped_lock lock(m_AnonymousClientsLock); + if (m_AnonymousClients.size() > 25) + return false; + m_AnonymousClients.insert(aclient); + return true; } void ApiListener::RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient) diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index eb195f5b5..b3894992a 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -79,7 +79,7 @@ public: static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); std::pair GetStatus(); - void AddAnonymousClient(const JsonRpcConnection::Ptr& aclient); + bool AddAnonymousClient(const JsonRpcConnection::Ptr& aclient); void RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient); std::set GetAnonymousClients() const;