From: Mike Stepanek (mstepane) Date: Fri, 30 Oct 2020 20:47:31 +0000 (+0000) Subject: Merge pull request #2585 in SNORT/snort3 from ~KATHARVE/snort3:h2i_pp3_final to master X-Git-Tag: 3.0.3-5~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f8e51d051bf61ee116e91b39d24f2eeadc79ff71;p=thirdparty%2Fsnort3.git Merge pull request #2585 in SNORT/snort3 from ~KATHARVE/snort3:h2i_pp3_final to master Squashed commit of the following: commit 0c21bbf58fcc70d1e1cbb758589796a442b97ebb Author: Katura Harvey Date: Thu Oct 15 16:30:25 2020 -0400 http2_inspect: send push_promise frames through http_inspect --- diff --git a/src/service_inspectors/http2_inspect/CMakeLists.txt b/src/service_inspectors/http2_inspect/CMakeLists.txt index 8ac11eef3..b5bf70b4e 100644 --- a/src/service_inspectors/http2_inspect/CMakeLists.txt +++ b/src/service_inspectors/http2_inspect/CMakeLists.txt @@ -17,6 +17,8 @@ set (FILE_LIST http2_headers_frame_header.h http2_headers_frame_trailer.cc http2_headers_frame_trailer.h + http2_headers_frame_with_startline.cc + http2_headers_frame_with_startline.h http2_hpack.cc http2_hpack.h http2_hpack_dynamic_table.cc diff --git a/src/service_inspectors/http2_inspect/dev_notes.txt b/src/service_inspectors/http2_inspect/dev_notes.txt index 83b4daf31..d36a149c1 100644 --- a/src/service_inspectors/http2_inspect/dev_notes.txt +++ b/src/service_inspectors/http2_inspect/dev_notes.txt @@ -1,20 +1,31 @@ The HTTP/2 inspector (H2I) converts HTTP/2 frames into HTTP/1.1 message sections and feeds them to the new HTTP inspector (NHI) for further processing. -The Http2StreamSplitter strips the frame headers from the frame data and stores them in separate -buffers. As in NHI, long data frames are split into 16kb chunks for inspection. If a header frame -is followed by continuation frames, all the header frames are flushed together for inspection. The -frame headers from each frame are stored contiguously in the frame_header buffer. After cutting out -the frame headers, the frame data is stored as a single block, consisting of the HPACK encoded -HTTP/2 headers. +The Http2StreamSplitter splits HTTP/2 traffic on frame boundaries with two exceptions. First, any +Continuation frames following a Headers frame are aggregated and sent through detection as a single +header block. Second, long Data frames are split into 16kb chunks for inspection as message bodies +are in NHI. The Http2StreamSplitter strips the frame headers from the frame data and stores them in +separate buffers. In the case of a Headers frame followed by Continuation frames, the Headers frame +header is stored and the Continuation frame headers are discarded. + +HTTP/2 frames are stored in frame objects for processing. +1. Http2Frame - top level class +2. Http2HeadersFrame : Http2Frame - common elements of frames containing headers +3. Htt2HeadersFrameWithStartline : Http2HeadersFrame - common elements of headers with start lines +4. Http2HeadersFrameHeader : Htt2HeadersFrameWithStartline - regular request/response headers +5. Http2PushPromiseFrame : Htt2HeadersFrameWithStartline - server push headers +6. Http2HeadersFrameTrailer : Http2HeadersFrame - headers frame containing trailers +7. Http2DataFrame : Http2Frame +8. Http2SettingsFrame : Http2Frame HTTP/2 headers frames can come at the start of a stream and contain the header block, or at the end of a stream and contain trailers. H2I contains headers frame subclasses Http2HeadersFrameHeader and -Http2HeadersFrameTrailer to support these two types of headers frames. Headers frames containing the -header block will contain pseudo-headers that must be converted into an HTTP/1.1 start line in -addition to regular HTTP/1.1 headers. The two Http2StartLine subclasses, Http2RequestLine and -Http2ResponseLine perform this translation and generate the start-line, which is stored in a new -buffer inside the Http2StartLine object. Trailers may only contain regular headers. +Http2HeadersFrameTrailer to support these two types of headers frames. Http2PushPromise frames begin +server pushed streams and also contain the header block. Headers frames containing the header block +will contain pseudo-headers that must be converted into an HTTP/1.1 start line in addition to +regular HTTP/1.1 headers. The two Http2StartLine subclasses, Http2RequestLine and Http2ResponseLine +perform this translation and generate the start-line, which is stored in a new buffer inside the +Http2StartLine object. Trailers may only contain regular headers. Both headers and trailers must undergo HPACK decoding before being sent to NHI for processing. To perform decoding, reassemble() makes a first copy of the encoded headers, which is stored in the diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.cc b/src/service_inspectors/http2_inspect/http2_flow_data.cc index 181c99ec8..b4fb63084 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.cc +++ b/src/service_inspectors/http2_inspect/http2_flow_data.cc @@ -157,23 +157,30 @@ class Http2Stream* Http2FlowData::get_current_stream(HttpCommon::SourceId source return get_stream(current_stream[source_id]); } +class Http2Stream* Http2FlowData::get_processing_stream() +{ + return get_stream(get_processing_stream_id()); +} + +uint32_t Http2FlowData::get_processing_stream_id() const +{ + return processing_stream_id; +} + // processing stream is the current stream except for push promise frames with properly formatted // promised stream IDs -class Http2Stream* Http2FlowData::get_processing_stream(const HttpCommon::SourceId source_id) +void Http2FlowData::set_processing_stream_id(const HttpCommon::SourceId source_id) { - if (processing_stream_id[source_id] == NO_STREAM_ID) - { - if (frame_type[source_id] == FT_PUSH_PROMISE) - processing_stream_id[source_id] = Http2PushPromiseFrame::get_promised_stream_id( - events[source_id], infractions[source_id], frame_data[source_id], - frame_data_size[source_id]); - if (processing_stream_id[source_id] == NO_STREAM_ID) - processing_stream_id[source_id] = current_stream[source_id]; - } - return get_stream(processing_stream_id[source_id]); + assert(processing_stream_id == NO_STREAM_ID); + if (frame_type[source_id] == FT_PUSH_PROMISE) + processing_stream_id = Http2PushPromiseFrame::get_promised_stream_id( + events[source_id], infractions[source_id], frame_data[source_id], + frame_data_size[source_id]); + if (processing_stream_id == NO_STREAM_ID) + processing_stream_id = current_stream[source_id]; } -uint32_t Http2FlowData::get_current_stream_id(const HttpCommon::SourceId source_id) +uint32_t Http2FlowData::get_current_stream_id(const HttpCommon::SourceId source_id) const { return current_stream[source_id]; } diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.h b/src/service_inspectors/http2_inspect/http2_flow_data.h index dede05818..962a14154 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.h +++ b/src/service_inspectors/http2_inspect/http2_flow_data.h @@ -69,6 +69,7 @@ public: friend class Http2HeadersFrame; friend class Http2HeadersFrameHeader; friend class Http2HeadersFrameTrailer; + friend class Http2HeadersFrameWithStartline; friend class Http2Hpack; friend class Http2Inspect; friend class Http2PushPromiseFrame; @@ -94,8 +95,10 @@ public: ~StreamInfo() { delete stream; } }; class Http2Stream* get_current_stream(const HttpCommon::SourceId source_id); - uint32_t get_current_stream_id(const HttpCommon::SourceId source_id); - class Http2Stream* get_processing_stream(const HttpCommon::SourceId source_id); + uint32_t get_current_stream_id(const HttpCommon::SourceId source_id) const; + class Http2Stream* get_processing_stream(); + uint32_t get_processing_stream_id() const; + void set_processing_stream_id(const HttpCommon::SourceId source_id); Http2HpackDecoder* get_hpack_decoder(const HttpCommon::SourceId source_id) { return &hpack_decoder[source_id]; } @@ -128,8 +131,9 @@ protected: // header). This is set in scan(). uint32_t current_stream[2] = { Http2Enums::NO_STREAM_ID, Http2Enums::NO_STREAM_ID }; // Stream ID of the stream responsible for processing the current frame. This will be the same - // as current_stream except when processing a push_promise frame. This is set in eval(). - uint32_t processing_stream_id[2] = { Http2Enums::NO_STREAM_ID, Http2Enums::NO_STREAM_ID }; + // as current_stream except when processing a push_promise frame. This is set in eval() and + // cleared in clear(). + uint32_t processing_stream_id = Http2Enums::NO_STREAM_ID; // At any given time there may be different streams going in each direction. But only one of // them is the stream that http_inspect is actually processing at the moment. uint32_t stream_in_hi = Http2Enums::NO_STREAM_ID; diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame.cc b/src/service_inspectors/http2_inspect/http2_headers_frame.cc index 02ab6eaae..69f32b586 100644 --- a/src/service_inspectors/http2_inspect/http2_headers_frame.cc +++ b/src/service_inspectors/http2_inspect/http2_headers_frame.cc @@ -76,7 +76,7 @@ void Http2HeadersFrame::clear() session_data->hi->clear(&dummy_pkt); } -void Http2HeadersFrame::process_decoded_headers(HttpFlowData* http_flow) +void Http2HeadersFrame::process_decoded_headers(HttpFlowData* http_flow, SourceId hi_source_id) { if (session_data->abort_flow[source_id]) return; @@ -91,7 +91,7 @@ void Http2HeadersFrame::process_decoded_headers(HttpFlowData* http_flow) dummy_pkt.flow = session_data->flow; const uint32_t unused = 0; const StreamSplitter::Status header_scan_result = - session_data->hi_ss[source_id]->scan(&dummy_pkt, http1_header.start(), + session_data->hi_ss[hi_source_id]->scan(&dummy_pkt, http1_header.start(), http1_header.length(), unused, &flush_offset); assert(header_scan_result == StreamSplitter::FLUSH); UNUSED(header_scan_result); @@ -101,7 +101,7 @@ void Http2HeadersFrame::process_decoded_headers(HttpFlowData* http_flow) // http_inspect reassemble() of headers { unsigned copied; - stream_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow, + stream_buf = session_data->hi_ss[hi_source_id]->reassemble(session_data->flow, http1_header.length(), 0, http1_header.start(), http1_header.length(), PKT_PDU_TAIL, copied); assert(stream_buf.data != nullptr); @@ -112,7 +112,7 @@ void Http2HeadersFrame::process_decoded_headers(HttpFlowData* http_flow) { Http2DummyPacket dummy_pkt; dummy_pkt.flow = session_data->flow; - dummy_pkt.packet_flags = (source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER; + dummy_pkt.packet_flags = (hi_source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER; dummy_pkt.dsize = stream_buf.length; dummy_pkt.data = stream_buf.data; dummy_pkt.xtradata_mask = 0; @@ -120,7 +120,7 @@ void Http2HeadersFrame::process_decoded_headers(HttpFlowData* http_flow) // Following if condition won't get exercised until finish() (during Headers) is // implemented for H2I. Without finish() H2I will only flush complete header blocks. Below // ABORT is only possible if tcp connection closes unexpectedly in middle of a header. - if (http_flow->get_type_expected(source_id) == HttpEnums::SEC_ABORT) + if (http_flow->get_type_expected(hi_source_id) == HttpEnums::SEC_ABORT) { *session_data->infractions[source_id] += INF_INVALID_HEADER; session_data->events[source_id]->create_event(EVENT_INVALID_HEADER); diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame.h b/src/service_inspectors/http2_inspect/http2_headers_frame.h index 7f7c4904c..5016bb6c0 100644 --- a/src/service_inspectors/http2_inspect/http2_headers_frame.h +++ b/src/service_inspectors/http2_inspect/http2_headers_frame.h @@ -47,7 +47,7 @@ protected: Http2HeadersFrame(const uint8_t* header_buffer, const uint32_t header_len, const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* ssn_data, HttpCommon::SourceId src_id, Http2Stream* stream); - void process_decoded_headers(HttpFlowData* http_flow); + void process_decoded_headers(HttpFlowData* http_flow, HttpCommon::SourceId hi_source_id); uint8_t* decoded_headers = nullptr; // working buffer to store decoded headers Field http1_header; // finalized headers to be passed to NHI diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame_header.cc b/src/service_inspectors/http2_inspect/http2_headers_frame_header.cc index c91003fb1..f28db8f3c 100644 --- a/src/service_inspectors/http2_inspect/http2_headers_frame_header.cc +++ b/src/service_inspectors/http2_inspect/http2_headers_frame_header.cc @@ -23,18 +23,13 @@ #include "http2_headers_frame_header.h" -#include "protocols/packet.h" #include "service_inspectors/http_inspect/http_enum.h" #include "service_inspectors/http_inspect/http_flow_data.h" -#include "service_inspectors/http_inspect/http_inspect.h" -#include "service_inspectors/http_inspect/http_stream_splitter.h" -#include "http2_dummy_packet.h" #include "http2_enum.h" #include "http2_flow_data.h" #include "http2_hpack.h" #include "http2_request_line.h" -#include "http2_start_line.h" #include "http2_status_line.h" #include "http2_stream.h" @@ -45,8 +40,8 @@ using namespace Http2Enums; Http2HeadersFrameHeader::Http2HeadersFrameHeader(const uint8_t* header_buffer, const uint32_t header_len, const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* session_data_, HttpCommon::SourceId source_id_, Http2Stream* stream_) : - Http2HeadersFrame(header_buffer, header_len, data_buffer, data_len, session_data_, source_id_, - stream_) + Http2HeadersFrameWithStartline(header_buffer, header_len, data_buffer, data_len, session_data_, + source_id_, stream_) { if (!process_frame) return; @@ -76,11 +71,6 @@ Http2HeadersFrameHeader::Http2HeadersFrameHeader(const uint8_t* header_buffer, } } -Http2HeadersFrameHeader::~Http2HeadersFrameHeader() -{ - delete start_line_generator; -} - bool Http2HeadersFrameHeader::valid_sequence(Http2Enums::StreamState state) { return (state == Http2Enums::STREAM_EXPECT_HEADERS); @@ -91,53 +81,15 @@ void Http2HeadersFrameHeader::analyze_http1() if (!process_frame) return; - // http_inspect scan() of start line - { - uint32_t flush_offset; - Http2DummyPacket dummy_pkt; - dummy_pkt.flow = session_data->flow; - const uint32_t unused = 0; - const StreamSplitter::Status start_scan_result = - session_data->hi_ss[source_id]->scan(&dummy_pkt, start_line.start(), - start_line.length(), unused, &flush_offset); - assert(start_scan_result == StreamSplitter::FLUSH); - UNUSED(start_scan_result); - assert((int64_t)flush_offset == start_line.length()); - } - - StreamBuffer stream_buf; - - // http_inspect reassemble() of start line - { - unsigned copied; - stream_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow, - start_line.length(), 0, start_line.start(), start_line.length(), PKT_PDU_TAIL, - copied); - assert(stream_buf.data != nullptr); - assert(copied == (unsigned)start_line.length()); - } + HttpFlowData* http_flow; + if (!process_start_line(http_flow, source_id)) + return; - HttpFlowData* const http_flow = - session_data->get_current_stream(source_id)->get_hi_flow_data(); - // http_inspect eval() and clear() of start line - { - Http2DummyPacket dummy_pkt; - dummy_pkt.flow = session_data->flow; - dummy_pkt.packet_flags = (source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER; - dummy_pkt.dsize = stream_buf.length; - dummy_pkt.data = stream_buf.data; - session_data->hi->eval(&dummy_pkt); - if (http_flow->get_type_expected(source_id) != HttpEnums::SEC_HEADER) - { - *session_data->infractions[source_id] += INF_INVALID_STARTLINE; - session_data->events[source_id]->create_event(EVENT_INVALID_STARTLINE); - stream->set_state(source_id, STREAM_ERROR); - return; - } - session_data->hi->clear(&dummy_pkt); - } + // if END_STREAM flag set on headers, tell http_inspect not to expect a message body + if (get_flags() & END_STREAM) + stream->get_hi_flow_data()->finish_h2_body(source_id, HttpEnums::H2_BODY_NO_BODY, false); - process_decoded_headers(http_flow); + process_decoded_headers(http_flow, source_id); } void Http2HeadersFrameHeader::update_stream_state() @@ -154,7 +106,6 @@ void Http2HeadersFrameHeader::update_stream_state() void Http2HeadersFrameHeader::print_frame(FILE* output) { fprintf(output, "Headers frame\n"); - start_line.print(output, "Decoded start-line"); - Http2HeadersFrame::print_frame(output); + Http2HeadersFrameWithStartline::print_frame(output); } #endif diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame_header.h b/src/service_inspectors/http2_inspect/http2_headers_frame_header.h index a4af8f04e..c031aa6bc 100644 --- a/src/service_inspectors/http2_inspect/http2_headers_frame_header.h +++ b/src/service_inspectors/http2_inspect/http2_headers_frame_header.h @@ -21,15 +21,13 @@ #define HTTP2_HEADERS_FRAME_HEADER_H #include "http2_frame.h" -#include "http2_headers_frame.h" +#include "http2_headers_frame_with_startline.h" class Http2StartLine; -class Http2HeadersFrameHeader : public Http2HeadersFrame +class Http2HeadersFrameHeader : public Http2HeadersFrameWithStartline { public: - ~Http2HeadersFrameHeader() override; - friend Http2Frame* Http2Frame::new_frame(const uint8_t*, const uint32_t, const uint8_t*, const uint32_t, Http2FlowData*, HttpCommon::SourceId, Http2Stream* stream); @@ -45,8 +43,5 @@ private: Http2HeadersFrameHeader(const uint8_t* header_buffer, const uint32_t header_len, const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* ssn_data, HttpCommon::SourceId src_id, Http2Stream* stream); - - Http2StartLine* start_line_generator = nullptr; - Field start_line; }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame_trailer.cc b/src/service_inspectors/http2_inspect/http2_headers_frame_trailer.cc index 442a59e7a..b73796400 100644 --- a/src/service_inspectors/http2_inspect/http2_headers_frame_trailer.cc +++ b/src/service_inspectors/http2_inspect/http2_headers_frame_trailer.cc @@ -81,8 +81,7 @@ void Http2HeadersFrameTrailer::analyze_http1() if (!process_frame) return; - HttpFlowData* const http_flow = - session_data->get_current_stream(source_id)->get_hi_flow_data(); + HttpFlowData* const http_flow = stream->get_hi_flow_data(); assert(http_flow); if (http_flow->get_type_expected(source_id) != HttpEnums::SEC_TRAILER) @@ -115,7 +114,7 @@ void Http2HeadersFrameTrailer::analyze_http1() session_data->hi->clear(&dummy_pkt); } - process_decoded_headers(http_flow); + process_decoded_headers(http_flow, source_id); } void Http2HeadersFrameTrailer::update_stream_state() 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 new file mode 100644 index 000000000..646fcbbe9 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.cc @@ -0,0 +1,112 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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_headers_frame_with_startline.cc author Katura Harvey + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "http2_headers_frame_with_startline.h" + +#include "protocols/packet.h" +#include "service_inspectors/http_inspect/http_enum.h" +#include "service_inspectors/http_inspect/http_flow_data.h" +#include "service_inspectors/http_inspect/http_inspect.h" +#include "service_inspectors/http_inspect/http_stream_splitter.h" + +#include "http2_dummy_packet.h" +#include "http2_enum.h" +#include "http2_flow_data.h" +#include "http2_hpack.h" +#include "http2_request_line.h" +#include "http2_start_line.h" +#include "http2_status_line.h" +#include "http2_stream.h" + +using namespace snort; +using namespace HttpCommon; +using namespace Http2Enums; + + +Http2HeadersFrameWithStartline::~Http2HeadersFrameWithStartline() +{ + delete start_line_generator; +} + +bool Http2HeadersFrameWithStartline::process_start_line(HttpFlowData*& http_flow, + SourceId hi_source_id) +{ + if (session_data->abort_flow[source_id]) + return false; + + // http_inspect scan() of start line + { + uint32_t flush_offset; + Http2DummyPacket dummy_pkt; + dummy_pkt.flow = session_data->flow; + const uint32_t unused = 0; + const StreamSplitter::Status start_scan_result = + session_data->hi_ss[hi_source_id]->scan(&dummy_pkt, start_line.start(), + start_line.length(), unused, &flush_offset); + assert(start_scan_result == StreamSplitter::FLUSH); + UNUSED(start_scan_result); + assert((int64_t)flush_offset == start_line.length()); + } + + StreamBuffer stream_buf; + + // http_inspect reassemble() of start line + { + unsigned copied; + stream_buf = session_data->hi_ss[hi_source_id]->reassemble(session_data->flow, + start_line.length(), 0, start_line.start(), start_line.length(), PKT_PDU_TAIL, + copied); + assert(stream_buf.data != nullptr); + assert(copied == (unsigned)start_line.length()); + } + + http_flow = stream->get_hi_flow_data(); + assert(http_flow); + // http_inspect eval() and clear() of start line + { + Http2DummyPacket dummy_pkt; + dummy_pkt.flow = session_data->flow; + dummy_pkt.packet_flags = (hi_source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER; + dummy_pkt.dsize = stream_buf.length; + dummy_pkt.data = stream_buf.data; + session_data->hi->eval(&dummy_pkt); + if (http_flow->get_type_expected(hi_source_id) != HttpEnums::SEC_HEADER) + { + *session_data->infractions[source_id] += INF_INVALID_STARTLINE; + session_data->events[source_id]->create_event(EVENT_INVALID_STARTLINE); + stream->set_state(hi_source_id, STREAM_ERROR); + return false; + } + session_data->hi->clear(&dummy_pkt); + } + return true; +} + + +#ifdef REG_TEST +void Http2HeadersFrameWithStartline::print_frame(FILE* output) +{ + start_line.print(output, "Decoded start-line"); + Http2HeadersFrame::print_frame(output); +} +#endif diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.h b/src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.h new file mode 100644 index 000000000..f759496b7 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.h @@ -0,0 +1,54 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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_headers_frame_with_startline.h author Katura Harvey + +#ifndef HTTP2_HEADERS_FRAME_WITH_STARTLINE_H +#define HTTP2_HEADERS_FRAME_WITH_STARTLINE_H + +#include "service_inspectors/http_inspect/http_common.h" + +#include "http2_enum.h" +#include "http2_frame.h" +#include "http2_headers_frame.h" + +class Field; +class Http2Frame; +class Http2Stream; +class HttpFlowData; + +class Http2HeadersFrameWithStartline : public Http2HeadersFrame +{ +public: + ~Http2HeadersFrameWithStartline() override; + +#ifdef REG_TEST + void print_frame(FILE* output) override; +#endif + +protected: + Http2HeadersFrameWithStartline(const uint8_t* header_buffer, const uint32_t header_len, + const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* ssn_data, + HttpCommon::SourceId src_id, Http2Stream* stream_) : + Http2HeadersFrame(header_buffer, header_len, data_buffer, data_len, ssn_data, src_id, + stream_) { } + bool process_start_line(HttpFlowData*& http_flow, HttpCommon::SourceId hi_source_id); + + Http2StartLine* start_line_generator = nullptr; + Field start_line; +}; +#endif diff --git a/src/service_inspectors/http2_inspect/http2_inspect.cc b/src/service_inspectors/http2_inspect/http2_inspect.cc index 35cc75134..8d4bbd7a2 100644 --- a/src/service_inspectors/http2_inspect/http2_inspect.cc +++ b/src/service_inspectors/http2_inspect/http2_inspect.cc @@ -86,8 +86,7 @@ bool Http2Inspect::get_buf(unsigned id, Packet* p, InspectionBuffer& b) if (!session_data->frame_in_detection) return false; - const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER; - Http2Stream* const stream = session_data->get_processing_stream(source_id); + Http2Stream* const stream = session_data->get_processing_stream(); const Field& buffer = stream->get_buf(id); if (buffer.length() <= 0) return false; @@ -128,8 +127,9 @@ void Http2Inspect::eval(Packet* p) return; } - Http2Stream* stream = session_data->get_processing_stream(source_id); - assert(session_data->processing_stream_id[source_id] != NO_STREAM_ID); + session_data->set_processing_stream_id(source_id); + Http2Stream* stream = session_data->get_processing_stream(); + assert(session_data->processing_stream_id != NO_STREAM_ID); assert(stream); session_data->stream_in_hi = stream->get_stream_id(); @@ -180,12 +180,10 @@ void Http2Inspect::clear(Packet* p) session_data->frame_in_detection = false; - const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER; - - Http2Stream* stream = session_data->get_processing_stream(source_id); + Http2Stream* stream = session_data->get_processing_stream(); stream->clear_frame(); session_data->stream_in_hi = NO_STREAM_ID; - session_data->processing_stream_id[source_id] = NO_STREAM_ID; + session_data->processing_stream_id = NO_STREAM_ID; } #ifdef REG_TEST 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 e370f0064..46c23e73a 100644 --- a/src/service_inspectors/http2_inspect/http2_push_promise_frame.cc +++ b/src/service_inspectors/http2_inspect/http2_push_promise_frame.cc @@ -23,21 +23,24 @@ #include "http2_push_promise_frame.h" +#include "service_inspectors/http_inspect/http_enum.h" +#include "service_inspectors/http_inspect/http_flow_data.h" + #include "http2_flow_data.h" #include "http2_hpack.h" #include "http2_request_line.h" -#include "http2_start_line.h" #include "http2_stream.h" #include "http2_utils.h" +using namespace snort; using namespace HttpCommon; using namespace Http2Enums; Http2PushPromiseFrame::Http2PushPromiseFrame(const uint8_t* header_buffer, const uint32_t header_len, const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* session_data_, HttpCommon::SourceId source_id_, Http2Stream* stream_) : - Http2HeadersFrame(header_buffer, header_len, data_buffer, data_len, session_data_, source_id_, - stream_) + Http2HeadersFrameWithStartline(header_buffer, header_len, data_buffer, data_len, session_data_, + source_id_, stream_) { // If this was a short frame, it's being processed by the stream that sent it. We've already // alerted @@ -79,11 +82,6 @@ Http2PushPromiseFrame::Http2PushPromiseFrame(const uint8_t* header_buffer, } } -Http2PushPromiseFrame::~Http2PushPromiseFrame() -{ - delete start_line_generator; -} - bool Http2PushPromiseFrame::valid_sequence(Http2Enums::StreamState) { if (data.length() < PROMISED_ID_LENGTH) @@ -116,15 +114,8 @@ bool Http2PushPromiseFrame::valid_sequence(Http2Enums::StreamState) return true; } -// FIXIT-E current implementation for testing purposes only. Headers are not yet being sent to -// http_inspect. void Http2PushPromiseFrame::analyze_http1() { - if (session_data->abort_flow[source_id]) - return; - - detection_required = true; - if (!start_line_generator->generate_start_line(start_line)) { // can't send request or push-promise headers to http_inspect, but response will still @@ -133,7 +124,15 @@ void Http2PushPromiseFrame::analyze_http1() return; } - http1_header = hpack_decoder->get_decoded_headers(decoded_headers); + HttpFlowData* http_flow; + if (!process_start_line(http_flow, SRC_CLIENT)) + return; + + // Push promise cannot have a message body + // FIXIT-E handle bad request lines and cases where a message body is implied + stream->get_hi_flow_data()->finish_h2_body(SRC_CLIENT, HttpEnums::H2_BODY_NO_BODY, false); + + process_decoded_headers(http_flow, SRC_CLIENT); } void Http2PushPromiseFrame::update_stream_state() @@ -164,7 +163,6 @@ uint32_t Http2PushPromiseFrame::get_promised_stream_id(Http2EventGen* const even void Http2PushPromiseFrame::print_frame(FILE* output) { fprintf(output, "Push_Promise frame\n"); - start_line.print(output, "Decoded start-line"); - Http2HeadersFrame::print_frame(output); + Http2HeadersFrameWithStartline::print_frame(output); } #endif diff --git a/src/service_inspectors/http2_inspect/http2_push_promise_frame.h b/src/service_inspectors/http2_inspect/http2_push_promise_frame.h index 604e56d87..dc5838532 100644 --- a/src/service_inspectors/http2_inspect/http2_push_promise_frame.h +++ b/src/service_inspectors/http2_inspect/http2_push_promise_frame.h @@ -26,7 +26,7 @@ #include "http2_enum.h" #include "http2_frame.h" -#include "http2_headers_frame.h" +#include "http2_headers_frame_with_startline.h" class Field; class Http2Frame; @@ -37,10 +37,9 @@ using Http2Infractions = Infractions; -class Http2PushPromiseFrame : public Http2HeadersFrame +class Http2PushPromiseFrame : public Http2HeadersFrameWithStartline { public: - ~Http2PushPromiseFrame() override; bool valid_sequence(Http2Enums::StreamState state) override; void analyze_http1() override; void update_stream_state() override; @@ -59,7 +58,5 @@ private: const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* ssn_data, HttpCommon::SourceId src_id, Http2Stream* stream); static const int32_t PROMISED_ID_LENGTH = 4; - Http2StartLine* start_line_generator = nullptr; - Field start_line; }; #endif diff --git a/src/service_inspectors/http_inspect/http_enum.h b/src/service_inspectors/http_inspect/http_enum.h index 4e0a8b956..e8523bb40 100755 --- a/src/service_inspectors/http_inspect/http_enum.h +++ b/src/service_inspectors/http_inspect/http_enum.h @@ -385,7 +385,8 @@ extern const bool is_sp_tab_quote_dquote[256]; extern const bool is_print_char[256]; // printable includes SP, tab, CR, LF extern const bool is_sp_comma[256]; -enum H2BodyState { H2_BODY_NOT_COMPLETE, H2_BODY_COMPLETE, H2_BODY_COMPLETE_EXPECT_TRAILERS }; +enum H2BodyState { H2_BODY_NOT_COMPLETE, H2_BODY_COMPLETE, H2_BODY_COMPLETE_EXPECT_TRAILERS, + H2_BODY_NO_BODY }; } // end namespace HttpEnums diff --git a/src/service_inspectors/http_inspect/http_msg_header.cc b/src/service_inspectors/http_inspect/http_msg_header.cc index 74ab1e0ee..cd98bcb0c 100755 --- a/src/service_inspectors/http_inspect/http_msg_header.cc +++ b/src/service_inspectors/http_inspect/http_msg_header.cc @@ -55,7 +55,7 @@ HttpMsgHeader::HttpMsgHeader(const uint8_t* buffer, const uint16_t buf_size, void HttpMsgHeader::publish() { - const uint32_t stream_id = get_h2_stream_id(source_id); + const uint32_t stream_id = get_h2_stream_id(); HttpEvent http_event(this, session_data->for_http2, stream_id); @@ -267,8 +267,13 @@ void HttpMsgHeader::update_flow() create_event(EVENT_BAD_CONTENT_LENGTH); } } - session_data->type_expected[source_id] = SEC_BODY_H2; - prepare_body(); + if (session_data->h2_body_state[source_id] == H2_BODY_NO_BODY) + session_data->half_reset(source_id); + else + { + session_data->type_expected[source_id] = SEC_BODY_H2; + prepare_body(); + } return; } @@ -423,7 +428,7 @@ void HttpMsgHeader::setup_file_processing() } // Generate the unique file id for multi file processing - set_multi_file_processing_id(get_transaction_id(), get_h2_stream_id(source_id)); + set_multi_file_processing_id(get_transaction_id(), get_h2_stream_id()); // Do we meet all the conditions for MIME file processing? if (source_id == SRC_CLIENT) diff --git a/src/service_inspectors/http_inspect/http_msg_section.cc b/src/service_inspectors/http_inspect/http_msg_section.cc index b403369a3..cf744ab20 100644 --- a/src/service_inspectors/http_inspect/http_msg_section.cc +++ b/src/service_inspectors/http_inspect/http_msg_section.cc @@ -395,18 +395,20 @@ void HttpMsgSection::get_related_sections() trailer[SRC_SERVER] = transaction->get_trailer(SRC_SERVER); } -uint32_t HttpMsgSection::get_h2_stream_id(HttpCommon::SourceId source_id) +uint32_t HttpMsgSection::get_h2_stream_id() { if (h2_stream_id != STAT_NOT_COMPUTE) return h2_stream_id; + + h2_stream_id = 0; if (session_data->for_http2) { - Http2FlowData* h2i_flow_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id); + Http2FlowData* h2i_flow_data = + (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id); assert(h2i_flow_data); - h2_stream_id = h2i_flow_data->get_current_stream_id(source_id); + if (h2i_flow_data) + h2_stream_id = h2i_flow_data->get_processing_stream_id(); } - else - h2_stream_id = 0; return h2_stream_id; } diff --git a/src/service_inspectors/http_inspect/http_msg_section.h b/src/service_inspectors/http_inspect/http_msg_section.h index 660d74511..75dd51b19 100644 --- a/src/service_inspectors/http_inspect/http_msg_section.h +++ b/src/service_inspectors/http_inspect/http_msg_section.h @@ -108,7 +108,7 @@ protected: const bool tcp_close; int64_t h2_stream_id = HttpCommon::STAT_NOT_COMPUTE; - uint32_t get_h2_stream_id(HttpCommon::SourceId source_id); + uint32_t get_h2_stream_id(); // Pointers to related message sections in the same transaction HttpMsgRequest* request;