From: Mike Stepanek (mstepane) Date: Fri, 29 Jan 2021 16:27:13 +0000 (+0000) Subject: Merge pull request #2721 in SNORT/snort3 from ~KATHARVE/snort3:h2i_stream_limit to... X-Git-Tag: 3.1.2.0~55 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=95fa48ea8695c282567880de1e05c0f4c5c6bee6;p=thirdparty%2Fsnort3.git Merge pull request #2721 in SNORT/snort3 from ~KATHARVE/snort3:h2i_stream_limit to master Squashed commit of the following: commit 8dc19216a06d0e2b18fc4f02aabc4b2955e2e65e Author: Katura Harvey Date: Fri Jan 22 14:46:34 2021 -0500 http2_inspect: limit number of concurrent streams --- diff --git a/src/service_inspectors/http2_inspect/http2_data_frame.cc b/src/service_inspectors/http2_inspect/http2_data_frame.cc index f0b8c487a..0fbf844d8 100644 --- a/src/service_inspectors/http2_inspect/http2_data_frame.cc +++ b/src/service_inspectors/http2_inspect/http2_data_frame.cc @@ -85,7 +85,11 @@ void Http2DataFrame::update_stream_state() } } if (stream->is_end_stream_on_data_flush(source_id)) + { + if (data_length > 0) + session_data->concurrent_files -= 1; stream->set_state(source_id, STREAM_COMPLETE); + } break; case STREAM_BODY: if (stream->is_end_stream_on_data_flush(source_id)) diff --git a/src/service_inspectors/http2_inspect/http2_enum.h b/src/service_inspectors/http2_inspect/http2_enum.h index 64c5b8632..d2c57bb64 100644 --- a/src/service_inspectors/http2_inspect/http2_enum.h +++ b/src/service_inspectors/http2_inspect/http2_enum.h @@ -28,6 +28,7 @@ static const int MAX_OCTETS = 63780; static const int DATA_SECTION_SIZE = 16384; static const int FRAME_HEADER_LENGTH = 9; static const uint32_t NO_STREAM_ID = 0xFFFFFFFF; +static const uint32_t CONCURRENT_STREAMS_LIMIT = 100; static const uint32_t HTTP2_GID = 121; @@ -48,8 +49,8 @@ enum HTTP2_BUFFER { HTTP2_BUFFER_FRAME_HEADER = 1, HTTP2_BUFFER_FRAME_DATA, // Peg counts // This enum must remain synchronized with Http2Module::peg_names[] in http2_tables.cc enum PEG_COUNT { PEG_FLOW = 0, PEG_CONCURRENT_SESSIONS, PEG_MAX_CONCURRENT_SESSIONS, - PEG_MAX_TABLE_ENTRIES, PEG_MAX_CONCURRENT_FILES, PEG_TOTAL_BYTES, - PEG_COUNT__MAX }; + PEG_MAX_TABLE_ENTRIES, PEG_MAX_CONCURRENT_FILES, PEG_TOTAL_BYTES, PEG_MAX_CONCURRENT_STREAMS, + PEG_FLOWS_OVER_STREAM_LIMIT, PEG_COUNT__MAX }; enum EventSid { @@ -80,6 +81,7 @@ enum EventSid EVENT_INVALID_PUSH_FRAME = 24, EVENT_BAD_PUSH_SEQUENCE = 25, EVENT_BAD_SETTINGS_VALUE = 26, + EVENT_TOO_MANY_STREAMS = 27, EVENT__MAX_VALUE }; @@ -129,6 +131,7 @@ enum Infraction INF_TRUNCATED_HEADER_LINE = 39, INF_REQUEST_WITHOUT_METHOD = 40, INF_CONNECT_WITHOUT_AUTHORITY = 41, + INF_TOO_MANY_STREAMS = 42, INF__MAX_VALUE }; diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.h b/src/service_inspectors/http2_inspect/http2_flow_data.h index a7e9c7baa..49e63da53 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.h +++ b/src/service_inspectors/http2_inspect/http2_flow_data.h @@ -161,6 +161,7 @@ protected: Http2HpackDecoder hpack_decoder[2]; std::list streams; uint32_t concurrent_files = 0; + uint32_t concurrent_streams = 0; // Internal to scan() bool preface[2] = { true, false }; diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.cc b/src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.cc index 5ce237eb4..f4a3a4778 100644 --- a/src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.cc +++ b/src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.cc @@ -33,6 +33,7 @@ #include "http2_enum.h" #include "http2_flow_data.h" #include "http2_hpack.h" +#include "http2_module.h" #include "http2_request_line.h" #include "http2_start_line.h" #include "http2_status_line.h" @@ -42,7 +43,6 @@ using namespace snort; using namespace HttpCommon; using namespace Http2Enums; - Http2HeadersFrameWithStartline::~Http2HeadersFrameWithStartline() { delete start_line_generator; @@ -54,6 +54,31 @@ bool Http2HeadersFrameWithStartline::process_start_line(HttpFlowData*& http_flow if (session_data->abort_flow[source_id]) return false; + if (!stream->get_hi_flow_data()) + { + if (session_data->concurrent_streams < CONCURRENT_STREAMS_LIMIT) + { + session_data->concurrent_streams += 1; + if (session_data->concurrent_streams > + Http2Module::get_peg_counts(PEG_MAX_CONCURRENT_STREAMS)) + + { + Http2Module::increment_peg_counts(PEG_MAX_CONCURRENT_STREAMS); + } + } + else + { + *session_data->infractions[source_id] += INF_TOO_MANY_STREAMS; + session_data->events[source_id]->create_event(EVENT_TOO_MANY_STREAMS); + Http2Module::increment_peg_counts(PEG_FLOWS_OVER_STREAM_LIMIT); + session_data->abort_flow[SRC_CLIENT] = true; + session_data->abort_flow[SRC_SERVER] = true; + stream->set_state(SRC_CLIENT, STREAM_ERROR); + stream->set_state(SRC_SERVER, STREAM_ERROR); + return false; + } + } + // http_inspect scan() of start line { uint32_t flush_offset; diff --git a/src/service_inspectors/http2_inspect/http2_push_promise_frame.cc b/src/service_inspectors/http2_inspect/http2_push_promise_frame.cc index 81a6ce47c..90da5d742 100644 --- a/src/service_inspectors/http2_inspect/http2_push_promise_frame.cc +++ b/src/service_inspectors/http2_inspect/http2_push_promise_frame.cc @@ -135,7 +135,8 @@ void Http2PushPromiseFrame::update_stream_state() if (stream->get_state(SRC_CLIENT) == STREAM_EXPECT_HEADERS) stream->set_state(SRC_CLIENT, STREAM_COMPLETE); - assert(stream->get_state(SRC_SERVER) == STREAM_EXPECT_HEADERS); + assert(stream->get_state(SRC_SERVER) == STREAM_EXPECT_HEADERS or + stream->get_state(SRC_SERVER) == STREAM_ERROR); assert((stream->get_state(SRC_CLIENT) == STREAM_COMPLETE) or (stream->get_state(SRC_CLIENT) == STREAM_ERROR)); } diff --git a/src/service_inspectors/http2_inspect/http2_stream.cc b/src/service_inspectors/http2_inspect/http2_stream.cc index 0a1575fb2..a41c5c2d9 100644 --- a/src/service_inspectors/http2_inspect/http2_stream.cc +++ b/src/service_inspectors/http2_inspect/http2_stream.cc @@ -83,6 +83,9 @@ void Http2Stream::clear_frame() session_data->deallocate_hi_memory(hi_flow_data); delete hi_flow_data; hi_flow_data = nullptr; + + assert(session_data->concurrent_streams > 0); + session_data->concurrent_streams -= 1; } } diff --git a/src/service_inspectors/http2_inspect/http2_tables.cc b/src/service_inspectors/http2_inspect/http2_tables.cc index 5dfd7df6c..8f7186b0a 100644 --- a/src/service_inspectors/http2_inspect/http2_tables.cc +++ b/src/service_inspectors/http2_inspect/http2_tables.cc @@ -57,6 +57,7 @@ const RuleMap Http2Module::http2_events[] = { EVENT_INVALID_PUSH_FRAME, "invalid HTTP/2 push promise frame" }, { EVENT_BAD_PUSH_SEQUENCE, "HTTP/2 push promise frame sent at invalid time" }, { EVENT_BAD_SETTINGS_VALUE, "invalid parameter value sent in HTTP/2 settings frame" }, + { EVENT_TOO_MANY_STREAMS, "excessive concurrent HTTP/2 streams" }, { 0, nullptr } }; @@ -69,6 +70,9 @@ const PegInfo Http2Module::peg_names[PEG_COUNT__MAX+1] = { CountType::MAX, "max_concurrent_files", "maximum concurrent file transfers per HTTP/2 " "connection" }, { CountType::SUM, "total_bytes", "total HTTP/2 data bytes inspected" }, + { CountType::MAX, "max_concurrent_streams", "maximum concurrent streams per HTTP/2 " + "connection" }, + { CountType::SUM, "flows_over_stream_limit", "HTTP/2 flows exceeding 100 concurrent streams" }, { CountType::END, nullptr, nullptr } };