From: Mike Stepanek (mstepane) Date: Thu, 5 Dec 2019 21:16:40 +0000 (+0000) Subject: Merge pull request #1856 in SNORT/snort3 from ~DERAMADA/snort3:h2i_settings_frame... X-Git-Tag: 3.0.0-267~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8705ca1138bb3ab51550eddb1e0b040c52598580;p=thirdparty%2Fsnort3.git Merge pull request #1856 in SNORT/snort3 from ~DERAMADA/snort3:h2i_settings_frame to master Squashed commit of the following: commit eb9c908e03f795681f2d82e92eaee6d8d17d5759 Author: deramada Date: Thu Nov 21 14:50:57 2019 -0500 http2: parse settings frames --- diff --git a/src/service_inspectors/http2_inspect/CMakeLists.txt b/src/service_inspectors/http2_inspect/CMakeLists.txt index db5d9f060..a1ddd7c8f 100644 --- a/src/service_inspectors/http2_inspect/CMakeLists.txt +++ b/src/service_inspectors/http2_inspect/CMakeLists.txt @@ -28,6 +28,8 @@ set (FILE_LIST http2_module.h http2_request_line.cc http2_request_line.h + http2_settings_frame.cc + http2_settings_frame.h http2_start_line.cc http2_start_line.h http2_status_line.cc diff --git a/src/service_inspectors/http2_inspect/http2_enum.h b/src/service_inspectors/http2_inspect/http2_enum.h index f7e03ede1..c7e0166bd 100644 --- a/src/service_inspectors/http2_inspect/http2_enum.h +++ b/src/service_inspectors/http2_inspect/http2_enum.h @@ -55,9 +55,11 @@ enum EventSid EVENT_UNEXPECTED_CONTINUATION = 5, EVENT_MISFORMATTED_HTTP2 = 6, EVENT_PREFACE_MATCH_FAILURE = 7, - EVENT_REQUEST_WITHOUT_REQUIRED_FIELD = 9, - EVENT_RESPONSE_WITHOUT_STATUS = 10, - EVENT_INVALID_HEADER = 11, + EVENT_REQUEST_WITHOUT_REQUIRED_FIELD = 8, + EVENT_RESPONSE_WITHOUT_STATUS = 9, + EVENT_INVALID_HEADER = 10, + EVENT_SETTINGS_FRAME_ERROR = 11, + EVENT_SETTINGS_FRAME_UNKN_PARAM = 12, EVENT__MAX_VALUE }; @@ -83,6 +85,8 @@ enum Infraction INF_PSEUDO_HEADER_URI_FORM_MISMATCH = 15, INF_RESPONSE_WITHOUT_STATUS = 16, INF_HPACK_INDEX_OUT_OF_BOUNDS = 17, + INF_INVALID_SETTINGS_FRAME = 18, + INF_SETTINGS_FRAME_UNKN_PARAM = 19, INF__MAX_VALUE }; @@ -106,6 +110,16 @@ enum PseudoHeaders STATUS = 14, }; +enum SettingsFrameIds +{ + HEADER_TABLE_SIZE = 1, + ENABLE_PUSH, + MAX_CONCURRENT_STREAMS, + INITIAL_WINDOW_SIZE, + MAX_FRAME_SIZE, + MAX_HEADER_LIST_SIZE, +}; + } // end namespace Http2Enums #endif diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.h b/src/service_inspectors/http2_inspect/http2_flow_data.h index 8a0aa94ee..671956df2 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.h +++ b/src/service_inspectors/http2_inspect/http2_flow_data.h @@ -34,6 +34,7 @@ #include "http2_hpack.h" #include "http2_hpack_int_decode.h" #include "http2_hpack_string_decode.h" +#include "http2_settings_frame.h" using Http2Infractions = Infractions; @@ -53,6 +54,7 @@ public: friend class Http2Hpack; friend class Http2Inspect; friend class Http2RequestLine; + friend class Http2SettingsFrame; friend class Http2StartLine; friend class Http2StatusLine; friend class Http2StreamSplitter; @@ -79,6 +81,7 @@ protected: // Used in eval() bool frame_in_detection = false; class Http2Frame* current_frame[2] = { nullptr, nullptr }; + Http2ConnectionSettings connection_settings[2]; Http2HpackDecoder hpack_decoder[2]; // Internal to scan() diff --git a/src/service_inspectors/http2_inspect/http2_frame.cc b/src/service_inspectors/http2_inspect/http2_frame.cc index aeca87988..5f69a93d6 100644 --- a/src/service_inspectors/http2_inspect/http2_frame.cc +++ b/src/service_inspectors/http2_inspect/http2_frame.cc @@ -27,6 +27,7 @@ #include "http2_enum.h" #include "http2_flow_data.h" #include "http2_headers_frame.h" +#include "http2_settings_frame.h" #include "service_inspectors/http_inspect/http_field.h" using namespace HttpCommon; @@ -56,6 +57,10 @@ Http2Frame* Http2Frame::new_frame(const uint8_t* header, const int32_t header_le return new Http2HeadersFrame(header, header_len, data, data_len, session_data, source_id); break; + case FT_SETTINGS: + return new Http2SettingsFrame(header, header_len, data, data_len, session_data, + source_id); + break; default: return new Http2Frame(header, header_len, data, data_len, session_data, source_id); break; @@ -83,6 +88,18 @@ uint8_t Http2Frame::get_flags() return 0; } +uint32_t Http2Frame::get_stream_id() +{ + if (header.length() <= 0) + return INVALID_STREAM_ID; + + const uint8_t* header_start = header.start(); + return ((header_start[stream_id_index] & 0x7f) << 24) + + (header_start[stream_id_index + 1] << 16) + + (header_start[stream_id_index + 2] << 8) + + header_start[stream_id_index + 3]; +} + #ifdef REG_TEST void Http2Frame::print_frame(FILE* output) { diff --git a/src/service_inspectors/http2_inspect/http2_frame.h b/src/service_inspectors/http2_inspect/http2_frame.h index dc33efb16..e0d3a565e 100644 --- a/src/service_inspectors/http2_inspect/http2_frame.h +++ b/src/service_inspectors/http2_inspect/http2_frame.h @@ -51,6 +51,7 @@ protected: const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, HttpCommon::SourceId source_id); uint8_t get_flags(); + uint32_t get_stream_id(); Field header; Field data; @@ -58,5 +59,7 @@ protected: HttpCommon::SourceId source_id; const static uint8_t flags_index = 4; + const static uint8_t stream_id_index = 5; + const static uint32_t INVALID_STREAM_ID = 0xFFFFFFFF; }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_settings_frame.cc b/src/service_inspectors/http2_inspect/http2_settings_frame.cc new file mode 100644 index 000000000..b7a9a1660 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_settings_frame.cc @@ -0,0 +1,135 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// http2_settings_frame.cc author Deepak Ramadass + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "http2_settings_frame.h" + +#include "service_inspectors/http_inspect/http_test_manager.h" + +#include "http2_enum.h" +#include "http2_flow_data.h" + +using namespace Http2Enums; + +static const uint8_t SfSize = 6; + +static uint16_t get_parameter_id(const uint8_t* data_buffer) +{ + return (data_buffer[0] << 8) + data_buffer[1]; +} + +static uint32_t get_parameter_value(const uint8_t* data_buffer) +{ + static const uint8_t frame_value_index = 2; + return (data_buffer[frame_value_index] << 24) + + (data_buffer[frame_value_index + 1] << 16) + + (data_buffer[frame_value_index + 2] << 8) + + data_buffer[frame_value_index + 3]; +} + +Http2SettingsFrame::Http2SettingsFrame(const uint8_t* header_buffer, const int32_t header_len, + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, + HttpCommon::SourceId source_id) : Http2Frame(header_buffer, header_len, data_buffer, data_len, + session_data, source_id) +{ + if (!sanity_check()) + { + session_data->events[source_id]->create_event(EVENT_SETTINGS_FRAME_ERROR); + *session_data->infractions[source_id] += INF_INVALID_SETTINGS_FRAME; + return; + } + + if (SfAck & get_flags()) + return; + + parse_settings_frame(); +} + +void Http2SettingsFrame::parse_settings_frame() +{ + int32_t data_pos = 0; + + while (data_pos < data.length()) + { + uint16_t parameter_id = get_parameter_id(data.start() + data_pos); + uint32_t parameter_value = get_parameter_value(data.start() + data_pos); + + data_pos += SfSize; + + if (parameter_id < HEADER_TABLE_SIZE or parameter_id > MAX_HEADER_LIST_SIZE) + { + session_data->events[source_id]->create_event(EVENT_SETTINGS_FRAME_UNKN_PARAM); + *session_data->infractions[source_id] += INF_SETTINGS_FRAME_UNKN_PARAM; + continue; + } + + session_data->connection_settings[source_id].set_param(parameter_id, parameter_value); + } +} + +bool Http2SettingsFrame::sanity_check() +{ + const bool ack = SfAck & get_flags(); + + if (get_stream_id() != 0) + bad_frame = true; + else if (!ack and ((data.length() % 6) != 0)) + bad_frame = true; + else if (ack and data.length() > 0) + bad_frame = true; + + return !(bad_frame); +} + +#ifdef REG_TEST +void Http2SettingsFrame::print_frame(FILE* output) +{ + fprintf(output, "SETTINGS frame:"); + + if (bad_frame) + fprintf(output, " Error in settings frame."); + else if (SfAck & get_flags()) + fprintf(output, " ACK"); + else + fprintf(output, " Parameters in current frame - %d.", (data.length()/6)) ; + + fprintf(output, "\n"); + Http2Frame::print_frame(output); + fprintf(output, "\n"); +} +#endif + +uint32_t Http2ConnectionSettings::get_param(uint16_t id) +{ + assert(id >= HEADER_TABLE_SIZE); + assert(id <= MAX_HEADER_LIST_SIZE); + + return parameters[id - 1]; +} + +void Http2ConnectionSettings::set_param(uint16_t id, uint32_t value) +{ + assert(id >= HEADER_TABLE_SIZE); + assert(id <= MAX_HEADER_LIST_SIZE); + + parameters[id - 1] = value; +} diff --git a/src/service_inspectors/http2_inspect/http2_settings_frame.h b/src/service_inspectors/http2_inspect/http2_settings_frame.h new file mode 100644 index 000000000..a5d9e41bb --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_settings_frame.h @@ -0,0 +1,71 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// http2_settings_frame.cc author Deepak Ramadass + +#ifndef HTTP2_SETTINGS_FRAME_H +#define HTTP2_SETTINGS_FRAME_H + +#include "http2_frame.h" + +class Field; +class Http2ConnectionSettings; +class Http2Frame; + +class Http2SettingsFrame : public Http2Frame +{ +public: + friend Http2Frame* Http2Frame::new_frame(const uint8_t* header_buffer, const int32_t header_len, + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, + HttpCommon::SourceId source_id); + +#ifdef REG_TEST + void print_frame(FILE* output) override; +#endif + +private: + Http2SettingsFrame(const uint8_t* header_buffer, const int32_t header_len, + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, + HttpCommon::SourceId source_id); + + void parse_settings_frame(); + bool sanity_check(); + + bool bad_frame = false; + static const uint8_t SfAck = 0x01; +}; + +class Http2ConnectionSettings +{ +public: + uint32_t get_param(uint16_t id); + void set_param(uint16_t id, uint32_t value); + +private: + void validate_param_id(uint16_t id); + + static const uint16_t PARAMETER_COUNT = 6; + uint32_t parameters[PARAMETER_COUNT] = { + 4096, // Header table size + 1, // Push promise + 100, // Max concurrent Streams + 65535, // Window size + 16384, // Max frame size + 4294967295 // Max header list size + }; +}; +#endif diff --git a/src/service_inspectors/http2_inspect/http2_tables.cc b/src/service_inspectors/http2_inspect/http2_tables.cc index 8ba1c37c4..bf270adb9 100644 --- a/src/service_inspectors/http2_inspect/http2_tables.cc +++ b/src/service_inspectors/http2_inspect/http2_tables.cc @@ -41,6 +41,8 @@ const RuleMap Http2Module::http2_events[] = { EVENT_REQUEST_WITHOUT_REQUIRED_FIELD, "HTTP/2 request missing required header field" }, { EVENT_RESPONSE_WITHOUT_STATUS, "HTTP/2 response has no status code" }, { EVENT_INVALID_HEADER, "invalid HTTP/2 header field" }, + { EVENT_SETTINGS_FRAME_ERROR, "error in HTTP/2 settings frame" }, + { EVENT_SETTINGS_FRAME_UNKN_PARAM, "unknown parameter in HTTP/2 settings frame" }, { 0, nullptr } };