]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2019 in SNORT/snort3 from ~MDAGON/snort3:data_frame to master
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Tue, 3 Mar 2020 19:51:34 +0000 (19:51 +0000)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Tue, 3 Mar 2020 19:51:34 +0000 (19:51 +0000)
Squashed commit of the following:

commit 00768b50e95acb2349676661affb73b40f2a53b1
Author: mdagon <mdagon@cisco.com>
Date:   Fri Jan 31 11:49:38 2020 -0500

    http2_inspect: send data frames to http - full frames only in a single flush

19 files changed:
doc/http2_inspect.txt
src/service_inspectors/http2_inspect/CMakeLists.txt
src/service_inspectors/http2_inspect/http2_api.cc
src/service_inspectors/http2_inspect/http2_data_cutter.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_data_cutter.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_data_frame.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_data_frame.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_enum.h
src/service_inspectors/http2_inspect/http2_flow_data.cc
src/service_inspectors/http2_inspect/http2_flow_data.h
src/service_inspectors/http2_inspect/http2_frame.cc
src/service_inspectors/http2_inspect/http2_headers_frame.cc
src/service_inspectors/http2_inspect/http2_inspect.cc
src/service_inspectors/http2_inspect/http2_stream.cc
src/service_inspectors/http2_inspect/http2_stream.h
src/service_inspectors/http2_inspect/http2_stream_splitter.cc
src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc
src/service_inspectors/http2_inspect/http2_tables.cc
src/service_inspectors/http2_inspect/ips_http2.cc

index 6db65d6a2971fbe36acb67821169ef9303f9666d..85577ee4e79c57ac629b65fd25d26c5297bacead 100644 (file)
@@ -7,13 +7,11 @@ You can configure it by adding:
 to your snort.lua configuration file.
 
 Everything has a beginning and for http2_inspect this is the beginning of
-the beginning. Most of the protocol including HPACK decompression is not
-implemented yet.
+the beginning.
 
 Currently http2_inspect will divide an HTTP/2 connection into individual
-frames and make them available for detection. Two new rule options are
-available for looking at HTTP/2 frames: http2_frame_header provides the
-9-octet frame header and http2_frame_data provides the frame content.
+frames. Two new rule options are available for looking at HTTP/2 frames:
+http2_frame_header provides the 9-octet frame header.
 
     alert tcp any any -> any any (msg:"Frame type"; flow:established,
     to_client; http2_frame_header; content:"|06|", offset 3, depth 1;
@@ -21,22 +19,6 @@ available for looking at HTTP/2 frames: http2_frame_header provides the
 
 This will match if the Type byte of the frame header is 6 (PING).
 
-    alert tcp any any -> any any ( msg:"Content of HTTP/2 frame";
-    flow:established, to_client; http2_frame_data; content:"peppermint";
-    sid:2; rev:1; )
-
-This will look for peppermint in the frame data but not the frame header.
-
-These can be combined:
-
-    alert tcp any any -> any any ( msg:"Search in message bodies";
-    flow:established, to_client;
-    http2_frame_header; content:"|00|", offset 3, depth 1;
-    http2_frame_data; content:"MaLwArE"; sid:3; rev:1; )
-
-Frame type 0 is DATA which carries the HTTP message body. This rule will
-search for MaLwArE inside an HTTP message body.
-
 To smooth the transition to inspecting HTTP/2, rules that specify 
 service:http will be treated as if they also specify service:http2. 
 Thus:
@@ -61,7 +43,6 @@ large numbers of existing rules. New rules should explicitly specify
 "service http,http2;" if that is the desired behavior. Eventually 
 support for http implies http2 may be deprecated and removed.
 
-In the future, http2_inspect will support HPACK header decompression and
-be fully integrated with http_inspect to provide full inspection of the
-individual HTTP/1.1 streams.
+In the future, http2_inspect will be fully integrated with http_inspect to
+provide full inspection of the individual HTTP/1.1 streams.
 
index 3d11259a0e480ec8e307eaa91cb9537f2ffc7fd6..4e81773037004cbea2bcf962c8486c6710a288ef 100644 (file)
@@ -2,6 +2,10 @@
 set (FILE_LIST
     http2_api.cc
     http2_api.h
+    http2_data_frame.cc
+    http2_data_frame.h
+    http2_data_cutter.cc
+    http2_data_cutter.h
     http2_enum.h
     http2_flow_data.cc
     http2_flow_data.h
index fd733aed0fddf9a5ff366bc5892e08a6c7b124a2..e0478b185c28808e18bb1ef548ca1347a4e51c48 100644 (file)
@@ -73,7 +73,6 @@ const InspectApi Http2Api::http2_api =
 };
 
 extern const BaseApi* ips_http2_frame_header;
-extern const BaseApi* ips_http2_frame_data;
 extern const BaseApi* ips_http2_decoded_header;
 
 #ifdef BUILDING_SO
@@ -84,7 +83,6 @@ const BaseApi* sin_http2[] =
 {
     &Http2Api::http2_api.base,
     ips_http2_frame_header,
-    ips_http2_frame_data,
     ips_http2_decoded_header,
     nullptr
 };
diff --git a/src/service_inspectors/http2_inspect/http2_data_cutter.cc b/src/service_inspectors/http2_inspect/http2_data_cutter.cc
new file mode 100644 (file)
index 0000000..b18f771
--- /dev/null
@@ -0,0 +1,290 @@
+//--------------------------------------------------------------------------
+// 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_data_cutter.cc author Maya Dagon <mdagon@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_data_cutter.h"
+
+#include "service_inspectors/http_inspect/http_flow_data.h"
+#include "service_inspectors/http_inspect/http_stream_splitter.h"
+
+#include "http2_dummy_packet.h"
+
+using namespace snort;
+using namespace HttpCommon;
+using namespace Http2Enums;
+
+static std::string create_chunk_hdr(uint32_t len)
+{
+    std::stringstream stream;
+    stream<<std::hex<< len;
+    return stream.str() + "\r\n";
+}
+
+Http2DataCutter::Http2DataCutter(Http2FlowData* _session_data, uint32_t len,
+    HttpCommon::SourceId src_id, bool is_padded) :
+    session_data(_session_data),  source_id(src_id), frame_length(len), data_len(len)
+{
+    data_state = (is_padded) ? PADDING_LENGTH : DATA;
+}
+
+// Scan data frame, extract information needed for http scan.
+// http scan will need the data only, stripped of padding and header.
+bool Http2DataCutter::http2_scan(const uint8_t* data, uint32_t length,
+    uint32_t* flush_offset)
+{
+    *flush_offset = cur_data_offset = cur_data = cur_padding = 0;
+
+    if (frame_bytes_seen == 0)
+    {
+        frame_bytes_seen = cur_data_offset = FRAME_HEADER_LENGTH;
+        length -= FRAME_HEADER_LENGTH;
+        *flush_offset = FRAME_HEADER_LENGTH;
+    }
+
+    uint32_t cur_pos = 0;
+
+    while ((cur_pos < length) && (data_state != FULL_FRAME))
+    {
+        switch (data_state)
+        {
+        case PADDING_LENGTH:
+            padding_len = *(data + cur_data_offset);
+
+            if (data_len <= padding_len)
+            {
+                *session_data->infractions[source_id] += INF_PADDING_LEN;
+                session_data->events[source_id]->create_event(EVENT_PADDING_LEN);
+                return false;
+            }
+            // FIXIT temporary - till multiple data frames sent to http
+            if (data_len == (padding_len + 1))
+                return false;
+            data_len -= (padding_len + 1);
+            data_state = DATA;
+            cur_pos++;
+            cur_data_offset++;
+            break;
+        case DATA:
+          {
+            const uint32_t missing = data_len - data_bytes_read;
+            cur_data = ((length - cur_pos) >= missing) ?
+                missing : (length - cur_pos);
+            data_bytes_read += cur_data;
+            cur_pos += cur_data;
+            if (data_bytes_read == data_len)
+                data_state = padding_len ? PADDING : FULL_FRAME;
+            break;
+          }
+        case PADDING:
+          {
+            const uint32_t missing = padding_len - padding_read;
+            cur_padding = ((length - cur_pos) >= missing) ?
+                missing : (length - cur_pos);
+            cur_pos += cur_padding;
+            padding_read += cur_padding;
+            if (padding_read == padding_len)
+                data_state = FULL_FRAME;
+            break;
+          }
+        default:
+            break;
+        }
+    }
+
+    frame_bytes_seen += cur_pos;
+    session_data->scan_remaining_frame_octets[source_id] = frame_length - frame_bytes_seen;
+    *flush_offset += cur_pos;
+
+    return true;
+}
+
+// Call http scan. Wrap data with chunk header and end of chunk.
+StreamSplitter::Status Http2DataCutter::http_scan(const uint8_t* data, uint32_t* flush_offset)
+{
+    StreamSplitter::Status scan_result = StreamSplitter::SEARCH;
+    uint32_t http_flush_offset = 0;
+    Http2DummyPacket dummy_pkt;
+    dummy_pkt.flow = session_data->flow;
+    uint32_t unused = 0;
+
+    // first phase supports only flush of full packet
+    switch (http_state)
+    {
+    case NONE_SENT:
+      {
+        if (cur_data)
+        {
+            std::string chunk_hdr = create_chunk_hdr(data_len);
+            scan_result = session_data->hi_ss[source_id]->scan(&dummy_pkt,
+                (const unsigned char*)chunk_hdr.c_str(),
+                chunk_hdr.length(), unused, &http_flush_offset);
+            bytes_sent_http += chunk_hdr.length();
+            http_state = HEADER_SENT;
+            if (scan_result != StreamSplitter::SEARCH)
+                return StreamSplitter::ABORT;
+        }
+      }   // fallthrough
+    case HEADER_SENT:
+      {
+        if (cur_data)
+        {
+            scan_result = session_data->hi_ss[source_id]->scan(&dummy_pkt, data + cur_data_offset,
+                cur_data, unused, &http_flush_offset);
+            bytes_sent_http += cur_data;
+
+            if (scan_result != StreamSplitter::SEARCH)
+                return StreamSplitter::ABORT;
+        }
+        if (data_state == FULL_FRAME)
+        {
+            scan_result = session_data->hi_ss[source_id]->scan(&dummy_pkt, (const unsigned
+                char*)"\r\n0\r\n",
+                5, unused, &http_flush_offset);
+            bytes_sent_http +=5;
+            assert(scan_result == StreamSplitter::FLUSH);
+
+            session_data->scan_octets_seen[source_id] = 0;
+            session_data->scan_remaining_frame_octets[source_id] = 0;
+        }
+      }
+    }
+
+    if (scan_result != StreamSplitter::FLUSH)
+        *flush_offset = 0;
+
+    return scan_result;
+}
+
+StreamSplitter::Status Http2DataCutter::scan(const uint8_t* data, uint32_t length,
+    uint32_t* flush_offset)
+{
+    if (!http2_scan(data, length, flush_offset))
+        return StreamSplitter::ABORT;
+
+    return Http2DataCutter::http_scan(data, flush_offset);
+}
+
+const StreamBuffer Http2DataCutter::reassemble(unsigned total, unsigned offset, const
+    uint8_t* data, unsigned len)
+{
+    StreamBuffer frame_buf { nullptr, 0 };
+
+    if (offset == 0)
+    {
+        padding_read = data_bytes_read = hdr_bytes_read = 0;
+    }
+    cur_data = cur_padding = cur_data_offset = 0;
+
+    unsigned cur_pos = 0;
+    while (cur_pos < len)
+    {
+        switch (reassemble_state)
+        {
+        case SKIP_FRAME_HDR:
+          {
+            if (hdr_bytes_read == 0)
+            {
+                session_data->frame_header[source_id] = new uint8_t[FRAME_HEADER_LENGTH];
+                session_data->frame_header_size[source_id] = FRAME_HEADER_LENGTH;
+            }
+            const uint32_t missing = FRAME_HEADER_LENGTH - hdr_bytes_read;
+            const uint32_t cur_frame = ((len - cur_pos) < missing) ? (len - cur_pos) : missing;
+            memcpy(session_data->frame_header[source_id] + hdr_bytes_read, data + cur_pos,
+                cur_frame);
+            hdr_bytes_read += cur_frame;
+            cur_pos += cur_frame;
+            if (hdr_bytes_read == FRAME_HEADER_LENGTH)
+            {
+                cur_data_offset = cur_pos;
+                reassemble_state = (padding_len) ? SKIP_PADDING_LEN : SEND_CHUNK_HDR;
+            }
+
+            break;
+          }
+        case SKIP_PADDING_LEN:
+            cur_pos++;
+            cur_data_offset++;
+            reassemble_state = SEND_CHUNK_HDR;
+            break;
+        case SEND_CHUNK_HDR:
+          {
+            std::string chunk_hdr = create_chunk_hdr(data_len);
+            unsigned copied;
+            session_data->hi_ss[source_id]->reassemble(session_data->flow,
+                bytes_sent_http, 0, (const uint8_t*)chunk_hdr.c_str(), chunk_hdr.length(), 0,
+                copied);
+            assert(copied == (unsigned)chunk_hdr.length());
+            reassemble_state = SEND_DATA;
+          }   // fallthrough
+        case SEND_DATA:
+          {
+            const uint32_t missing = data_len - data_bytes_read;
+            cur_data = ((len - cur_pos) >= missing) ? missing : (len - cur_pos);
+            data_bytes_read += cur_data;
+            cur_pos += cur_data;
+
+            unsigned copied;
+            frame_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow,
+                bytes_sent_http, 0, data + cur_data_offset, cur_data,
+                0, copied);
+            assert(copied == (unsigned)cur_data);
+
+            if (data_bytes_read == data_len)
+                reassemble_state = (padding_len) ? SKIP_PADDING : SEND_CRLF;
+
+            break;
+          }
+        case SKIP_PADDING:
+          {
+            const uint32_t missing = padding_len - padding_read;
+            cur_padding = ((len - cur_pos) >= missing) ?
+                missing : (len - cur_pos);
+            cur_pos += cur_padding;
+            padding_read += cur_padding;
+            if (padding_read == padding_len)
+                reassemble_state = SEND_CRLF;
+            break;
+          }
+
+        default:
+            break;
+        }
+    }
+
+    if (len + offset == total)
+        assert(reassemble_state == SEND_CRLF);
+
+    if (reassemble_state == SEND_CRLF)
+    {
+        unsigned copied;
+        frame_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow,
+            bytes_sent_http, 0,(const unsigned char*)"\r\n0\r\n", 5, PKT_PDU_TAIL, copied);
+        assert(copied == 5);
+
+        assert(frame_buf.data != nullptr);
+        session_data->frame_data[source_id] = const_cast <uint8_t*>(frame_buf.data);
+        session_data->frame_data_size[source_id] = frame_buf.length;
+    }
+
+    return frame_buf;
+}
+
diff --git a/src/service_inspectors/http2_inspect/http2_data_cutter.h b/src/service_inspectors/http2_inspect/http2_data_cutter.h
new file mode 100644 (file)
index 0000000..8755a19
--- /dev/null
@@ -0,0 +1,81 @@
+//--------------------------------------------------------------------------
+// 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_data_cutter.h author Maya Dagon <mdagon@cisco.com>
+
+#ifndef HTTP2_DATA_CUTTER_H
+#define HTTP2_DATA_CUTTER_H
+
+#include "service_inspectors/http_inspect/http_common.h"
+#include "stream/stream_splitter.h"
+
+#include "http2_enum.h"
+#include "http2_flow_data.h"
+
+class Http2DataCutter
+{
+public:
+    Http2DataCutter(Http2FlowData* flow_data, uint32_t len, HttpCommon::SourceId
+                     src_id, bool is_padded);
+    snort::StreamSplitter::Status scan(const uint8_t* data, uint32_t length,
+                                      uint32_t* flush_offset);
+    const snort::StreamBuffer reassemble(unsigned total, unsigned offset, const uint8_t* data,
+        unsigned len);
+
+private:
+
+    Http2FlowData* const session_data;
+    const HttpCommon::SourceId source_id;
+
+    // total per frame
+    const uint32_t frame_length;
+    uint32_t data_len;
+    uint32_t padding_len = 0;
+    // accumulating
+    uint32_t frame_bytes_seen = 0;
+    uint32_t bytes_sent_http = 0;
+    uint32_t hdr_bytes_read = 0;
+    uint32_t data_bytes_read = 0;
+    uint32_t padding_read = 0;
+    // per call
+    uint32_t cur_data;
+    uint32_t cur_padding;
+    uint32_t cur_data_offset;
+
+    //
+    // State machines
+    //
+
+    // data scan
+    enum DataState { PADDING_LENGTH, DATA, PADDING, FULL_FRAME };
+    enum DataState data_state;
+
+    // http scan
+    enum HttpScanState { NONE_SENT, HEADER_SENT };
+    enum HttpScanState http_state = NONE_SENT;
+
+    // reassemble
+    enum ReassembleState { SKIP_FRAME_HDR, SKIP_PADDING_LEN, SEND_CHUNK_HDR, SEND_DATA,
+                           SKIP_PADDING, SEND_CRLF };
+    enum ReassembleState reassemble_state = SKIP_FRAME_HDR;
+
+    bool http2_scan(const uint8_t* data, uint32_t length, uint32_t* flush_offset);
+    snort::StreamSplitter::Status http_scan(const uint8_t* data, uint32_t* flush_offset);
+};
+
+#endif
+
diff --git a/src/service_inspectors/http2_inspect/http2_data_frame.cc b/src/service_inspectors/http2_inspect/http2_data_frame.cc
new file mode 100644 (file)
index 0000000..01a6c96
--- /dev/null
@@ -0,0 +1,58 @@
+//--------------------------------------------------------------------------
+// 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_data_frame.cc author Maya Dagon <mdagon@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_data_frame.h"
+
+#include "protocols/packet.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_flow_data.h"
+
+using namespace HttpCommon;
+using namespace snort;
+
+Http2DataFrame::Http2DataFrame(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, nullptr, 0, session_data_, source_id_)
+{
+    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 = data_len;
+    dummy_pkt.data = data_buffer;
+    dummy_pkt.xtradata_mask = 0;
+    session_data->hi->eval(&dummy_pkt);
+    detection_required = dummy_pkt.is_detection_required();
+    xtradata_mask = dummy_pkt.xtradata_mask;
+}
+
+void Http2DataFrame::clear()
+{
+    Http2DummyPacket dummy_pkt;
+    dummy_pkt.flow = session_data->flow;
+    session_data->hi->clear(&dummy_pkt);
+}
+
diff --git a/src/service_inspectors/http2_inspect/http2_data_frame.h b/src/service_inspectors/http2_inspect/http2_data_frame.h
new file mode 100644 (file)
index 0000000..904f5aa
--- /dev/null
@@ -0,0 +1,45 @@
+//--------------------------------------------------------------------------
+// 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_data_frame.h author Maya Dagon <mdagon@cisco.com>
+
+#ifndef HTTP2_DATA_FRAME_H
+#define HTTP2_DATA_FRAME_H
+
+#include "http2_frame.h"
+
+class Http2Frame;
+
+class Http2DataFrame : public Http2Frame
+{
+public:
+    ~Http2DataFrame() override {}
+    void clear() override;    
+
+    uint32_t get_xtradata_mask() override { return xtradata_mask; }
+    bool is_detection_required() const override { return detection_required; }
+  
+    friend Http2Frame* Http2Frame::new_frame(const uint8_t*, const int32_t, const uint8_t*,
+        const int32_t, Http2FlowData*, HttpCommon::SourceId);
+
+private:
+    Http2DataFrame(const uint8_t* header_buffer, const int32_t header_len,const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* ssn_data,
+        HttpCommon::SourceId src_id);
+    uint32_t xtradata_mask = 0;
+    bool detection_required = false;
+};
+#endif
index fdab3edbf9106df7af146c9c6f1e965f28e35a2e..b042b3d24b1b1225954600b65266218cc829a80f 100644 (file)
@@ -64,6 +64,7 @@ enum EventSid
     EVENT_FRAME_SEQUENCE = 13,
     EVENT_DYNAMIC_TABLE_OVERFLOW = 14,
     EVENT_INVALID_STARTLINE = 15,
+    EVENT_PADDING_LEN = 16,
     EVENT__MAX_VALUE
 };
 
@@ -98,6 +99,7 @@ enum Infraction
     INF_TOO_MANY_TABLE_SIZE_UPDATES = 24,
     INF_INVALID_STARTLINE = 25,
     INF_INVALID_HEADER = 26,
+    INF_PADDING_LEN = 27,
     INF__MAX_VALUE
 };
 
index 51d5e362eb03f5b7d5d39bd10c8b986786ce0699..9627846ebbbdbfe300b8807a708c77a2124d227c 100644 (file)
@@ -87,6 +87,8 @@ Http2FlowData::~Http2FlowData()
         delete infractions[k];
         delete events[k];
         delete hi_ss[k];
+        delete[] frame_data[k];
+        delete[] frame_header[k];
     }
 }
 
index 51891d6cdaf0ae3614d954379ed986f783e483b2..ae7c8b3ce0cba1c6c430872beef40590968a4547 100644 (file)
@@ -62,6 +62,8 @@ public:
     void set_hi_msg_section(HttpMsgSection* section);
 
     friend class Http2Frame;
+    friend class Http2DataFrame;
+    friend class Http2DataCutter;
     friend class Http2HeadersFrame;
     friend class Http2Hpack;
     friend class Http2Inspect;
@@ -71,6 +73,8 @@ public:
     friend class Http2StatusLine;
     friend class Http2Stream;
     friend class Http2StreamSplitter;
+    friend snort::StreamSplitter::Status data_scan(Http2FlowData* session_data, const uint8_t* data,
+       uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id, uint32_t frame_length, bool is_padded);
     friend const snort::StreamBuffer implement_reassemble(Http2FlowData*, unsigned, unsigned,
         const uint8_t*, unsigned, uint32_t, HttpCommon::SourceId);
     friend snort::StreamSplitter::Status implement_scan(Http2FlowData*, const uint8_t*, uint32_t,
index 78837dd56f99d43ba2e8f90d87f584b0554f4326..1b34537d3dc0f2aeb76413e619ce331ffab3b648 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "http2_frame.h"
 
+#include "http2_data_frame.h"
 #include "http2_enum.h"
 #include "http2_flow_data.h"
 #include "http2_headers_frame.h"
@@ -57,6 +58,8 @@ Http2Frame* Http2Frame::new_frame(const uint8_t* header, const int32_t header_le
         case FT_SETTINGS:
             return new Http2SettingsFrame(header, header_len, data, data_len, session_data,
                 source_id);
+        case FT_DATA:
+            return new Http2DataFrame(header, header_len, data, data_len, session_data, source_id);      
         default:
             return new Http2Frame(header, header_len, data, data_len, session_data, source_id);
     }
index 87f84af670a56855a4c5a648260c1da9fa97b228..46254299d2fa8aff7f0d9f645a28ea488f86375b 100644 (file)
@@ -74,7 +74,7 @@ Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t
     start_line = hpack_decoder.get_start_line();
     http1_header = hpack_decoder.get_decoded_headers(decoded_headers);
 
-    if ((error_during_decode) || (session_data->hi_ss[source_id] == nullptr))
+    if (error_during_decode)
         return;
 
     // http_inspect scan() of start line
@@ -181,7 +181,7 @@ Http2HeadersFrame::~Http2HeadersFrame()
 
 void Http2HeadersFrame::clear()
 {
-    if (error_during_decode || hi_abort || (session_data->hi == nullptr))
+    if (error_during_decode || hi_abort)
         return;
     Packet dummy_pkt(false);
     dummy_pkt.flow = session_data->flow;
index 77de26ffdef80c274d0d4f8b37646326a7d6b2f2..e0e4b49eccd12b5c82f0d0ee8f84de9eb06d3bc7 100644 (file)
@@ -120,7 +120,7 @@ void Http2Inspect::eval(Packet* p)
 
     // FIXIT-H Workaround for unexpected eval() calls
     // Avoid eval if scan/reassemble aborts
-    if (session_data->frame_type[source_id] == FT__NONE)
+    if (session_data->frame_type[source_id] == FT__ABORT)
         return;
 
     Http2Stream* stream = session_data->get_current_stream(source_id);
index dffdb222ca7ed13a15aa0083485339fb9c3f864e..bd212718f5a6a1708a38567a57bcf1223a7ef5cd 100644 (file)
@@ -25,6 +25,8 @@
 
 #include "service_inspectors/http_inspect/http_flow_data.h"
 
+#include "http2_data_cutter.h"
+
 using namespace HttpCommon;
 
 Http2Stream::Http2Stream(uint32_t stream_id_, Http2FlowData* session_data_) :
@@ -37,6 +39,8 @@ Http2Stream::~Http2Stream()
 {
     delete current_frame;
     delete hi_flow_data;
+    delete data_cutter[SRC_CLIENT];
+    delete data_cutter[SRC_SERVER];
 }
 
 void Http2Stream::eval_frame(const uint8_t* header_buffer, int32_t header_len,
@@ -70,3 +74,9 @@ void Http2Stream::print_frame(FILE* output)
 }
 #endif
 
+Http2DataCutter* Http2Stream::get_data_cutter(HttpCommon::SourceId source_id, uint32_t len, bool is_padded)
+{
+    if (!data_cutter[source_id])
+        data_cutter[source_id] = new Http2DataCutter(session_data, len, source_id, is_padded);
+    return data_cutter[source_id];
+}
index a267335b37760e7dfa99ae9d939be24470acc6c7..408d6c2f9e81812e7628c8b696a8bbcbb0c401a2 100644 (file)
@@ -25,8 +25,9 @@
 
 #include "http2_frame.h"
 
-class HttpFlowData;
+class Http2DataCutter;
 class Http2FlowData;
+class HttpFlowData;
 class HttpMsgSection;
 
 class Http2Stream
@@ -47,6 +48,11 @@ public:
     uint32_t get_xtradata_mask() { return (current_frame != nullptr) ? 
         current_frame->get_xtradata_mask() : 0; }
     Http2Frame *get_current_frame() { return current_frame; }
+    Http2DataCutter* get_data_cutter(HttpCommon::SourceId source_id, uint32_t len=0, bool is_padded=false);
+    void set_data_cutter(Http2DataCutter* cutter, HttpCommon::SourceId source_id) { data_cutter[source_id] = cutter; }   
+    bool get_abort_data_processing(HttpCommon::SourceId source_id) const { return abort_data_processing[source_id]; }
+    void set_abort_data_processing(HttpCommon::SourceId source_id) { abort_data_processing[source_id] = true; }
+  
 #ifdef REG_TEST
     void print_frame(FILE* output);
 #endif
@@ -57,6 +63,8 @@ private:
     Http2Frame* current_frame = nullptr;
     HttpFlowData* hi_flow_data = nullptr;
     HttpMsgSection* hi_msg_section = nullptr;
+    Http2DataCutter* data_cutter[2] = { nullptr, nullptr};
+    bool abort_data_processing[2] = {false, false};
 };
 
 #endif
index bdf286fff62b75f609ed926d525db6b3aad18408..0d9c71afe9a5f1256c7dfb437ef180198e5dd542 100644 (file)
@@ -58,14 +58,7 @@ StreamSplitter::Status Http2StreamSplitter::scan(Packet* pkt, const uint8_t* dat
         pkt->flow->set_flow_data(session_data = new Http2FlowData(pkt->flow));
         Http2Module::increment_peg_counts(PEG_FLOW);
     }
-
-    // General mechanism to abort using scan
-    if (session_data->frame_type[source_id] == FT__ABORT)
-    {
-        session_data->frame_type[source_id] = FT__NONE;
-        return HttpStreamSplitter::status_value(StreamSplitter::ABORT, true);
-    }
-
+    
 #ifdef REG_TEST
     uint32_t dummy_flush_offset;
 
@@ -95,9 +88,16 @@ StreamSplitter::Status Http2StreamSplitter::scan(Packet* pkt, const uint8_t* dat
     }
 #endif
 
+    // General mechanism to abort using scan
+    if (session_data->frame_type[source_id] == FT__ABORT)
+        return HttpStreamSplitter::status_value(StreamSplitter::ABORT, true);
+    
     const StreamSplitter::Status ret_val =
         implement_scan(session_data, data, length, flush_offset, source_id);
 
+    if (ret_val == StreamSplitter::ABORT)
+        session_data->frame_type[source_id] = FT__ABORT;
+
 #ifdef REG_TEST
     if (HttpTestManager::use_test_input(HttpTestManager::IN_HTTP2))
         if (ret_val == StreamSplitter::FLUSH)
index 6eb937cc421b61f99a3f10e745035cbec0eaf089..100d779dd6933d49a2b0b70bdeaff41796ac5a6a 100644 (file)
 
 #include "service_inspectors/http_inspect/http_common.h"
 #include "service_inspectors/http_inspect/http_flow_data.h"
+#include "service_inspectors/http_inspect/http_stream_splitter.h"
 #include "service_inspectors/http_inspect/http_test_input.h"
 #include "service_inspectors/http_inspect/http_test_manager.h"
 
+#include "http2_data_cutter.h"
 #include "http2_flow_data.h"
 
 using namespace snort;
@@ -48,7 +50,7 @@ static uint8_t get_frame_type(const uint8_t* frame_buffer)
         return frame_buffer[frame_type_index];
     // If there was no frame header, this must be a piece of a long data frame
     else
-       return FT_DATA;
+        return FT_DATA;
 }
 
 static uint8_t get_frame_flags(const uint8_t* frame_buffer)
@@ -65,9 +67,38 @@ static uint8_t get_stream_id(const uint8_t* frame_buffer)
     const uint8_t stream_id_index = 5;
     assert(frame_buffer != nullptr);
     return ((frame_buffer[stream_id_index] & 0x7f) << 24) +
-            (frame_buffer[stream_id_index + 1] << 16) + 
-            (frame_buffer[stream_id_index + 2] << 8) +
-             frame_buffer[stream_id_index + 3];
+           (frame_buffer[stream_id_index + 1] << 16) +
+           (frame_buffer[stream_id_index + 2] << 8) +
+           frame_buffer[stream_id_index + 3];
+}
+
+StreamSplitter::Status data_scan(Http2FlowData* session_data, const uint8_t* data,
+    uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id,
+    uint32_t frame_length, bool is_padded)
+{
+    Http2Stream* const stream = session_data->find_stream(session_data->current_stream[source_id]);
+    HttpFlowData* http_flow = nullptr;
+    if (stream)
+    {
+        http_flow = (HttpFlowData*)stream->get_hi_flow_data();
+        // temporary, till more than 1 data frame sent to http inspect is supported
+        if (stream->get_abort_data_processing(source_id))
+            return StreamSplitter::ABORT;
+    }
+
+    if (!stream || !http_flow || (frame_length > 0 and
+        (http_flow->get_type_expected(source_id) != HttpEnums::SEC_BODY_CHUNK)))
+    {
+        *session_data->infractions[source_id] += INF_FRAME_SEQUENCE;
+        session_data->events[source_id]->create_event(EVENT_FRAME_SEQUENCE);
+        return StreamSplitter::ABORT;
+    }
+
+    if (frame_length == 0 or frame_length > MAX_OCTETS)
+        return StreamSplitter::ABORT;
+
+    Http2DataCutter* data_cutter = stream->get_data_cutter(source_id, frame_length, is_padded);
+    return data_cutter->scan(data, length, flush_offset);
 }
 
 StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t* data,
@@ -80,14 +111,14 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t
         // Verify preface is correct, else generate loss of sync event and abort
         switch (validate_preface(data, length, session_data->scan_octets_seen[source_id]))
         {
-            case V_GOOD:
-                break;
-            case V_BAD:
-                session_data->events[source_id]->create_event(EVENT_PREFACE_MATCH_FAILURE);
-                return StreamSplitter::ABORT;
-            case V_TBD:
-                session_data->scan_octets_seen[source_id] += length;
-                return StreamSplitter::SEARCH;
+        case V_GOOD:
+            break;
+        case V_BAD:
+            session_data->events[source_id]->create_event(EVENT_PREFACE_MATCH_FAILURE);
+            return StreamSplitter::ABORT;
+        case V_TBD:
+            session_data->scan_octets_seen[source_id] += length;
+            return StreamSplitter::SEARCH;
         }
 
         *flush_offset = 24 - session_data->scan_octets_seen[source_id];
@@ -107,8 +138,8 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t
             if (session_data->leftover_data[source_id] > DATA_SECTION_SIZE)
                 session_data->scan_remaining_frame_octets[source_id] = DATA_SECTION_SIZE;
             else
-                 session_data->scan_remaining_frame_octets[source_id] =
-                     session_data->leftover_data[source_id];
+                session_data->scan_remaining_frame_octets[source_id] =
+                    session_data->leftover_data[source_id];
             session_data->total_bytes_in_split[source_id] = 0;
         }
 
@@ -165,25 +196,16 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t
             // We have the full frame header, compute some variables
             const uint32_t frame_length = get_frame_length(session_data->
                 scan_frame_header[source_id]);
-            const uint8_t type = get_frame_type(session_data->scan_frame_header[source_id]);
+            const uint8_t type = session_data->frame_type[source_id] = get_frame_type(
+                session_data->scan_frame_header[source_id]);
+            uint8_t frame_flags = get_frame_flags(session_data->
+                scan_frame_header[source_id]);
             session_data->current_stream[source_id] =
                 get_stream_id(session_data->scan_frame_header[source_id]);
 
             if (type == FT_DATA)
-            {
-                Http2Stream* const stream = session_data->find_stream(session_data->current_stream[source_id]);
-                HttpFlowData* http_flow = nullptr;
-                if (stream)
-                    http_flow = (HttpFlowData*)stream->get_hi_flow_data();
-
-                if (!stream || !http_flow || (frame_length > 0 and
-                    (http_flow->get_type_expected(source_id) != HttpEnums::SEC_BODY_CHUNK)))
-                {
-                     *session_data->infractions[source_id] += INF_FRAME_SEQUENCE;
-                     session_data->events[source_id]->create_event(EVENT_FRAME_SEQUENCE);
-                     status = StreamSplitter::ABORT;
-                }
-            }
+                return data_scan(session_data, data, length, flush_offset, source_id,
+                    frame_length, ((frame_flags & PADDED) !=0));
 
             // Compute frame section length once per frame
             if (session_data->scan_remaining_frame_octets[source_id] == 0)
@@ -195,14 +217,8 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t
                     status = StreamSplitter::ABORT;
                     break;
                 }
-                if ((type == FT_DATA) && (frame_length > DATA_SECTION_SIZE))
-                {
-                    // Break up long data frames into pieces for detection
-                    session_data->scan_remaining_frame_octets[source_id] = DATA_SECTION_SIZE;
-                    session_data->total_bytes_in_split[source_id] = DATA_SECTION_SIZE +
-                        FRAME_HEADER_LENGTH;
-                }
-                else if (frame_length + FRAME_HEADER_LENGTH > MAX_OCTETS)
+
+                if (frame_length + FRAME_HEADER_LENGTH > MAX_OCTETS)
                 {
                     // FIXIT-M long non-data frame needs to be supported
                     status = StreamSplitter::ABORT;
@@ -225,51 +241,49 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t
             }
 
             // Have the full frame
-            uint8_t frame_flags = get_frame_flags(session_data->scan_frame_header[source_id]);
-            switch(type)
+            switch (type)
             {
-                case FT_HEADERS:
+            case FT_HEADERS:
+                if (!(frame_flags & END_HEADERS))
+                {
+                    session_data->continuation_expected[source_id] = true;
+                    status = StreamSplitter::SEARCH;
+                }
+                break;
+            case FT_CONTINUATION:
+                if (session_data->continuation_expected[source_id])
+                {
                     if (!(frame_flags & END_HEADERS))
-                    {
-                        session_data->continuation_expected[source_id] = true;
                         status = StreamSplitter::SEARCH;
-                    }
-                    break;
-                case FT_CONTINUATION:
-                    if (session_data->continuation_expected[source_id])
-                    {
-                        if (!(frame_flags & END_HEADERS))
-                            status = StreamSplitter::SEARCH;
-                        else
-                        {
-                            // continuation frame ending headers
-                            status = StreamSplitter::FLUSH;
-                            session_data->continuation_expected[source_id] = false;
-                        }
-                    }
                     else
                     {
-                        // FIXIT-M CONTINUATION frames can also follow PUSH_PROMISE frames, which
-                        // are not currently supported
-                        *session_data->infractions[source_id] += INF_UNEXPECTED_CONTINUATION;
-                        session_data->events[source_id]->create_event(
-                            EVENT_UNEXPECTED_CONTINUATION);
-                        status = StreamSplitter::ABORT;
+                        // continuation frame ending headers
+                        status = StreamSplitter::FLUSH;
+                        session_data->continuation_expected[source_id] = false;
                     }
-                    break;
-                case FT_DATA:
-                    session_data->leftover_data[source_id] = frame_length -
-                        (session_data->total_bytes_in_split[source_id] - FRAME_HEADER_LENGTH);
-                    break;
+                }
+                else
+                {
+                    // FIXIT-M CONTINUATION frames can also follow PUSH_PROMISE frames, which
+                    // are not currently supported
+                    *session_data->infractions[source_id] += INF_UNEXPECTED_CONTINUATION;
+                    session_data->events[source_id]->create_event(
+                        EVENT_UNEXPECTED_CONTINUATION);
+                    status = StreamSplitter::ABORT;
+                }
+                break;
+            default:
+                break;
             }
 
             data_offset += session_data->scan_remaining_frame_octets[source_id];
             *flush_offset = data_offset;
             session_data->scan_octets_seen[source_id] = 0;
             session_data->scan_remaining_frame_octets[source_id] = 0;
-
-        while (status == StreamSplitter::SEARCH && data_offset < length);
+        }
+        while (status == StreamSplitter::SEARCH && data_offset < length);
     }
+
     return status;
 }
 
@@ -282,103 +296,125 @@ const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned to
     assert(offset+len <= total);
     assert(total >= FRAME_HEADER_LENGTH);
     assert(total <= MAX_OCTETS);
-    assert(total == session_data->total_bytes_in_split[source_id]);
 
     StreamBuffer frame_buf { nullptr, 0 };
 
-    uint32_t data_offset = 0;
-
-    if (offset == 0)
+    if (session_data->frame_type[source_id] == FT_DATA)
     {
-        // This is the first reassemble() for this frame and we need to allocate some buffers
-        session_data->frame_header_size[source_id] = FRAME_HEADER_LENGTH *
-            session_data->num_frame_headers[source_id];
-        if (session_data->frame_header_size[source_id] > 0)
-            session_data->frame_header[source_id] = new uint8_t[
-                session_data->frame_header_size[source_id]];
-
-        session_data->frame_data_size[source_id]= total -
-            session_data->frame_header_size[source_id];
-        if (session_data->frame_data_size[source_id] > 0)
-            session_data->frame_data[source_id] = new uint8_t[
-                session_data->frame_data_size[source_id]];
-
-        session_data->frame_header_offset[source_id] = 0;
-        session_data->frame_data_offset[source_id] = 0;
-        session_data->remaining_frame_octets[source_id] =
-            session_data->octets_before_first_header[source_id];
-        session_data->padding_octets_in_frame[source_id] = 0;
+        Http2Stream* const stream = session_data->find_stream(
+            session_data->current_stream[source_id]);
+        Http2DataCutter* data_cutter = stream->get_data_cutter(source_id);
+        StreamBuffer http_frame_buf = data_cutter->reassemble(total, offset, data, len);
+        if (http_frame_buf.data)
+        {
+            delete data_cutter;
+            stream->set_data_cutter(nullptr, source_id);
+            stream->set_abort_data_processing(source_id);
+            session_data->frame_data[source_id] = const_cast<uint8_t*>(http_frame_buf.data);
+            session_data->frame_data_size[source_id] = http_frame_buf.length;
+        }
     }
-
-    do
+    else
     {
-        uint32_t octets_to_copy;
-
-        // Read the padding length if necessary
-        if (session_data->get_padding_len[source_id])
+        uint32_t data_offset = 0;
+       
+        if (offset == 0)
         {
-            session_data->get_padding_len[source_id] = false;
-            session_data->padding_octets_in_frame[source_id] = *(data + data_offset);
-            data_offset += 1;
-            session_data->remaining_frame_octets[source_id] -= 1;
-            // Subtract the padding and padding length from the frame data size
-            session_data->frame_data_size[source_id] -=
-                (session_data->padding_octets_in_frame[source_id] + 1);
+            // This is the first reassemble() for this frame and we need to allocate some buffers
+            session_data->frame_header_size[source_id] = FRAME_HEADER_LENGTH *
+                session_data->num_frame_headers[source_id];
+            if (session_data->frame_header_size[source_id] > 0)
+                session_data->frame_header[source_id] = new uint8_t[
+                    session_data->frame_header_size[source_id]];
+
+            session_data->frame_data_size[source_id]= total -
+                session_data->frame_header_size[source_id];
+            if (session_data->frame_data_size[source_id] > 0)
+                session_data->frame_data[source_id] = new uint8_t[
+                    session_data->frame_data_size[source_id]];
+
+            session_data->frame_header_offset[source_id] = 0;
+            session_data->frame_data_offset[source_id] = 0;
+            session_data->remaining_frame_octets[source_id] =
+                session_data->octets_before_first_header[source_id];
+            session_data->padding_octets_in_frame[source_id] = 0;
         }
 
-        // Copy data into the frame buffer until we run out of data or reach the end of the current
-        // frame's data
-        const uint32_t remaining_frame_payload =
-            session_data->remaining_frame_octets[source_id] -
-            session_data->padding_octets_in_frame[source_id];
-        octets_to_copy = remaining_frame_payload > len - data_offset ? len - data_offset :
-            remaining_frame_payload;
-        if (octets_to_copy > 0)
+        do
         {
-            memcpy(session_data->frame_data[source_id] + session_data->frame_data_offset[source_id],
-                data + data_offset, octets_to_copy);
-        }
-        session_data->frame_data_offset[source_id] += octets_to_copy;
-        session_data->remaining_frame_octets[source_id] -= octets_to_copy;
-        data_offset += octets_to_copy;
+            uint32_t octets_to_copy;
 
-        if (data_offset == len)
-            break;
+            // Read the padding length if necessary
+            if (session_data->get_padding_len[source_id])
+            {
+                session_data->get_padding_len[source_id] = false;
+                session_data->padding_octets_in_frame[source_id] = *(data + data_offset);
+                data_offset += 1;
+                session_data->remaining_frame_octets[source_id] -= 1;
+                // Subtract the padding and padding length from the frame data size
+                session_data->frame_data_size[source_id] -=
+                    (session_data->padding_octets_in_frame[source_id] + 1);
+            }
 
-        // Skip over any padding
-        uint32_t padding_bytes_to_skip = session_data->padding_octets_in_frame[source_id] >
-            len - data_offset ? len - data_offset :
-            session_data->padding_octets_in_frame[source_id];
-        session_data->remaining_frame_octets[source_id] -= padding_bytes_to_skip;
-        data_offset += padding_bytes_to_skip;
+            // Copy data into the frame buffer until we run out of data or reach the end of the
+            // current
+            // frame's data
+            const uint32_t remaining_frame_payload =
+                session_data->remaining_frame_octets[source_id] -
+                session_data->padding_octets_in_frame[source_id];
+            octets_to_copy = remaining_frame_payload > len - data_offset ? len - data_offset :
+                remaining_frame_payload;
+            if (octets_to_copy > 0)
+            {
+                memcpy(session_data->frame_data[source_id] +
+                    session_data->frame_data_offset[source_id],
+                    data + data_offset, octets_to_copy);
+            }
+            session_data->frame_data_offset[source_id] += octets_to_copy;
+            session_data->remaining_frame_octets[source_id] -= octets_to_copy;
+            data_offset += octets_to_copy;
 
-        if (data_offset == len)
-            break;
+            if (data_offset == len)
+                break;
 
-        // Copy headers
-        const uint32_t remaining_frame_header =  FRAME_HEADER_LENGTH -
-            (session_data->frame_header_offset[source_id] % FRAME_HEADER_LENGTH);
-        octets_to_copy = remaining_frame_header > len - data_offset ? len - data_offset :
-            remaining_frame_header;
-        memcpy(session_data->frame_header[source_id] + session_data->frame_header_offset[source_id],
-            data + data_offset, octets_to_copy);
-        session_data->frame_header_offset[source_id] += octets_to_copy;
-        data_offset += octets_to_copy;
-
-        if (session_data->frame_header_offset[source_id] % FRAME_HEADER_LENGTH != 0)
-            break;
+            // Skip over any padding
+            uint32_t padding_bytes_to_skip = session_data->padding_octets_in_frame[source_id] >
+                len - data_offset ? len - data_offset :
+                session_data->padding_octets_in_frame[source_id];
+            session_data->remaining_frame_octets[source_id] -= padding_bytes_to_skip;
+            data_offset += padding_bytes_to_skip;
 
-        // If we just finished copying a header, parse and update frame variables
-        session_data->remaining_frame_octets[source_id] =
-            get_frame_length(session_data->frame_header[source_id] +
-            session_data->frame_header_offset[source_id] - FRAME_HEADER_LENGTH);
+            if (data_offset == len)
+                break;
 
-        uint8_t frame_flags = get_frame_flags(session_data->frame_header[source_id] +
-            session_data->frame_header_offset[source_id] - FRAME_HEADER_LENGTH);
-        if (frame_flags & PADDED)
-            session_data->get_padding_len[source_id] = true;
-    } while (data_offset < len);
+            // Copy headers
+            const uint32_t remaining_frame_header =  FRAME_HEADER_LENGTH -
+                (session_data->frame_header_offset[source_id] % FRAME_HEADER_LENGTH);
+            octets_to_copy = remaining_frame_header > len - data_offset ? len - data_offset :
+                remaining_frame_header;
+            memcpy(session_data->frame_header[source_id] +
+                session_data->frame_header_offset[source_id],
+                data + data_offset, octets_to_copy);
+            session_data->frame_header_offset[source_id] += octets_to_copy;
+            data_offset += octets_to_copy;
+
+            if (session_data->frame_header_offset[source_id] % FRAME_HEADER_LENGTH != 0)
+                break;
+
+            // If we just finished copying a header, parse and update frame variables
+            session_data->remaining_frame_octets[source_id] =
+                get_frame_length(session_data->frame_header[source_id] +
+                session_data->frame_header_offset[source_id] - FRAME_HEADER_LENGTH);
 
+            uint8_t frame_flags = get_frame_flags(session_data->
+                scan_frame_header[source_id]);
+            if (frame_flags & PADDED)
+                session_data->get_padding_len[source_id] = true;
+        }
+        while (data_offset < len);
+        session_data->frame_type[source_id] = get_frame_type(
+            session_data->frame_header[source_id]);
+    }
     if (flags & PKT_PDU_TAIL)
     {
         session_data->total_bytes_in_split[source_id] = 0;
@@ -389,7 +425,6 @@ const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned to
         // create pkt_data buffer
         frame_buf.data = (const uint8_t*)"";
     }
-    session_data->frame_type[source_id] = get_frame_type(session_data->frame_header[source_id]);
 
     return frame_buf;
 }
@@ -399,8 +434,8 @@ ValidationResult validate_preface(const uint8_t* data, const uint32_t length,
 {
     const uint32_t preface_length = 24;
 
-    static const uint8_t connection_prefix[] = {'P', 'R', 'I', ' ', '*', ' ', 'H', 'T', 'T', 'P',
-        '/', '2', '.', '0', '\r', '\n', '\r', '\n', 'S', 'M', '\r', '\n', '\r', '\n'};
+    static const uint8_t connection_prefix[] = { 'P', 'R', 'I', ' ', '*', ' ', 'H', 'T', 'T', 'P',
+        '/', '2', '.', '0', '\r', '\n', '\r', '\n', 'S', 'M', '\r', '\n', '\r', '\n' };
 
     assert(octets_seen < preface_length);
 
@@ -415,3 +450,4 @@ ValidationResult validate_preface(const uint8_t* data, const uint32_t length,
 
     return V_GOOD;
 }
+
index 71557351e57bb826df9047a74fd63f8e1a23f94c..b558c107cb8d6905dc88c1ebd01133c48a71f3c4 100644 (file)
@@ -46,6 +46,7 @@ const RuleMap Http2Module::http2_events[] =
     { EVENT_FRAME_SEQUENCE, "invalid HTTP/2 frame sequence" },
     { EVENT_DYNAMIC_TABLE_OVERFLOW, "HTTP/2 dynamic table size limit exceeded" },
     { EVENT_INVALID_STARTLINE, "invalid HTTP/2 start line" },
+    { EVENT_PADDING_LEN, "HTTP/2 padding length is bigger than frame data size" },
     { 0, nullptr }
 };
 
index 957ac9343a84f2c6748ab2d181a80fd5f27fea3e..8eef40b38fbfdcb4f695d35b3e0c66378dcef05a 100644 (file)
@@ -81,46 +81,6 @@ IpsOption::EvalStatus Http2IpsOption::eval(Cursor& c, Packet* p)
     return MATCH;
 }
 
-//-------------------------------------------------------------------------
-// http2_frame_data
-//-------------------------------------------------------------------------
-
-#undef IPS_OPT
-#define IPS_OPT "http2_frame_data"
-#undef IPS_HELP
-#define IPS_HELP "rule option to set detection cursor to the HTTP/2 frame body"
-
-static Module* frame_data_mod_ctor()
-{
-    return new Http2CursorModule(IPS_OPT, IPS_HELP, HTTP2_BUFFER_FRAME_DATA, CAT_SET_OTHER,
-        PSI_FRAME_DATA);
-}
-
-static const IpsApi frame_data_api =
-{
-    {
-        PT_IPS_OPTION,
-        sizeof(IpsApi),
-        IPSAPI_VERSION,
-        1,
-        API_RESERVED,
-        API_OPTIONS,
-        IPS_OPT,
-        IPS_HELP,
-        frame_data_mod_ctor,
-        Http2CursorModule::mod_dtor
-    },
-    OPT_TYPE_DETECTION,
-    0, PROTO_BIT__TCP,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    Http2IpsOption::opt_ctor,
-    Http2IpsOption::opt_dtor,
-    nullptr
-};
-
 //-------------------------------------------------------------------------
 // http2_frame_header
 //-------------------------------------------------------------------------
@@ -205,7 +165,6 @@ static const IpsApi decoded_header_api =
 // plugins
 //-------------------------------------------------------------------------
 
-const BaseApi* ips_http2_frame_data = &frame_data_api.base;
 const BaseApi* ips_http2_frame_header = &frame_header_api.base;
 const BaseApi* ips_http2_decoded_header = &decoded_header_api.base;