From: Mike Stepanek (mstepane) Date: Tue, 28 Jul 2020 14:33:32 +0000 (+0000) Subject: Merge pull request #2332 in SNORT/snort3 from ~MDAGON/snort3:inj_h2i to master X-Git-Tag: 3.0.2-3~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b1b9dc4277393d2a7876ca2864415bcdc351f232;p=thirdparty%2Fsnort3.git Merge pull request #2332 in SNORT/snort3 from ~MDAGON/snort3:inj_h2i to master Squashed commit of the following: commit 01d5bf727cb31e8c6ed1e6670a097909ec90d903 Author: mdagon Date: Mon Jul 6 13:56:57 2020 -0400 payload_injector: extend utility to support http2 (no injection) --- diff --git a/src/packet_io/active.cc b/src/packet_io/active.cc index 14aab9a5e..51ae3bf2f 100644 --- a/src/packet_io/active.cc +++ b/src/packet_io/active.cc @@ -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); } + diff --git a/src/payload_injector/payload_injector_module.cc b/src/payload_injector/payload_injector_module.cc index 37f6f0698..919df8ffc 100644 --- a/src/payload_injector/payload_injector_module.cc +++ b/src/payload_injector/payload_injector_module.cc @@ -28,10 +28,6 @@ #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; diff --git a/src/payload_injector/payload_injector_module.h b/src/payload_injector/payload_injector_module.h index a196545a5..67717ede3 100644 --- a/src/payload_injector/payload_injector_module.h +++ b/src/payload_injector/payload_injector_module.h @@ -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 diff --git a/src/payload_injector/test/payload_injector_test.cc b/src/payload_injector/test/payload_injector_test.cc index 11b27de16..d13946e8d 100644 --- a/src/payload_injector/test/payload_injector_test.cc +++ b/src/payload_injector/test/payload_injector_test.cc @@ -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)