]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2585 in SNORT/snort3 from ~KATHARVE/snort3:h2i_pp3_final to master
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Fri, 30 Oct 2020 20:47:31 +0000 (20:47 +0000)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Fri, 30 Oct 2020 20:47:31 +0000 (20:47 +0000)
Squashed commit of the following:

commit 0c21bbf58fcc70d1e1cbb758589796a442b97ebb
Author: Katura Harvey <katharve@cisco.com>
Date:   Thu Oct 15 16:30:25 2020 -0400

    http2_inspect: send push_promise frames through http_inspect

18 files changed:
src/service_inspectors/http2_inspect/CMakeLists.txt
src/service_inspectors/http2_inspect/dev_notes.txt
src/service_inspectors/http2_inspect/http2_flow_data.cc
src/service_inspectors/http2_inspect/http2_flow_data.h
src/service_inspectors/http2_inspect/http2_headers_frame.cc
src/service_inspectors/http2_inspect/http2_headers_frame.h
src/service_inspectors/http2_inspect/http2_headers_frame_header.cc
src/service_inspectors/http2_inspect/http2_headers_frame_header.h
src/service_inspectors/http2_inspect/http2_headers_frame_trailer.cc
src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_headers_frame_with_startline.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_inspect.cc
src/service_inspectors/http2_inspect/http2_push_promise_frame.cc
src/service_inspectors/http2_inspect/http2_push_promise_frame.h
src/service_inspectors/http_inspect/http_enum.h
src/service_inspectors/http_inspect/http_msg_header.cc
src/service_inspectors/http_inspect/http_msg_section.cc
src/service_inspectors/http_inspect/http_msg_section.h

index 8ac11eef373de05c97f90378efda99374ff3fa35..b5bf70b4e92aabd8956b7432806de9b21b723f1b 100644 (file)
@@ -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
index 83b4daf3104c7ea577199de0345a11d04fbf6923..d36a149c1c374032d4b507a40daaa5649ca11f65 100644 (file)
@@ -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
index 181c99ec8311ccc5056cf55f954d818073df7fc5..b4fb63084cb863e6bd3924886b32f8fd76258be3 100644 (file)
@@ -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];
 }
index dede05818119cd7e2f7ea1a226aa2139f2d1b09e..962a14154a86e9bf5e4dd006133677785173afb4 100644 (file)
@@ -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;
index 02ab6eaaedb2a9abedfd18c0513a8aa631548e07..69f32b586c53d8ce93c9da6b6a3e8f27da1446a3 100644 (file)
@@ -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);
index 7f7c4904ca659fe51707c7b6425dc604894e9262..5016bb6c0ba7aa5f11342a75d8c22d49340000c3 100644 (file)
@@ -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
index c91003fb1f0eec8261066f154b93abc135e0d865..f28db8f3c1579093b73c31ea9bc0b034d76dd064 100644 (file)
 
 #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
index a4af8f04ef8451018c36f015dc60b959989b1d61..c031aa6bc093e7c36fad081fcb506a1c999e1bfa 100644 (file)
 #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
index 442a59e7a1e36d39118b9de2f4ebbe341b62cf4a..b737964005b30ba851ed7d0a3747c73f643a43da 100644 (file)
@@ -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 (file)
index 0000000..646fcbb
--- /dev/null
@@ -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 <katharve@cisco.com>
+
+#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 (file)
index 0000000..f759496
--- /dev/null
@@ -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 <katharve@cisco.com>
+
+#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
index 35cc75134ca480e9f4c6f46fdeb909e5c0daa26f..8d4bbd7a261c569a4c237a6ab21302ccd24a915e 100644 (file)
@@ -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
index e370f006440c5358babafe04f2090d701bd9774c..46c23e73aa6e84ae8a6601420e2a68b53af3f50c 100644 (file)
 
 #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
index 604e56d879b0898671bcbbe8040eff3c1ee1a0eb..dc5838532d56cfca8b8baf747b3549e8d91c5bbf 100644 (file)
@@ -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<Http2Enums::INF__MAX_VALUE, Http2Enums::INF
 using Http2EventGen = EventGen<Http2Enums::EVENT__MAX_VALUE, Http2Enums::EVENT__NONE,
     Http2Enums::HTTP2_GID>;
 
-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
index 4e0a8b95608cb0cdbdbfba0a747b9963d440c239..e8523bb409743fe112b304241e9d0c1f8fac1904 100755 (executable)
@@ -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
 
index 74ab1e0ee8d62b1c19f83f4e9d1125f361418212..cd98bcb0cfd1a891beca9ad9707e648f1bf4e393 100755 (executable)
@@ -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)
index b403369a345ec8eb09f690884e02479249b67d1e..cf744ab2097edf2ab175e8b1b7acb9ee0c32ee2d 100644 (file)
@@ -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;
 }
 
index 660d745115d4d2cc2d60419721a117405a5102eb..75dd51b191b9707c5bc90171acc7aa0781bb3588 100644 (file)
@@ -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;