as transmitting RST flags to endpoints as well as blocking flows within Snort.
It coordinates with the Active component of Snort to perform these functions.
-Currently it is being used for HTTP/1 injections. HTTP/2 support is in development.
+Currently it is being used for HTTP/1 and HTTP/2 injections. For HTTP/2
+injection triggered while server-to-client flow of traffic is in a middle of a
+frame is not supported. The traffic will be blocked, but the page will not be
+injected/displayed.
get_http2_payload supports translation of HTTP block/redirect page to HTTP2.
Current implementation is limited, the constraints are specified in
#include "detection/detection_engine.h"
#include "packet_io/active.h"
#include "protocols/packet.h"
+#include "service_inspectors/http2_inspect/http2_flow_data.h"
+#include "utils/util.h"
#define s_name "payload_injector"
#define s_help \
{
{ CountType::SUM, "http_injects", "total number of http injections" },
{ CountType::SUM, "http2_injects", "total number of http2 injections" },
+ { CountType::SUM, "http2_translate_err", "total number of http2 page translation errors" },
+ { CountType::SUM, "http2_mid_frame", "total number of attempts to inject mid-frame" },
{ CountType::END, nullptr, nullptr }
};
+// Should have an entry for each error in InjectionReturnStatus
+static const std::map <InjectionReturnStatus, const char*> InjectionErrorToString =
+{
+ { ERR_INJECTOR_NOT_CONFIGURED, "Payload injector is not configured" },
+ { ERR_STREAM_NOT_ESTABLISHED, "TCP stream not established" },
+ { ERR_UNIDENTIFIED_PROTOCOL, "Unidentified protocol" },
+ { ERR_HTTP2_STREAM_ID_0, "HTTP/2 - injection to stream 0" },
+ { ERR_PAGE_TRANSLATION, "Error in translating HTTP block page to HTTP/2. "
+ "Unsupported or bad format." },
+ { ERR_HTTP2_MID_FRAME, "HTTP/2 - attempt to inject mid frame. Currently not supported." },
+ { ERR_HTTP2_HDR_FIELD_VAL_LEN, "HTTP/2 header field value length > 127. "
+ " Currently not supported." },
+ { ERR_TRANSLATED_HDRS_SIZE,
+ "HTTP/2 translated header size is bigger than expected. Update max size." },
+ { ERR_HTTP2_BODY_SIZE, "HTTP/2 body is > 16k. Currently not supported." }
+};
+
bool PayloadInjectorModule::configured = false;
PayloadInjectorModule::PayloadInjectorModule() :
return true;
}
+InjectionReturnStatus PayloadInjectorModule::inject_http2_payload(Packet* p,
+ const InjectionControl& control, EncodeFlags df)
+{
+ InjectionReturnStatus status;
+
+ if (control.stream_id == 0)
+ status = ERR_HTTP2_STREAM_ID_0;
+ else
+ {
+ // Check if mid frame
+ Http2FlowData* const session_data =
+ (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id);
+ if (!session_data || session_data->is_mid_frame())
+ {
+ payload_injector_stats.http2_mid_frame++;
+ // FIXIT-E mid-frame injection not supported
+ status = ERR_HTTP2_MID_FRAME;
+ }
+ else
+ {
+ uint8_t* http2_payload;
+ uint32_t payload_len;
+ status = get_http2_payload(control, http2_payload, payload_len);
+ if (status == INJECTION_SUCCESS)
+ {
+ p->active->send_data(p, df, http2_payload, payload_len);
+ snort_free(http2_payload);
+ payload_injector_stats.http2_injects++;
+ return INJECTION_SUCCESS;
+ }
+ else
+ payload_injector_stats.http2_translate_err++;
+ }
+ }
+
+ // If we got here, shouldn't inject the page
+ p->active->send_data(p, df, nullptr, 0);
+ return status;
+}
+
InjectionReturnStatus PayloadInjectorModule::inject_http_payload(Packet* p,
const InjectionControl& control)
{
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;
+ status = inject_http2_payload(p, control, df);
+ else
+ status = ERR_UNIDENTIFIED_PROTOCOL;
}
else
status = ERR_STREAM_NOT_ESTABLISHED;
return status;
}
+
+const char* PayloadInjectorModule::get_err_string(InjectionReturnStatus status)
+{
+ auto iter = InjectionErrorToString.find(status);
+ assert (iter != InjectionErrorToString.end());
+ return iter->second;
+}
+
#ifndef PAYLOAD_INJECTOR_MODULE_H
#define PAYLOAD_INJECTOR_MODULE_H
+#include "framework/codec.h"
#include "framework/module.h"
namespace snort
{
PegCount http_injects;
PegCount http2_injects;
+ PegCount http2_translate_err;
+ PegCount http2_mid_frame;
};
extern THREAD_LOCAL PayloadInjectorCounts payload_injection_stats;
INJECTION_SUCCESS = 1,
ERR_INJECTOR_NOT_CONFIGURED = -1,
ERR_STREAM_NOT_ESTABLISHED = -2,
- ERR_HTTP2_STREAM_ID_0 = -3,
- ERR_UNIDENTIFIED_PROTOCOL = -4,
- ERR_PAGE_TRANSLATION = -4,
+ ERR_UNIDENTIFIED_PROTOCOL = -3,
+ ERR_HTTP2_STREAM_ID_0 = -4,
+ ERR_PAGE_TRANSLATION = -5,
+ ERR_HTTP2_MID_FRAME = -6,
+ ERR_HTTP2_HDR_FIELD_VAL_LEN = -7,
+ ERR_TRANSLATED_HDRS_SIZE = -8,
+ ERR_HTTP2_BODY_SIZE = -9,
+ // Update InjectionErrorToString when adding/removing error codes
};
struct InjectionControl
bool end(const char*, int, snort::SnortConfig*) override;
- static InjectionReturnStatus inject_http_payload(snort::Packet* p, const InjectionControl& control);
+ static InjectionReturnStatus inject_http_payload(snort::Packet* p, const
+ InjectionControl& control);
+ static const char* get_err_string(InjectionReturnStatus status);
+
#ifdef UNIT_TEST
void set_configured(bool val) { configured = val; }
#endif
private:
static bool configured;
+ static InjectionReturnStatus inject_http2_payload(snort::Packet* p, const
+ InjectionControl& control, snort::EncodeFlags df);
+
#ifdef UNIT_TEST
+
public:
#endif
- static InjectionReturnStatus get_http2_payload(InjectionControl control, uint8_t *& http2_payload, uint32_t & payload_len);
+ static InjectionReturnStatus get_http2_payload(InjectionControl control, uint8_t*& http2_payload, uint32_t& payload_len);
};
#endif
// payload_injector_translate_page.cc author Maya Dagon <mdagon@cisco.com>
// Translates HTTP 1.1 block/redirect page to HTTP2.
-// 1. Headers are separated by /r/n
-// 2. Headers end with /r/n
+// 1. Headers are separated by \r\n or \n
+// 2. Headers end with \r\n or \n
// 3. Must have headers and body
// 4. Translated header length <= 2000
-// 5. Supported: HTTP/1.1 403, HTTP/1.1 307, Connection: close,
+// 5. Supported: HTTP/1.1 200, HTTP/1.1 403, HTTP/1.1 307, Connection: close,
// Content-Length: , Content-Type: , Set-Cookie: , Location:
#ifdef HAVE_CONFIG_H
#include "service_inspectors/http2_inspect/http2_enum.h"
#include "utils/util.h"
+using namespace Http2Enums;
+
static const char status_403[] = "HTTP/1.1 403";
static uint8_t status_403_h2[] = { 0, 7, ':', 's', 't', 'a', 't', 'u', 's', 3, '4', '0', '3' };
static const char status_307[] = "HTTP/1.1 307";
static uint8_t status_307_h2[] = { 0, 7, ':', 's', 't', 'a', 't', 'u', 's', 3, '3', '0', '7' };
+static const char status_200[] = "HTTP/1.1 200";
+static uint8_t status_200_h2[] = { 0x88 };
static const char connection[] = "Connection: close";
static const uint8_t connection_h2[] = { 0, 10, 'c','o','n','n','e','c','t','i','o','n',
5, 'c', 'l', 'o', 's', 'e' };
static const uint32_t max_hdr_size = 2000;
// Write literal header field
-static InjectionReturnStatus write_indexed(char* hdr, uint32_t len, uint8_t*& out,
+static InjectionReturnStatus write_indexed(const uint8_t* hdr, uint32_t len, uint8_t*& out,
uint32_t& out_free_space, const uint8_t* ind, uint8_t ind_size)
{
- const char* sep = (char*)memchr(hdr,':',len);
+ const uint8_t* sep = (const uint8_t*)memchr(hdr,':',len);
assert(sep != nullptr);
const uint32_t skip_len = strlen(": ");
assert((sep - hdr) >= skip_len);
const uint8_t max_val_len = (1<<7) - 1; // FIXIT-E bigger than this will have to be 7 bit
// prefix
// encoded - currently not supported
- if ((val_len == 0) || (val_len > max_val_len))
+ if (val_len > max_val_len)
+ return ERR_HTTP2_HDR_FIELD_VAL_LEN;
+
+ if (val_len == 0)
return ERR_PAGE_TRANSLATION;
if (out_free_space < (val_len + 1 + ind_size))
#ifndef UNIT_TEST
assert(false); // increase max_hdr_size
#endif
- return ERR_PAGE_TRANSLATION;
+ return ERR_TRANSLATED_HDRS_SIZE;
}
memcpy(out, ind, ind_size);
#ifndef UNIT_TEST
assert(false); // increase max_hdr_size
#endif
- return ERR_PAGE_TRANSLATION;
+ return ERR_TRANSLATED_HDRS_SIZE;
}
memcpy(out, translation, size);
return INJECTION_SUCCESS;
}
-static InjectionReturnStatus translate_hdr_field(char* hdr, uint32_t len, uint8_t*& out,
+static InjectionReturnStatus translate_hdr_field(const uint8_t* hdr, uint32_t len, uint8_t*& out,
uint32_t& out_free_space)
{
if (len > strlen(status_403) && memcmp(hdr, status_403, strlen(status_403)) == 0)
}
else if (len > strlen(status_307) && memcmp(hdr, status_307, strlen(status_307)) == 0)
{
- return write_translation(out, out_free_space,status_307_h2, sizeof(status_307_h2));
+ return write_translation(out, out_free_space, status_307_h2, sizeof(status_307_h2));
+ }
+ else if (len > strlen(status_200) && memcmp(hdr, status_200, strlen(status_200)) == 0)
+ {
+ return write_translation(out, out_free_space, status_200_h2, sizeof(status_200_h2));
}
else if (len == strlen(connection) && memcmp(hdr, connection, strlen(connection))==0)
{
return ERR_PAGE_TRANSLATION;
}
-static InjectionReturnStatus get_http2_hdr(char* http_page, uint32_t len,
+static InjectionReturnStatus get_http2_hdr(const uint8_t* http_page, uint32_t len,
uint8_t* http2_hdr, uint32_t& hdr_len, uint32_t& body_offset)
{
InjectionReturnStatus status = ERR_PAGE_TRANSLATION;
body_offset = 0;
uint32_t hdr_free_space = max_hdr_size;
- char* page_cur = http_page;
+ const uint8_t* page_cur = http_page;
uint8_t* hdr_cur = http2_hdr;
while ((page_cur - http_page) < len)
{
- char* cr_newline = strstr(page_cur, "\r\n");
- if (cr_newline != nullptr)
+ const uint8_t* newline = (const uint8_t*)memchr(page_cur, '\n', len - (page_cur - http_page));
+ if (newline != nullptr)
{
- if (cr_newline == page_cur)
+ // FIXIT-E only \r\n should be supported
+ if (newline == page_cur || (newline == page_cur + 1 && *page_cur == '\r'))
{
// reached end of headers
- if ((page_cur - http_page + 2) < len)
- body_offset = page_cur - http_page + 2;
+ if ((newline + 1 - http_page) < len)
+ body_offset = newline + 1 - http_page;
break;
}
- status = translate_hdr_field(page_cur, cr_newline-page_cur, hdr_cur, hdr_free_space);
+ if (*(newline - 1) == '\r')
+ status = translate_hdr_field(page_cur, newline - page_cur - 1, hdr_cur,
+ hdr_free_space);
+ else
+ status = translate_hdr_field(page_cur, newline - page_cur, hdr_cur,
+ hdr_free_space);
if (status != INJECTION_SUCCESS)
break;
- page_cur = cr_newline + 2;
+ page_cur = newline + 1;
}
else
break;
}
- if (status == ERR_PAGE_TRANSLATION || body_offset == 0)
+ if (status != INJECTION_SUCCESS)
+ return status;
+
+ if (body_offset == 0)
return ERR_PAGE_TRANSLATION;
hdr_len = hdr_cur - http2_hdr;
if (control.http_page == nullptr || control.http_page_len == 0)
return ERR_PAGE_TRANSLATION;
- // create a string version to run with strstr
- char* page_string = (char*)snort_alloc(control.http_page_len + 1);
- memcpy(page_string, control.http_page, control.http_page_len);
- page_string[control.http_page_len] = '\0';
-
uint8_t http2_hdr[max_hdr_size];
uint32_t hdr_len, body_offset;
- InjectionReturnStatus status = get_http2_hdr(page_string, control.http_page_len, http2_hdr,
- hdr_len, body_offset);
+ InjectionReturnStatus status = get_http2_hdr(control.http_page, control.http_page_len,
+ http2_hdr, hdr_len, body_offset);
- snort_free(page_string);
-
- if (status == ERR_PAGE_TRANSLATION)
+ if (status != INJECTION_SUCCESS)
return status;
const uint32_t body_len = control.http_page_len - body_offset;
// FIXIT-E support larger body size
if (body_len > 1<<14)
- return ERR_PAGE_TRANSLATION;
+ return ERR_HTTP2_BODY_SIZE;
- payload_len = 2*Http2Enums::FRAME_HEADER_LENGTH + hdr_len + body_len;
+ payload_len = 2*FRAME_HEADER_LENGTH + hdr_len + body_len;
http2_payload = (uint8_t*)snort_alloc(payload_len);
uint8_t* http2_payload_cur = http2_payload;
- // FIXIT-E update flags
- write_frame_hdr(http2_payload_cur, hdr_len, Http2Enums::FT_HEADERS, 0, control.stream_id);
+ write_frame_hdr(http2_payload_cur, hdr_len, FT_HEADERS, END_HEADERS, control.stream_id);
memcpy(http2_payload_cur, http2_hdr, hdr_len);
http2_payload_cur += hdr_len;
- write_frame_hdr(http2_payload_cur, body_len, Http2Enums::FT_DATA, 0, control.stream_id);
+ write_frame_hdr(http2_payload_cur, body_len, FT_DATA, END_STREAM, control.stream_id);
memcpy(http2_payload_cur, control.http_page + body_offset, body_len);
return INJECTION_SUCCESS;
#include "flow/flow.h"
#include "packet_io/active.h"
#include "protocols/packet.h"
+#include "utils/util.h"
+#include "service_inspectors/http_inspect/http_enum.h"
+#include "service_inspectors/http2_inspect/http2_flow_data.h"
#include <CppUTest/CommandLineTestRunner.h>
#include <CppUTest/TestHarness.h>
using namespace snort;
+using namespace HttpCommon;
//--------------------------------------------------------------------------
// mocks
{
return 1;
}
+
void Active::block_session(snort::Packet*, bool) { }
void DetectionEngine::disable_all(snort::Packet*) { }
Flow::Flow() { memset(this, 0, sizeof(*this)); }
Flow::~Flow() { }
Packet::Packet(bool) { packet_flags = 0; flow = nullptr; }
Packet::~Packet() { }
+int DetectionEngine::queue_event(unsigned int, unsigned int, snort::Actions::Type) { return 0; }
+FlowData::~FlowData() { }
+FlowData::FlowData(unsigned int, snort::Inspector*) { }
+// Inspector mocks, used by MockInspector class
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*) { }
+// MockInspector class
+
class MockInspector : public snort::Inspector
{
public:
- MockInspector() {}
- ~MockInspector() override {}
- void eval(snort::Packet*) override {}
- bool configure(snort::SnortConfig*) override { return true;}
+ MockInspector() { }
+ ~MockInspector() override { }
+ void eval(snort::Packet*) override { }
+ bool configure(snort::SnortConfig*) override { return true; }
};
+// Mocks for PayloadInjectorModule::get_http2_payload
+
+static InjectionReturnStatus translation_status = INJECTION_SUCCESS;
+InjectionReturnStatus PayloadInjectorModule::get_http2_payload(InjectionControl,
+ uint8_t*& http2_payload, uint32_t& payload_len)
+{
+ if (translation_status == INJECTION_SUCCESS)
+ {
+ http2_payload = (uint8_t*)snort_alloc(1);
+ payload_len = 1;
+ }
+
+ return translation_status;
+}
+
+// Mocks for snort::Flow::get_flow_data
+
+unsigned Http2FlowData::inspector_id = 0;
+Http2Stream::~Http2Stream() { }
+HpackDynamicTable::~HpackDynamicTable() { }
+Http2FlowData::Http2FlowData(snort::Flow*) :
+ FlowData(inspector_id),
+ flow(nullptr),
+ hi(nullptr),
+ hpack_decoder
+ {
+ Http2HpackDecoder(this, SRC_CLIENT, events[SRC_CLIENT], infractions[SRC_CLIENT]),
+ Http2HpackDecoder(this, SRC_SERVER, events[SRC_SERVER], infractions[SRC_SERVER])
+ }
+ { }
+Http2FlowData::~Http2FlowData() { }
+Http2FlowData http2_flow_data(nullptr);
+FlowData* snort::Flow::get_flow_data(unsigned int) const { return &http2_flow_data; }
TEST_GROUP(payload_injector_test)
{
{
counts->http_injects = 0;
counts->http2_injects = 0;
+ counts->http2_translate_err = 0;
+ counts->http2_mid_frame = 0;
control.http_page = (const uint8_t*)"test";
control.http_page_len = 4;
flow.set_state(Flow::FlowState::INSPECT);
+ translation_status = INJECTION_SUCCESS;
+ http2_flow_data.set_continuation_expected(SRC_SERVER, false);
+ http2_flow_data.set_reading_frame(SRC_SERVER, false);
}
};
CHECK(counts->http_injects == 0);
CHECK(status == ERR_INJECTOR_NOT_CONFIGURED);
CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+ const char* err_string = mod.get_err_string(status);
+ CHECK(strcmp(err_string, "Payload injector is not configured") == 0);
}
TEST(payload_injector_test, not_configured_stream_established)
InjectionReturnStatus status = mod.inject_http_payload(&p, control);
CHECK(counts->http_injects == 0);
CHECK(status == ERR_STREAM_NOT_ESTABLISHED);
+ const char* err_string = mod.get_err_string(status);
+ CHECK(strcmp(err_string, "TCP stream not established") == 0);
CHECK(flow.flow_state == Flow::FlowState::BLOCK);
}
CHECK(counts->http2_injects == 0);
CHECK(status == ERR_HTTP2_STREAM_ID_0);
CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+ const char* err_string = mod.get_err_string(status);
+ CHECK(strcmp(err_string, "HTTP/2 - injection to stream 0") == 0);
delete flow.gadget;
}
InjectionReturnStatus status = mod.inject_http_payload(&p, control);
CHECK(status == ERR_UNIDENTIFIED_PROTOCOL);
CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+ const char* err_string = mod.get_err_string(status);
+ CHECK(strcmp(err_string, "Unidentified protocol") == 0);
}
TEST(payload_injector_test, unidentified_gadget_name)
delete flow.gadget;
}
+TEST(payload_injector_test, http2_mid_frame)
+{
+ 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;
+ http2_flow_data.set_reading_frame(SRC_SERVER, true);
+ InjectionReturnStatus status = mod.inject_http_payload(&p, control);
+ CHECK(counts->http2_mid_frame == 1);
+ CHECK(status == ERR_HTTP2_MID_FRAME);
+ CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+ const char* err_string = mod.get_err_string(status);
+ CHECK(strcmp(err_string, "HTTP/2 - attempt to inject mid frame. Currently not supported.")
+ == 0);
+ delete flow.gadget;
+}
+
+TEST(payload_injector_test, http2_continuation_expected)
+{
+ 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;
+ http2_flow_data.set_continuation_expected(SRC_SERVER, true);
+ InjectionReturnStatus status = mod.inject_http_payload(&p, control);
+ CHECK(counts->http2_mid_frame == 1);
+ CHECK(status == ERR_HTTP2_MID_FRAME);
+ CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+ delete flow.gadget;
+}
+
+TEST_GROUP(payload_injector_translate_err_test)
+{
+ PayloadInjectorModule mod;
+ InjectionControl control;
+ PayloadInjectorCounts* counts = (PayloadInjectorCounts*)mod.get_counts();
+ Flow flow;
+ InjectionReturnStatus status = INJECTION_SUCCESS;
+
+ void setup() override
+ {
+ counts->http_injects = 0;
+ counts->http2_injects = 0;
+ counts->http2_translate_err = 0;
+ counts->http2_mid_frame = 0;
+ control.http_page = (const uint8_t*)"test";
+ control.http_page_len = 4;
+ flow.set_state(Flow::FlowState::INSPECT);
+ http2_flow_data.set_continuation_expected(SRC_SERVER, false);
+ http2_flow_data.set_reading_frame(SRC_SERVER, false);
+ mod.set_configured(true);
+ mock_api.base.name = "http2_inspect";
+ flow.gadget = new MockInspector();
+ control.stream_id = 1;
+ }
+
+ void teardown() override
+ {
+ CHECK(counts->http2_translate_err == 1);
+ CHECK(status == translation_status);
+ CHECK(flow.flow_state == Flow::FlowState::BLOCK);
+ delete flow.gadget;
+ }
+};
+
+TEST(payload_injector_translate_err_test, http2_page_translation_err)
+{
+ Packet p(false);
+ p.packet_flags = PKT_STREAM_EST;
+ p.flow = &flow;
+ translation_status = ERR_PAGE_TRANSLATION;
+ status = mod.inject_http_payload(&p, control);
+ const char* err_string = mod.get_err_string(status);
+ CHECK(strcmp(err_string, "Error in translating HTTP block page to HTTP/2. "
+ "Unsupported or bad format.") == 0);
+}
+
+TEST(payload_injector_translate_err_test, http2_field_len_err)
+{
+ Packet p(false);
+ p.packet_flags = PKT_STREAM_EST;
+ p.flow = &flow;
+ translation_status = ERR_HTTP2_HDR_FIELD_VAL_LEN;
+ status = mod.inject_http_payload(&p, control);
+ const char* err_string = mod.get_err_string(status);
+ CHECK(strcmp(err_string, "HTTP/2 header field value length > 127. "
+ " Currently not supported.") == 0);
+}
+
+TEST(payload_injector_translate_err_test, http2_hdrs_size)
+{
+ Packet p(false);
+ p.packet_flags = PKT_STREAM_EST;
+ p.flow = &flow;
+ translation_status = ERR_TRANSLATED_HDRS_SIZE;
+ status = mod.inject_http_payload(&p, control);
+ const char* err_string = mod.get_err_string(status);
+ CHECK(strcmp(err_string,
+ "HTTP/2 translated header size is bigger than expected. Update max size.") == 0);
+}
+
+TEST(payload_injector_translate_err_test, http2_body_size)
+{
+ Packet p(false);
+ p.packet_flags = PKT_STREAM_EST;
+ p.flow = &flow;
+ translation_status = ERR_HTTP2_BODY_SIZE;
+ status = mod.inject_http_payload(&p, control);
+ const char* err_string = mod.get_err_string(status);
+ CHECK(strcmp(err_string, "HTTP/2 body is > 16k. Currently not supported.") == 0);
+}
+
int main(int argc, char** argv)
{
return CommandLineTestRunner::RunAllTests(argc, argv);
status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
CHECK(status == INJECTION_SUCCESS);
- uint8_t out[] = { 0x0, 0x0, 0x40, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x7, 0x3a, 0x73, 0x74,
- 0x61, 0x74, 0x75, 0x73, 0x3, 0x34, 0x30, 0x33, 0x0, 0xa, 0x63, 0x6f, 0x6e,
- 0x6e, 0x65,
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0xf, 0xd,
- 0x3, 0x35,
- 0x30, 0x34, 0xf, 0x10, 0x18, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d,
- 0x6c,
- 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x55, 0x54, 0x46,
- 0x2d,
- 0x38, 0x0, 0x0, 0x62, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3c, 0x21, 0x44, 0x4f,
- 0x43, 0x54,
- 0x59, 0x50, 0x45, 0x20, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0x3c, 0x68, 0x74,
- 0x6d,
- 0x6c, 0x3e, 0x3c, 0x68, 0x65, 0x61, 0x64, 0x3e, 0xa, 0x3c, 0x6d, 0x65, 0x74,
- 0x61,
- 0x68, 0x74, 0x74, 0x70, 0x2d, 0x65, 0x71, 0x75, 0x69, 0x76, 0x3d, 0x22, 0x63,
- 0x6f,
- 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x22, 0x20, 0x63,
- 0x6f,
- 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68,
- 0x74,
- 0x6d, 0x6c, 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x55,
- 0x54,
- 0x46, 0x2d, 0x38, 0x22, 0x20, 0x2f, 0x3e, 0xa };
+ uint8_t out[] =
+ {
+ 0x0, 0x0, 0x40, 0x1, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x7, 0x3a, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x3, 0x34, 0x30, 0x33, 0x0, 0xa, 0x63, 0x6f, 0x6e,
+ 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0xf, 0xd,
+ 0x3, 0x35, 0x30, 0x34, 0xf, 0x10, 0x18, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d,
+ 0x6c, 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x55, 0x54, 0x46,
+ 0x2d, 0x38, 0x0, 0x0, 0x62, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x3c, 0x21, 0x44, 0x4f,
+ 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0x3c, 0x68, 0x74,
+ 0x6d, 0x6c, 0x3e, 0x3c, 0x68, 0x65, 0x61, 0x64, 0x3e, 0xa, 0x3c, 0x6d, 0x65, 0x74,
+ 0x61, 0x68, 0x74, 0x74, 0x70, 0x2d, 0x65, 0x71, 0x75, 0x69, 0x76, 0x3d, 0x22, 0x63,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x22, 0x20, 0x63,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68,
+ 0x74, 0x6d, 0x6c, 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x55,
+ 0x54, 0x46, 0x2d, 0x38, 0x22, 0x20, 0x2f, 0x3e, 0xa
+ };
CHECK(payload_len == sizeof(out));
CHECK(memcmp(http2_payload, out, payload_len) == 0);
CHECK(status == INJECTION_SUCCESS);
- uint8_t out[] = { 0x0, 0x0, 0x36, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x7, 0x3a, 0x73, 0x74,
- 0x61, 0x74, 0x75, 0x73, 0x3, 0x33, 0x30, 0x37, 0xf, 0x1f, 0x8, 0x68, 0x74,
- 0x74, 0x70,
- 0x73, 0x3a, 0x2f, 0x2f, 0xf, 0x28, 0x1b, 0x30, 0x34, 0x66, 0x32, 0x3b, 0x20,
- 0x4d,
- 0x61, 0x78, 0x2d, 0x41, 0x67, 0x65, 0x3a, 0x20, 0x36, 0x30, 0x30, 0x3b, 0x20,
- 0x70,
- 0x61, 0x74, 0x68, 0x3d, 0x2f, 0x3b, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x1, 0x42,
- 0x6f, 0x64, 0x79, 0xa };
+ uint8_t out[] =
+ {
+ 0x0, 0x0, 0x36, 0x1, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x7, 0x3a, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x3, 0x33, 0x30, 0x37, 0xf, 0x1f, 0x8, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0xf, 0x28, 0x1b, 0x30, 0x34, 0x66, 0x32, 0x3b, 0x20,
+ 0x4d, 0x61, 0x78, 0x2d, 0x41, 0x67, 0x65, 0x3a, 0x20, 0x36, 0x30, 0x30, 0x3b, 0x20,
+ 0x70, 0x61, 0x74, 0x68, 0x3d, 0x2f, 0x3b, 0x0, 0x0, 0x5, 0x0, 0x1, 0x0, 0x0, 0x0,
+ 0x1, 0x42, 0x6f, 0x64, 0x79, 0xa
+ };
+ CHECK(payload_len == sizeof(out));
+ CHECK(memcmp(http2_payload, out, payload_len) == 0);
+
+ snort_free(http2_payload);
+}
+
+// Same like 2 , using \n instead of \r\n
+TEST(payload_injector_translate_test, basic_hdr_translation3)
+{
+ char http_page[] =
+ "HTTP/1.1 307 Proxy Redirect\nLocation: https://\nSet-Cookie: 04f2; Max-Age: 600; path=/;\n\nBody\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == INJECTION_SUCCESS);
+
+ uint8_t out[] =
+ {
+ 0x0, 0x0, 0x36, 0x1, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x7, 0x3a, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x3, 0x33, 0x30, 0x37, 0xf, 0x1f, 0x8, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0xf, 0x28, 0x1b, 0x30, 0x34, 0x66, 0x32, 0x3b, 0x20,
+ 0x4d, 0x61, 0x78, 0x2d, 0x41, 0x67, 0x65, 0x3a, 0x20, 0x36, 0x30, 0x30, 0x3b, 0x20,
+ 0x70, 0x61, 0x74, 0x68, 0x3d, 0x2f, 0x3b, 0x0, 0x0, 0x5, 0x0, 0x1, 0x0, 0x0, 0x0,
+ 0x1, 0x42, 0x6f, 0x64, 0x79, 0xa
+ };
+ CHECK(payload_len == sizeof(out));
+ CHECK(memcmp(http2_payload, out, payload_len) == 0);
+
+ snort_free(http2_payload);
+}
+
+// Mix of \r\n and \n, body length == 1
+TEST(payload_injector_translate_test, mix_n_and_rn)
+{
+ char http_page[] =
+ "HTTP/1.1 200 OK\nConnection: close\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\nContent-Length: 956\r\nContent-Type: text/html; charset=UTF-8\n\nb";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == INJECTION_SUCCESS);
+
+ uint8_t out[] =
+ {
+ 0x0, 0x0, 0x52, 0x1, 0x4, 0x0, 0x0, 0x0, 0x1, 0x88, 0x0, 0xa, 0x63, 0x6f,
+ 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5, 0x63, 0x6c, 0x6f, 0x73,
+ 0x65, 0xf, 0x28, 0x1b, 0x30, 0x34, 0x66, 0x32, 0x3b, 0x20, 0x4d, 0x61, 0x78, 0x2d, 0x41,
+ 0x67, 0x65, 0x3a, 0x20, 0x36, 0x30, 0x30, 0x3b, 0x20, 0x70, 0x61, 0x74, 0x68, 0x3d,
+ 0x2f, 0x3b, 0xf, 0xd, 0x3, 0x39, 0x35, 0x36, 0xf, 0x10, 0x18, 0x74, 0x65, 0x78,
+ 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74,
+ 0x3d, 0x55, 0x54, 0x46, 0x2d, 0x38, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1,
+ 0x62
+ };
CHECK(payload_len == sizeof(out));
CHECK(memcmp(http2_payload, out, payload_len) == 0);
CHECK(status == ERR_PAGE_TRANSLATION);
}
-TEST(payload_injector_translate_test, unsuporrted_status)
+TEST(payload_injector_translate_test, unsupported_status)
{
char http_page[] =
- "HTTP/1.1 200 OK\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n";
+ "HTTP/1.1 201 Created\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n";
InjectionControl control;
control.stream_id = 1;
control.http_page_len = size;
status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
- CHECK(status == ERR_PAGE_TRANSLATION);
+ CHECK(status == ERR_HTTP2_HDR_FIELD_VAL_LEN);
}
// Header value has maximum supported length 127
CHECK(status == INJECTION_SUCCESS);
CHECK(payload_len == 2019);
- uint8_t hdr[] = { 0x0, 0x7, 0xd0, 0x1, 0x0, 0x0, 0x0, 0xf0, 0x0 };
+ uint8_t hdr[] = { 0x0, 0x7, 0xd0, 0x1, 0x4, 0x0, 0x0, 0xf0, 0x0 };
CHECK(memcmp(http2_payload, hdr, sizeof(hdr))==0);
- uint8_t body[] = { 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 'b' };
+ uint8_t body[] = { 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0xf0, 0x0, 'b' };
CHECK(memcmp(http2_payload + 2009, body, sizeof(body))==0);
snort_free(http2_payload);
}
control.http_page_len = size;
status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
- CHECK(status == ERR_PAGE_TRANSLATION);
+ CHECK(status == ERR_TRANSLATED_HDRS_SIZE);
}
// Translated header > 2000. Goes through write_translation code path.
control.http_page = http_page;
control.http_page_len = size;
status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
- CHECK(status == ERR_PAGE_TRANSLATION);
+ CHECK(status == ERR_TRANSLATED_HDRS_SIZE);
}
TEST(payload_injector_translate_test, payload_body_larger_than_max)
control.http_page = http_page;
control.http_page_len = size;
status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
- CHECK(status == ERR_PAGE_TRANSLATION);
+ CHECK(status == ERR_HTTP2_BODY_SIZE);
}
TEST(payload_injector_translate_test, http_page_is_nullptr)
TEST(payload_injector_translate_test, http_page_is_0_length)
{
- uint8_t http_page[] = {1};
+ uint8_t http_page[] = { 1 };
InjectionControl control;
control.http_page = http_page;
}
}
+ if (data_state == FULL_FRAME)
+ session_data->reading_frame[source_id] = false;
+
frame_bytes_seen += (cur_pos - leftover_bytes - data_offset - leftover_padding);
*flush_offset = data_offset = cur_pos;
session_data->scan_remaining_frame_octets[source_id] = frame_length - frame_bytes_seen;
Http2ConnectionSettings* get_connection_settings(const HttpCommon::SourceId source_id)
{ return &connection_settings[source_id]; }
+ bool is_mid_frame(const HttpCommon::SourceId source_id = HttpCommon::SRC_SERVER)
+ { return (continuation_expected[source_id] || reading_frame[source_id]); }
+
+#ifdef UNIT_TEST
+ void set_reading_frame(HttpCommon::SourceId source_id, bool val)
+ { reading_frame[source_id] = val;}
+ void set_continuation_expected(HttpCommon::SourceId source_id, bool val)
+ { continuation_expected[source_id] = val;}
+#endif
+
protected:
snort::Flow* flow;
HttpInspect* const hi;
uint8_t padding_octets_in_frame[2] = { 0, 0 };
bool get_padding_len[2] = { false, false };
+ // used to signal frame wasn't fully read yet,
+ // currently used by payload injector
+ bool reading_frame[2] = { false, false };
+
#ifdef REG_TEST
static uint64_t instance_count;
uint64_t seq_num;
}
// Have the full frame
+ session_data->reading_frame[source_id] = false;
StreamSplitter::Status status = StreamSplitter::FLUSH;
switch (type)
{
{
// Scanning a new frame
session_data->num_frame_headers[source_id] += 1;
+ session_data->reading_frame[source_id] = true;
}
// The first nine bytes are the frame header. But all nine might not all be