]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2332 in SNORT/snort3 from ~MDAGON/snort3:inj_h2i to master
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Tue, 28 Jul 2020 14:33:32 +0000 (14:33 +0000)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Tue, 28 Jul 2020 14:33:32 +0000 (14:33 +0000)
Squashed commit of the following:

commit 01d5bf727cb31e8c6ed1e6670a097909ec90d903
Author: mdagon <mdagon@cisco.com>
Date:   Mon Jul 6 13:56:57 2020 -0400

    payload_injector: extend utility to support http2 (no injection)

src/packet_io/active.cc
src/payload_injector/payload_injector_module.cc
src/payload_injector/payload_injector_module.h
src/payload_injector/test/payload_injector_test.cc

index 14aab9a5ec87b8ab8f999596ca5f84c27b0924de..51ae3bf2fc46be397a70e71ee0dbfe4162fe973e 100644 (file)
@@ -47,7 +47,7 @@ using namespace snort;
 class ResetAction : public snort::ActiveAction
 {
 public:
-    ResetAction() : ActiveAction(ActionType::ACT_RESET) {}
+    ResetAction() : ActiveAction(ActionType::ACT_RESET) { }
 
     void exec(snort::Packet* p) override
     {
@@ -295,7 +295,8 @@ uint32_t Active::send_data(
         if ( use_direct_inject )
         {
             DIOCTL_DirectInjectReset msg =
-                { p->daq_msg, (uint8_t)((tmp_flags & ENC_FLAG_FWD) ? DAQ_DIR_FORWARD : DAQ_DIR_REVERSE) };
+                { p->daq_msg, (uint8_t)((tmp_flags & ENC_FLAG_FWD) ? DAQ_DIR_FORWARD :
+                DAQ_DIR_REVERSE) };
             ret = p->daq_instance->ioctl(DIOCTL_DIRECT_INJECT_RESET,
                 &msg, sizeof(msg));
             if ( ret != DAQ_SUCCESS )
@@ -327,61 +328,68 @@ uint32_t Active::send_data(
 
     uint32_t sent = 0;
 
-    // Inject the payload.
-    if ( use_direct_inject )
+    if (buf != nullptr)
     {
-        flags = (flags & ~ENC_FLAG_VAL);
-        const DAQ_DIPayloadSegment segments[] = { {buf, blen} };
-        const DAQ_DIPayloadSegment* payload[] = { &segments[0] };
-        DIOCTL_DirectInjectPayload msg = { p->daq_msg,  payload, 1,
-            (uint8_t)((flags & ENC_FLAG_FWD) ? DAQ_DIR_FORWARD : DAQ_DIR_REVERSE) };
-        ret = p->daq_instance->ioctl(DIOCTL_DIRECT_INJECT_PAYLOAD,
-            &msg, sizeof(msg));
-        if ( ret != DAQ_SUCCESS )
+        // Inject the payload.
+        if ( use_direct_inject )
         {
-            active_counts.failed_direct_injects++;
-            return 0;
-        }
-
-        sent = blen;
-        active_counts.direct_injects++;
-    }
-    else
-    {
-        const uint16_t maxPayload = PacketManager::encode_get_max_payload(p);
+            flags = (flags & ~ENC_FLAG_VAL);
+            const DAQ_DIPayloadSegment segments[] = {
+                { buf, blen }
+            };
+            const DAQ_DIPayloadSegment* payload[] = { &segments[0] };
+            DIOCTL_DirectInjectPayload msg = { p->daq_msg,  payload, 1,
+                                               (uint8_t)((flags & ENC_FLAG_FWD) ? DAQ_DIR_FORWARD :
+                                               DAQ_DIR_REVERSE) };
+            ret = p->daq_instance->ioctl(DIOCTL_DIRECT_INJECT_PAYLOAD,
+                &msg, sizeof(msg));
+            if ( ret != DAQ_SUCCESS )
+            {
+                active_counts.failed_direct_injects++;
+                return 0;
+            }
 
-        if (maxPayload)
+            sent = blen;
+            active_counts.direct_injects++;
+        }
+        else
         {
-            uint32_t toSend;
-            do
-            {
-                plen = 0;
-                flags = (flags & ~ENC_FLAG_VAL) | sent;
-                toSend = blen > maxPayload ? maxPayload : blen;
-                seg = PacketManager::encode_response(TcpResponse::PUSH, flags, p, plen, buf, toSend);
+            const uint16_t maxPayload = PacketManager::encode_get_max_payload(p);
 
-                if ( !seg )
+            if (maxPayload)
+            {
+                uint32_t toSend;
+                do
                 {
-                    active_counts.failed_injects++;
-                    return sent;
+                    plen = 0;
+                    flags = (flags & ~ENC_FLAG_VAL) | sent;
+                    toSend = blen > maxPayload ? maxPayload : blen;
+                    seg = PacketManager::encode_response(TcpResponse::PUSH, flags, p, plen, buf,
+                        toSend);
+
+                    if ( !seg )
+                    {
+                        active_counts.failed_injects++;
+                        return sent;
+                    }
+
+                    ret = s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen);
+                    if ( ret )
+                        active_counts.failed_injects++;
+                    else
+                        active_counts.injects++;
+
+                    sent += toSend;
+                    buf += toSend;
                 }
-
-                ret = s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen);
-                if ( ret )
-                    active_counts.failed_injects++;
-                else
-                    active_counts.injects++;
-
-                sent += toSend;
-                buf += toSend;
+                while (blen -= toSend);
             }
-            while (blen -= toSend);
         }
     }
 
     // FIXIT-L: Currently there is no support for injecting a FIN via
     // direct injection.
-    if ( ! use_direct_inject )
+    if ( !use_direct_inject )
     {
         plen = 0;
         flags = (flags & ~ENC_FLAG_VAL) | sent;
@@ -596,7 +604,8 @@ bool Active::hold_packet(const Packet* p)
         return false;
 
     // FIXIT-L same semi-arbitrary heuristic as the retry queue logic - reevaluate later
-    if (!p->daq_instance || p->daq_instance->get_pool_available() < p->daq_instance->get_batch_size())
+    if (!p->daq_instance || p->daq_instance->get_pool_available() <
+        p->daq_instance->get_batch_size())
     {
         active_counts.holds_denied++;
         return false;
@@ -715,7 +724,7 @@ void Active::apply_delayed_action(Packet* p)
         reset_session(p, delayed_reject, force);
         break;
     case ACT_RETRY:
-        if(!retry_packet(p))
+        if (!retry_packet(p))
             drop_packet(p, force);
         break;
     default:
@@ -725,7 +734,6 @@ void Active::apply_delayed_action(Packet* p)
     delayed_active_action = ACT_ALLOW;
 }
 
-
 //--------------------------------------------------------------------
 
 bool Active::open(const char* dev)
@@ -823,3 +831,4 @@ void Active::send_reason_to_daq(Packet& p)
     if ( reason != -1 )
         p.daq_instance->set_packet_verdict_reason(p.daq_msg, reason);
 }
+
index 37f6f0698c305a05adaa3d95a230b132b72d3a92..919df8ffc3085a58c899bc297dec462e37449898 100644 (file)
 #include "packet_io/active.h"
 #include "protocols/packet.h"
 
-#ifdef UNIT_TEST
-#include "catch/snort_catch.h"
-#endif
-
 #define s_name "payload_injector"
 #define s_help \
     "payload injection utility"
@@ -43,6 +39,7 @@ THREAD_LOCAL PayloadInjectorCounts payload_injector_stats;
 const PegInfo payload_injector_pegs[] =
 {
     { CountType::SUM, "http_injects", "total number of http injections" },
+    { CountType::SUM, "http2_injects", "total number of http2 injections" },
     { CountType::END, nullptr, nullptr }
 };
 
@@ -64,7 +61,8 @@ bool PayloadInjectorModule::end(const char*, int, SnortConfig*)
     return true;
 }
 
-InjectionReturnStatus PayloadInjectorModule::inject_http_payload(Packet* p, InjectionControl& control)
+InjectionReturnStatus PayloadInjectorModule::inject_http_payload(Packet* p,
+    InjectionControl& control)
 {
     InjectionReturnStatus status = INJECTION_SUCCESS;
 
@@ -77,8 +75,26 @@ InjectionReturnStatus PayloadInjectorModule::inject_http_payload(Packet* p, Inje
 
         if (p->packet_flags & PKT_STREAM_EST)
         {
-            payload_injector_stats.http_injects++;
-            p->active->send_data(p, df, control.http_page, control.http_page_len);
+            if (!p->flow || !p->flow->gadget)
+                status = ERR_UNIDENTIFIED_PROTOCOL;
+            else if (strcmp(p->flow->gadget->get_name(),"http_inspect") == 0)
+            {
+                payload_injector_stats.http_injects++;
+                p->active->send_data(p, df, control.http_page, control.http_page_len);
+            }
+            else if (strcmp(p->flow->gadget->get_name(),"http2_inspect") == 0)
+            {
+                if (control.stream_id != 0)
+                {
+                    payload_injector_stats.http2_injects++;
+                    // FIXIT-E translate page, inject payload
+                    p->active->send_data(p, df, nullptr, 0);
+                }
+                else
+                    status = ERR_HTTP2_STREAM_ID_0;
+            }
+           else
+               status = ERR_UNIDENTIFIED_PROTOCOL;
         }
         else
             status = ERR_STREAM_NOT_ESTABLISHED;
index a196545a53c25fcacecb8e9c81924fb5bfcf861d..67717ede3b723895566837ca9875dd0cc0267fa4 100644 (file)
@@ -31,6 +31,7 @@ struct Packet;
 struct PayloadInjectorCounts
 {
     PegCount http_injects;
+    PegCount http2_injects;
 };
 
 extern THREAD_LOCAL PayloadInjectorCounts payload_injection_stats;
@@ -40,12 +41,15 @@ enum InjectionReturnStatus : int8_t
     INJECTION_SUCCESS = 1,
     ERR_INJECTOR_NOT_CONFIGURED = -1,
     ERR_STREAM_NOT_ESTABLISHED = -2,
+    ERR_HTTP2_STREAM_ID_0 = -3,
+    ERR_UNIDENTIFIED_PROTOCOL = -4,
 };
 
 struct InjectionControl
 {
     const uint8_t* http_page = nullptr;
     uint32_t http_page_len = 0;
+    uint32_t stream_id = 0;
 };
 
 class SO_PUBLIC PayloadInjectorModule : public snort::Module
index 11b27de16244c974c78e06cbee7f3d96efba3b09..d13946e8da6df99cb0606db9c0771125a19d47a5 100644 (file)
@@ -46,25 +46,48 @@ uint32_t Active::send_data(snort::Packet*, EncodeFlags, unsigned char const*, un
 }
 void Active::block_session(snort::Packet*, bool) { }
 void DetectionEngine::disable_all(snort::Packet*) { }
-Flow::Flow() { }
+Flow::Flow() { memset(this, 0, sizeof(*this)); }
 Flow::~Flow() { }
 Packet::Packet(bool) { packet_flags = 0; flow = nullptr; }
 Packet::~Packet() { }
+
+InspectApi mock_api;
+Inspector::Inspector()
+{
+    set_api(&mock_api);
+}
+Inspector::~Inspector() = default;
+bool Inspector::likes(Packet*) { return true; }
+bool Inspector::get_buf(const char*, Packet*, InspectionBuffer&) { return true; }
+class StreamSplitter* Inspector::get_splitter(bool) { return nullptr; }
+
 }
 
 void show_stats(PegCount*, const PegInfo*, unsigned, const char*) { }
 void show_stats(PegCount*, const PegInfo*, const IndexVec&, const char*, FILE*) { }
 
+class MockInspector : public snort::Inspector
+{
+public:
+
+  MockInspector() {}
+  ~MockInspector() override {}
+  void eval(snort::Packet*) override {}
+  bool configure(snort::SnortConfig*) override { return true;}
+};
+
+
 TEST_GROUP(payload_injector_test)
 {
     PayloadInjectorModule mod;
     InjectionControl control;
-    PegCount* counts = mod.get_counts();
+    PayloadInjectorCounts* counts = (PayloadInjectorCounts*)mod.get_counts();
     Flow flow;
 
     void setup() override
     {
-        counts[0] = 0;
+        counts->http_injects = 0;
+        counts->http2_injects = 0;
         control.http_page = (const uint8_t*)"test";
         control.http_page_len = 4;
         flow.set_state(Flow::FlowState::INSPECT);
@@ -77,7 +100,7 @@ TEST(payload_injector_test, not_configured_stream_not_established)
     Packet p(false);
     p.flow = &flow;
     InjectionReturnStatus status = mod.inject_http_payload(&p, control);
-    CHECK(counts[0] == 0);
+    CHECK(counts->http_injects == 0);
     CHECK(status == ERR_INJECTOR_NOT_CONFIGURED);
     CHECK(flow.flow_state == Flow::FlowState::BLOCK);
 }
@@ -89,7 +112,7 @@ TEST(payload_injector_test, not_configured_stream_established)
     p.packet_flags = PKT_STREAM_EST;
     p.flow = &flow;
     InjectionReturnStatus status = mod.inject_http_payload(&p, control);
-    CHECK(counts[0] == 0);
+    CHECK(counts->http_injects == 0);
     CHECK(status == ERR_INJECTOR_NOT_CONFIGURED);
     CHECK(flow.flow_state == Flow::FlowState::BLOCK);
 }
@@ -100,7 +123,7 @@ TEST(payload_injector_test, configured_stream_not_established)
     Packet p(false);
     p.flow = &flow;
     InjectionReturnStatus status = mod.inject_http_payload(&p, control);
-    CHECK(counts[0] == 0);
+    CHECK(counts->http_injects == 0);
     CHECK(status == ERR_STREAM_NOT_ESTABLISHED);
     CHECK(flow.flow_state == Flow::FlowState::BLOCK);
 }
@@ -110,11 +133,70 @@ TEST(payload_injector_test, configured_stream_established)
     mod.set_configured(true);
     Packet p(false);
     p.packet_flags = PKT_STREAM_EST;
+    mock_api.base.name = "http_inspect";
+    flow.gadget = new MockInspector();
+    p.flow = &flow;
+    InjectionReturnStatus status = mod.inject_http_payload(&p, control);
+    CHECK(counts->http_injects == 1);
+    CHECK(status == INJECTION_SUCCESS);
+    CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+    delete flow.gadget;
+}
+
+TEST(payload_injector_test, http2_stream0)
+{
+    mod.set_configured(true);
+    Packet p(false);
+    p.packet_flags = PKT_STREAM_EST;
+    mock_api.base.name = "http2_inspect";
+    flow.gadget = new MockInspector();
+    p.flow = &flow;
+    InjectionReturnStatus status = mod.inject_http_payload(&p, control);
+    CHECK(counts->http2_injects == 0);
+    CHECK(status == ERR_HTTP2_STREAM_ID_0);
+    CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+    delete flow.gadget;
+}
+
+TEST(payload_injector_test, http2_success)
+{
+    mod.set_configured(true);
+    Packet p(false);
+    p.packet_flags = PKT_STREAM_EST;
+    mock_api.base.name = "http2_inspect";
+    flow.gadget = new MockInspector();
     p.flow = &flow;
+    control.stream_id = 1;
     InjectionReturnStatus status = mod.inject_http_payload(&p, control);
-    CHECK(counts[0] == 1);
+    CHECK(counts->http2_injects == 1);
     CHECK(status == INJECTION_SUCCESS);
     CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+    delete flow.gadget;
+}
+
+TEST(payload_injector_test, unidentified_gadget_is_null)
+{
+    mod.set_configured(true);
+    Packet p(false);
+    p.packet_flags = PKT_STREAM_EST;
+    p.flow = &flow;
+    InjectionReturnStatus status = mod.inject_http_payload(&p, control);
+    CHECK(status == ERR_UNIDENTIFIED_PROTOCOL);
+    CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+}
+
+TEST(payload_injector_test, unidentified_gadget_name)
+{
+    mod.set_configured(true);
+    Packet p(false);
+    p.packet_flags = PKT_STREAM_EST;
+    mock_api.base.name = "inspector";
+    flow.gadget = new MockInspector();
+    p.flow = &flow;
+    InjectionReturnStatus status = mod.inject_http_payload(&p, control);
+    CHECK(status == ERR_UNIDENTIFIED_PROTOCOL);
+    CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+    delete flow.gadget;
 }
 
 int main(int argc, char** argv)