]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #1856 in SNORT/snort3 from ~DERAMADA/snort3:h2i_settings_frame...
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Thu, 5 Dec 2019 21:16:40 +0000 (21:16 +0000)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Thu, 5 Dec 2019 21:16:40 +0000 (21:16 +0000)
Squashed commit of the following:

commit eb9c908e03f795681f2d82e92eaee6d8d17d5759
Author: deramada <deramada@cisco.com>
Date:   Thu Nov 21 14:50:57 2019 -0500

    http2: parse settings frames

src/service_inspectors/http2_inspect/CMakeLists.txt
src/service_inspectors/http2_inspect/http2_enum.h
src/service_inspectors/http2_inspect/http2_flow_data.h
src/service_inspectors/http2_inspect/http2_frame.cc
src/service_inspectors/http2_inspect/http2_frame.h
src/service_inspectors/http2_inspect/http2_settings_frame.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_settings_frame.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_tables.cc

index db5d9f0608c0b91ab57e32665bcf8b7101dc6ddf..a1ddd7c8f75f30217a13b53e4edea3a3397c3aa1 100644 (file)
@@ -28,6 +28,8 @@ set (FILE_LIST
     http2_module.h
     http2_request_line.cc
     http2_request_line.h
+    http2_settings_frame.cc
+    http2_settings_frame.h
     http2_start_line.cc
     http2_start_line.h
     http2_status_line.cc
index f7e03ede1c85b2ebad67c5eae1526fc894f0d482..c7e0166bd976ae12d46e090b86cf83150e90500c 100644 (file)
@@ -55,9 +55,11 @@ enum EventSid
     EVENT_UNEXPECTED_CONTINUATION = 5,
     EVENT_MISFORMATTED_HTTP2 = 6,
     EVENT_PREFACE_MATCH_FAILURE = 7,
-    EVENT_REQUEST_WITHOUT_REQUIRED_FIELD = 9,
-    EVENT_RESPONSE_WITHOUT_STATUS = 10,
-    EVENT_INVALID_HEADER = 11,
+    EVENT_REQUEST_WITHOUT_REQUIRED_FIELD = 8,
+    EVENT_RESPONSE_WITHOUT_STATUS = 9,
+    EVENT_INVALID_HEADER = 10,
+    EVENT_SETTINGS_FRAME_ERROR = 11,
+    EVENT_SETTINGS_FRAME_UNKN_PARAM = 12,
     EVENT__MAX_VALUE
 };
 
@@ -83,6 +85,8 @@ enum Infraction
     INF_PSEUDO_HEADER_URI_FORM_MISMATCH = 15,
     INF_RESPONSE_WITHOUT_STATUS = 16,
     INF_HPACK_INDEX_OUT_OF_BOUNDS = 17,
+    INF_INVALID_SETTINGS_FRAME = 18,
+    INF_SETTINGS_FRAME_UNKN_PARAM = 19,
     INF__MAX_VALUE
 };
 
@@ -106,6 +110,16 @@ enum PseudoHeaders
     STATUS = 14,
 };
 
+enum SettingsFrameIds
+{
+    HEADER_TABLE_SIZE = 1,
+    ENABLE_PUSH, 
+    MAX_CONCURRENT_STREAMS,
+    INITIAL_WINDOW_SIZE,
+    MAX_FRAME_SIZE,
+    MAX_HEADER_LIST_SIZE,
+};
+
 } // end namespace Http2Enums
 
 #endif
index 8a0aa94eeb3faae40cab6766940dd8d5b2c7b83c..671956df2443e8a0148c14bd3c31f8daeea1c80b 100644 (file)
@@ -34,6 +34,7 @@
 #include "http2_hpack.h"
 #include "http2_hpack_int_decode.h"
 #include "http2_hpack_string_decode.h"
+#include "http2_settings_frame.h"
 
 using Http2Infractions = Infractions<Http2Enums::INF__MAX_VALUE, Http2Enums::INF__NONE>;
 
@@ -53,6 +54,7 @@ public:
     friend class Http2Hpack;
     friend class Http2Inspect;
     friend class Http2RequestLine;
+    friend class Http2SettingsFrame;
     friend class Http2StartLine;
     friend class Http2StatusLine;
     friend class Http2StreamSplitter;
@@ -79,6 +81,7 @@ protected:
     // Used in eval()
     bool frame_in_detection = false;
     class Http2Frame* current_frame[2] = { nullptr, nullptr };
+    Http2ConnectionSettings connection_settings[2];
     Http2HpackDecoder hpack_decoder[2];
 
     // Internal to scan()
index aeca879885e8e271019a687997c63ce50f3c8ecf..5f69a93d67646c4b15f3b30342a2f9e200ef4c11 100644 (file)
@@ -27,6 +27,7 @@
 #include "http2_enum.h"
 #include "http2_flow_data.h"
 #include "http2_headers_frame.h"
+#include "http2_settings_frame.h"
 #include "service_inspectors/http_inspect/http_field.h"
 
 using namespace HttpCommon;
@@ -56,6 +57,10 @@ Http2Frame* Http2Frame::new_frame(const uint8_t* header, const int32_t header_le
             return new Http2HeadersFrame(header, header_len, data, data_len, session_data,
                 source_id);
             break;
+        case FT_SETTINGS:
+            return new Http2SettingsFrame(header, header_len, data, data_len, session_data,
+                source_id);
+            break;
         default:
             return new Http2Frame(header, header_len, data, data_len, session_data, source_id);
             break;
@@ -83,6 +88,18 @@ uint8_t Http2Frame::get_flags()
         return 0;
 }
 
+uint32_t Http2Frame::get_stream_id()
+{
+    if (header.length() <= 0)
+        return INVALID_STREAM_ID;
+
+    const uint8_t* header_start = header.start();
+    return ((header_start[stream_id_index] & 0x7f) << 24) +
+        (header_start[stream_id_index + 1] << 16) + 
+        (header_start[stream_id_index + 2] << 8) +
+        header_start[stream_id_index + 3];
+}
+
 #ifdef REG_TEST
 void Http2Frame::print_frame(FILE* output)
 {
index dc33efb16a88beda3ad63da6cd78763133565c73..e0d3a565e996c59fed51ceef13e9fad2a8981255 100644 (file)
@@ -51,6 +51,7 @@ protected:
         const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
         HttpCommon::SourceId source_id);
     uint8_t get_flags();
+    uint32_t get_stream_id();
 
     Field header;
     Field data;
@@ -58,5 +59,7 @@ protected:
     HttpCommon::SourceId source_id;
 
     const static uint8_t flags_index = 4;
+    const static uint8_t stream_id_index = 5; 
+    const static uint32_t INVALID_STREAM_ID = 0xFFFFFFFF;
 };
 #endif
diff --git a/src/service_inspectors/http2_inspect/http2_settings_frame.cc b/src/service_inspectors/http2_inspect/http2_settings_frame.cc
new file mode 100644 (file)
index 0000000..b7a9a16
--- /dev/null
@@ -0,0 +1,135 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_settings_frame.cc author Deepak Ramadass <deramada@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_settings_frame.h"
+
+#include "service_inspectors/http_inspect/http_test_manager.h"
+
+#include "http2_enum.h"
+#include "http2_flow_data.h"
+
+using namespace Http2Enums;
+
+static const uint8_t SfSize = 6;
+
+static uint16_t get_parameter_id(const uint8_t* data_buffer)
+{
+    return (data_buffer[0] << 8) + data_buffer[1];
+}
+
+static uint32_t get_parameter_value(const uint8_t* data_buffer)
+{      
+    static const uint8_t frame_value_index = 2;
+    return (data_buffer[frame_value_index]  << 24) +
+        (data_buffer[frame_value_index + 1] << 16) +
+        (data_buffer[frame_value_index + 2] << 8) +
+        data_buffer[frame_value_index + 3];
+}
+
+Http2SettingsFrame::Http2SettingsFrame(const uint8_t* header_buffer, const int32_t header_len,
+       const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
+    HttpCommon::SourceId source_id) : Http2Frame(header_buffer, header_len, data_buffer, data_len,
+    session_data, source_id)
+{
+    if (!sanity_check())
+    {
+        session_data->events[source_id]->create_event(EVENT_SETTINGS_FRAME_ERROR);
+        *session_data->infractions[source_id] += INF_INVALID_SETTINGS_FRAME;
+        return;
+    }
+
+    if (SfAck & get_flags())
+        return;
+    
+    parse_settings_frame();
+}
+
+void Http2SettingsFrame::parse_settings_frame()
+{
+       int32_t data_pos = 0;
+
+    while (data_pos < data.length())
+    {
+        uint16_t parameter_id = get_parameter_id(data.start() + data_pos);
+        uint32_t parameter_value = get_parameter_value(data.start() + data_pos);
+
+        data_pos += SfSize;
+
+        if (parameter_id < HEADER_TABLE_SIZE or parameter_id > MAX_HEADER_LIST_SIZE)
+        {
+            session_data->events[source_id]->create_event(EVENT_SETTINGS_FRAME_UNKN_PARAM);
+            *session_data->infractions[source_id] += INF_SETTINGS_FRAME_UNKN_PARAM;
+            continue;
+        }
+
+        session_data->connection_settings[source_id].set_param(parameter_id, parameter_value);  
+    }
+}
+
+bool Http2SettingsFrame::sanity_check()
+{
+    const bool ack = SfAck & get_flags();
+
+    if (get_stream_id() != 0)
+        bad_frame = true;
+    else if (!ack and ((data.length() % 6) != 0))
+        bad_frame = true;
+    else if (ack and data.length() > 0)
+        bad_frame = true;
+    
+    return !(bad_frame);
+}
+
+#ifdef REG_TEST
+void Http2SettingsFrame::print_frame(FILE* output)
+{
+    fprintf(output, "SETTINGS frame:");
+
+    if (bad_frame)
+        fprintf(output, " Error in settings frame.");
+    else if (SfAck & get_flags())
+        fprintf(output, " ACK");
+    else
+        fprintf(output, " Parameters in current frame - %d.", (data.length()/6)) ;
+
+    fprintf(output, "\n");
+    Http2Frame::print_frame(output);
+    fprintf(output, "\n");
+}
+#endif
+
+uint32_t Http2ConnectionSettings::get_param(uint16_t id) 
+{ 
+    assert(id >= HEADER_TABLE_SIZE);
+    assert(id <= MAX_HEADER_LIST_SIZE);
+
+    return parameters[id - 1]; 
+}
+
+void Http2ConnectionSettings::set_param(uint16_t id, uint32_t value) 
+{ 
+    assert(id >= HEADER_TABLE_SIZE);
+    assert(id <= MAX_HEADER_LIST_SIZE);
+
+    parameters[id - 1] = value; 
+}
diff --git a/src/service_inspectors/http2_inspect/http2_settings_frame.h b/src/service_inspectors/http2_inspect/http2_settings_frame.h
new file mode 100644 (file)
index 0000000..a5d9e41
--- /dev/null
@@ -0,0 +1,71 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_settings_frame.cc author Deepak Ramadass <deramada@cisco.com>
+
+#ifndef HTTP2_SETTINGS_FRAME_H
+#define HTTP2_SETTINGS_FRAME_H
+
+#include "http2_frame.h"
+
+class Field;
+class Http2ConnectionSettings;
+class Http2Frame;
+
+class Http2SettingsFrame : public Http2Frame
+{
+public:
+    friend Http2Frame* Http2Frame::new_frame(const uint8_t* header_buffer, const int32_t header_len,
+        const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
+        HttpCommon::SourceId source_id);
+
+#ifdef REG_TEST
+    void print_frame(FILE* output) override;
+#endif
+
+private:
+    Http2SettingsFrame(const uint8_t* header_buffer, const int32_t header_len,
+        const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
+        HttpCommon::SourceId source_id);
+
+    void parse_settings_frame();
+    bool sanity_check();
+
+    bool bad_frame = false;
+    static const uint8_t SfAck = 0x01;
+};
+
+class Http2ConnectionSettings
+{
+public:
+       uint32_t get_param(uint16_t id);
+       void set_param(uint16_t id, uint32_t value);
+
+private:
+    void validate_param_id(uint16_t id);
+
+    static const uint16_t PARAMETER_COUNT = 6; 
+    uint32_t parameters[PARAMETER_COUNT] = {
+             4096, // Header table size
+                1, // Push promise
+              100, // Max concurrent Streams
+            65535, // Window size
+            16384, // Max frame size
+       4294967295 // Max header list size
+    };
+};
+#endif
index 8ba1c37c417b6232e319383de0166db7dad4a592..bf270adb9877619616c9355098284c2f0a112d1d 100644 (file)
@@ -41,6 +41,8 @@ const RuleMap Http2Module::http2_events[] =
     { EVENT_REQUEST_WITHOUT_REQUIRED_FIELD, "HTTP/2 request missing required header field" },
     { EVENT_RESPONSE_WITHOUT_STATUS, "HTTP/2 response has no status code" },
     { EVENT_INVALID_HEADER, "invalid HTTP/2 header field" },
+    { EVENT_SETTINGS_FRAME_ERROR, "error in HTTP/2 settings frame" },
+    { EVENT_SETTINGS_FRAME_UNKN_PARAM, "unknown parameter in HTTP/2 settings frame" },
     { 0, nullptr }
 };