Merge pull request #6135 from Icinga/fix/limit-api-package-size

Limit JSON RPC message size
This commit is contained in:
Gunnar Beutner 2018-03-05 13:24:09 +01:00 committed by GitHub
commit 9b72a6c2f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 32 additions and 10 deletions

View File

@ -32,7 +32,8 @@ using namespace icinga;
* @exception invalid_argument The input stream is invalid. * @exception invalid_argument The input stream is invalid.
* @see https://github.com/PeterScott/netstring-c/blob/master/netstring.c * @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) if (context.Eof)
return StatusEof; return StatusEof;
@ -84,6 +85,13 @@ StreamReadStatus NetString::ReadStringFromStream(const Stream::Ptr& stream, Stri
/* read the whole message */ /* read the whole message */
size_t data_length = len + 1; size_t data_length = len + 1;
if (maxMessageLength >= 0 && data_length > maxMessageLength) {
std::stringstream errorMessage;
errorMessage << "Max data length exceeded: " << (maxMessageLength / 1024) << " KB";
BOOST_THROW_EXCEPTION(std::invalid_argument(errorMessage.str()));
}
char *data = context.Buffer + header_length + 1; char *data = context.Buffer + header_length + 1;
if (context.Size < header_length + 1 + data_length) { if (context.Size < header_length + 1 + data_length) {

View File

@ -38,7 +38,8 @@ class String;
class NetString class NetString
{ {
public: 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 size_t WriteStringToStream(const Stream::Ptr& stream, const String& message);
static void WriteStringToStream(std::ostream& stream, const String& message); static void WriteStringToStream(std::ostream& stream, const String& message);

View File

@ -547,8 +547,12 @@ void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const Stri
endpoint->AddClient(aclient); endpoint->AddClient(aclient);
m_SyncQueue.Enqueue(std::bind(&ApiListener::SyncClient, this, aclient, endpoint, needSync)); m_SyncQueue.Enqueue(std::bind(&ApiListener::SyncClient, this, aclient, endpoint, needSync));
} else } else {
AddAnonymousClient(aclient); if (!AddAnonymousClient(aclient)) {
Log(LogNotice, "ApiListener", "Ignoring anonymous JSON-RPC connection. Max connections exceeded.");
aclient->Disconnect();
}
}
} else { } else {
Log(LogNotice, "ApiListener", "New HTTP client"); Log(LogNotice, "ApiListener", "New HTTP client");
@ -1360,10 +1364,14 @@ double ApiListener::CalculateZoneLag(const Endpoint::Ptr& endpoint)
return 0; return 0;
} }
void ApiListener::AddAnonymousClient(const JsonRpcConnection::Ptr& aclient) bool ApiListener::AddAnonymousClient(const JsonRpcConnection::Ptr& aclient)
{ {
boost::mutex::scoped_lock lock(m_AnonymousClientsLock); boost::mutex::scoped_lock lock(m_AnonymousClientsLock);
if (m_AnonymousClients.size() > 25)
return false;
m_AnonymousClients.insert(aclient); m_AnonymousClients.insert(aclient);
return true;
} }
void ApiListener::RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient) void ApiListener::RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient)

View File

@ -79,7 +79,7 @@ public:
static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata);
std::pair<Dictionary::Ptr, Dictionary::Ptr> GetStatus(); std::pair<Dictionary::Ptr, Dictionary::Ptr> GetStatus();
void AddAnonymousClient(const JsonRpcConnection::Ptr& aclient); bool AddAnonymousClient(const JsonRpcConnection::Ptr& aclient);
void RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient); void RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient);
std::set<JsonRpcConnection::Ptr> GetAnonymousClients() const; std::set<JsonRpcConnection::Ptr> GetAnonymousClients() const;

View File

@ -72,10 +72,10 @@ size_t JsonRpc::SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& me
return NetString::WriteStringToStream(stream, json); 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; String jsonString;
StreamReadStatus srs = NetString::ReadStringFromStream(stream, &jsonString, src, may_wait); StreamReadStatus srs = NetString::ReadStringFromStream(stream, &jsonString, src, may_wait, maxMessageLength);
if (srs != StatusNewItem) if (srs != StatusNewItem)
return srs; return srs;

View File

@ -36,7 +36,7 @@ class JsonRpc
{ {
public: public:
static size_t SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& message); 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); static Dictionary::Ptr DecodeMessage(const String& message);
private: private:

View File

@ -250,9 +250,14 @@ void JsonRpcConnection::MessageHandler(const String& jsonString)
bool JsonRpcConnection::ProcessMessage() bool JsonRpcConnection::ProcessMessage()
{ {
ssize_t maxMessageLength = 64 * 1024;
if (m_Endpoint)
maxMessageLength = -1; /* no limit */
String message; 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) if (srs != StatusNewItem)
return false; return false;