From: Mike Stepanek (mstepane) Date: Tue, 8 Oct 2019 17:54:07 +0000 (-0400) Subject: Merge pull request #1768 in SNORT/snort3 from ~KATHARVE/snort3:h2i_decode_string_lite... X-Git-Tag: 3.0.0-262~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=14bcb80e6c7b626c918fc38f1038e26422668efa;p=thirdparty%2Fsnort3.git Merge pull request #1768 in SNORT/snort3 from ~KATHARVE/snort3:h2i_decode_string_literals to master Squashed commit of the following: commit c2a9ef959b7ecda8405591d9f53b041b6b06cec1 Author: Katura Harvey Date: Thu Sep 5 08:49:22 2019 -0400 http2_inspect: parse hpack header representations and decode string literals --- diff --git a/src/service_inspectors/http2_inspect/dev_notes.txt b/src/service_inspectors/http2_inspect/dev_notes.txt index 26c649e90..c636cc013 100644 --- a/src/service_inspectors/http2_inspect/dev_notes.txt +++ b/src/service_inspectors/http2_inspect/dev_notes.txt @@ -1,6 +1,14 @@ The HTTP/2 inspector (H2I) will convert HTTP/2 frames into HTTP/1.1 message sections and feed them to the new HTTP inspector (NHI) for further processing. -The current implementation is the very first step. It splits an HTTP/2 stream into frames and -forwards them for inspection. It does not interface with NHI, does not provide error detection and -handling, and does not address the multiplexed nature of HTTP/2. +The current implementation is the very first step. It splits an HTTP/2 stream into frame sections and +forwards them for inspection. It does not interface with NHI and does not address the multiplexed +nature of HTTP/2. + +The Http2StreamSplitter strips the frame headers from the frame data and stores them in separate +buffers. As in NHI, long data frames are split into 16kb chunks for inspection. If a header frame is +followed by continuation frames, all the header frames are flushed together for inspection. The +frame headers from each frame are stored contiguously in the frame_header buffer. After cutting out +the frame headers, the frame data is stored as a single block, consisting of the HPACK encoded +HTTP/2 headers. HPACK decoding is under ongoing development. Decoded headers will be stored in the +http2_decoded_header buffer. diff --git a/src/service_inspectors/http2_inspect/http2_enum.h b/src/service_inspectors/http2_inspect/http2_enum.h index bb1133374..02e2e89ac 100644 --- a/src/service_inspectors/http2_inspect/http2_enum.h +++ b/src/service_inspectors/http2_inspect/http2_enum.h @@ -52,6 +52,7 @@ enum EventSid EVENT_STRING_DECODE_FAILURE = 3, EVENT_MISSING_CONTINUATION = 4, EVENT_UNEXPECTED_CONTINUATION = 5, + EVENT_MISFORMATTED_HTTP2 = 6, EVENT_PREFACE_MATCH_FAILURE = 7, EVENT__MAX_VALUE }; @@ -66,7 +67,7 @@ enum Infraction INF_INT_LEADING_ZEROS = 3, INF_STRING_EMPTY_BUFF = 4, INF_STRING_MISSING_BYTES = 5, - INF_OUT_BUFF_OUT_OF_SPACE = 6, + INF_DECODED_HEADER_BUFF_OUT_OF_SPACE = 6, INF_HUFFMAN_BAD_PADDING = 7, INF_HUFFMAN_DECODED_EOS = 8, INF_HUFFMAN_INCOMPLETE_CODE_PADDING = 9, diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.cc b/src/service_inspectors/http2_inspect/http2_flow_data.cc index 741d6ed9d..0b4060e28 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.cc +++ b/src/service_inspectors/http2_inspect/http2_flow_data.cc @@ -71,7 +71,6 @@ Http2FlowData::~Http2FlowData() for (int k=0; k <= 1; k++) { delete[] frame_header[k]; - delete[] currently_processing_frame_header[k]; delete[] frame_data[k]; delete[] http2_decoded_header[k]; delete infractions[k]; diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.h b/src/service_inspectors/http2_inspect/http2_flow_data.h index b52bbdd95..9191b3c4f 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.h +++ b/src/service_inspectors/http2_inspect/http2_flow_data.h @@ -31,6 +31,8 @@ #include "stream/stream_splitter.h" #include "http2_enum.h" +#include "http2_hpack_int_decode.h" +#include "http2_hpack_string_decode.h" using Http2Infractions = Infractions; @@ -53,6 +55,36 @@ public: uint32_t*, HttpCommon::SourceId); friend bool implement_get_buf(unsigned id, Http2FlowData*, HttpCommon::SourceId, snort::InspectionBuffer&); + friend bool decode_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* raw_header_buffer, const uint32_t header_length); + friend bool decode_header_line(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t encoded_header_buffer_length, + uint32_t& bytes_consumed, uint8_t* decoded_header_buffer, + const uint32_t decoded_header_buffer_length, uint32_t& bytes_written); + friend bool handle_dynamic_size_update(Http2FlowData* session_data, + HttpCommon::SourceId source_id, const uint8_t* encoded_header_buffer, + const uint32_t encoded_header_length, const Http2HpackIntDecode &decode_int, + uint32_t &bytes_consumed, uint32_t &bytes_written); + friend bool decode_literal_header_line(Http2FlowData* session_data, + HttpCommon::SourceId source_id, const uint8_t* encoded_header_buffer, + const uint32_t encoded_header_buffer_length, + const uint8_t name_index_mask, const Http2HpackIntDecode &decode_int, + uint32_t &bytes_consumed, uint8_t* decoded_header_buffer, + const uint32_t decoded_header_buffer_length, uint32_t &bytes_written); + friend bool decode_index(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, + const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, + uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, + uint32_t &bytes_written); + friend bool decode_string_literal(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, + const Http2HpackStringDecode &decode_string, bool is_field_name, uint32_t &bytes_consumed, + uint8_t* decoded_header_buffer, const uint32_t decoded_header_buffer_length, + uint32_t &bytes_written); + friend bool write_decoded_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* in_buffer, const uint32_t in_length, + uint8_t* decoded_header_buffer, uint32_t decoded_header_buffer_length, + uint32_t &bytes_written); size_t size_of() override { return sizeof(*this); } @@ -70,7 +102,7 @@ protected: // Internal to scan bool continuation_expected[2] = { false, false }; - uint8_t* currently_processing_frame_header[2] = { nullptr, nullptr }; + uint8_t currently_processing_frame_header[2][Http2Enums::FRAME_HEADER_LENGTH]; uint32_t inspection_section_length[2] = { 0, 0 }; uint32_t leftover_data[2] = { 0, 0 }; @@ -92,7 +124,6 @@ protected: // transaction in NHI. Also as in NHI accessor methods will need to be added. Http2Infractions* infractions[2] = { new Http2Infractions, new Http2Infractions }; Http2EventGen* events[2] = { new Http2EventGen, new Http2EventGen }; - #ifdef REG_TEST static uint64_t instance_count; diff --git a/src/service_inspectors/http2_inspect/http2_hpack_int_decode.cc b/src/service_inspectors/http2_inspect/http2_hpack_int_decode.cc index e2ab8c434..835c40f3c 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack_int_decode.cc +++ b/src/service_inspectors/http2_inspect/http2_hpack_int_decode.cc @@ -30,15 +30,14 @@ using namespace Http2Enums; static const uint8_t VAL_MASK = 0x7F; static const uint8_t FLAG_BIT = 0x80; -Http2HpackIntDecode::Http2HpackIntDecode(uint8_t prefix, Http2EventGen* events, - Http2Infractions* infractions) : prefix_mask(((uint16_t)1 << prefix) - 1), events(events), - infractions(infractions) +Http2HpackIntDecode::Http2HpackIntDecode(uint8_t prefix) : prefix_mask(((uint16_t)1 << prefix) - 1) { assert ((0 < prefix) && (prefix < 9)); } bool Http2HpackIntDecode::translate(const uint8_t* in_buff, const uint32_t in_len, - uint32_t& bytes_consumed, uint64_t& result) + uint32_t& bytes_consumed, uint64_t& result, Http2EventGen* events, + Http2Infractions* infractions) const { bytes_consumed = 0; result = 0; diff --git a/src/service_inspectors/http2_inspect/http2_hpack_int_decode.h b/src/service_inspectors/http2_inspect/http2_hpack_int_decode.h index ce0e17f59..f62e6cec5 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack_int_decode.h +++ b/src/service_inspectors/http2_inspect/http2_hpack_int_decode.h @@ -33,16 +33,12 @@ using Http2EventGen = EventGentranslate(in_buff, in_len, bytes_consumed, encoded_len)) + if (!decode7.translate(in_buff, in_len, bytes_consumed, encoded_len, events, infractions)) return false; if (encoded_len > (uint64_t)(in_len - bytes_consumed)) @@ -72,25 +68,21 @@ bool Http2HpackStringDecode::translate(const uint8_t* in_buff, const uint32_t in return true; if (!isHuffman) - return get_string(in_buff, encoded_len, bytes_consumed, out_buff, out_len, bytes_written); + return get_string(in_buff, encoded_len, bytes_consumed, out_buff, out_len, bytes_written, + events, infractions); return get_huffman_string(in_buff, encoded_len, bytes_consumed, out_buff, out_len, - bytes_written); -} - -Http2HpackStringDecode::~Http2HpackStringDecode() -{ - assert(decode7 != nullptr); - delete decode7; + bytes_written, events, infractions); } bool Http2HpackStringDecode::get_string(const uint8_t* in_buff, const uint32_t encoded_len, - uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written) + uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written, + Http2EventGen* events, Http2Infractions* infractions) const { if (encoded_len > out_len) { - *infractions += INF_OUT_BUFF_OUT_OF_SPACE; - events->create_event(EVENT_STRING_DECODE_FAILURE); + *infractions += INF_DECODED_HEADER_BUFF_OUT_OF_SPACE; + events->create_event(EVENT_MISFORMATTED_HTTP2); return false; } @@ -103,7 +95,7 @@ bool Http2HpackStringDecode::get_string(const uint8_t* in_buff, const uint32_t e // return is tail/padding bool Http2HpackStringDecode::get_next_byte(const uint8_t* in_buff, const uint32_t last_byte, uint32_t& bytes_consumed, uint8_t& cur_bit, uint8_t match_len, uint8_t& byte, - bool& another_search) + bool& another_search) const { another_search = true; cur_bit += match_len; @@ -148,7 +140,8 @@ bool Http2HpackStringDecode::get_next_byte(const uint8_t* in_buff, const uint32_ } bool Http2HpackStringDecode::get_huffman_string(const uint8_t* in_buff, const uint32_t encoded_len, - uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written) + uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written, + Http2EventGen* events, Http2Infractions* infractions) const { const uint32_t last_encoded_byte = bytes_consumed + encoded_len; uint8_t byte; @@ -161,7 +154,7 @@ bool Http2HpackStringDecode::get_huffman_string(const uint8_t* in_buff, const ui const uint32_t max_length = floor(encoded_len * 8.0/5.0); if (max_length > out_len) { - *infractions += INF_OUT_BUFF_OUT_OF_SPACE; + *infractions += INF_DECODED_HEADER_BUFF_OUT_OF_SPACE; events->create_event(EVENT_STRING_DECODE_FAILURE); return false; } diff --git a/src/service_inspectors/http2_inspect/http2_hpack_string_decode.h b/src/service_inspectors/http2_inspect/http2_hpack_string_decode.h index 464bf6ba6..0a772420c 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack_string_decode.h +++ b/src/service_inspectors/http2_inspect/http2_hpack_string_decode.h @@ -30,22 +30,23 @@ class Http2HpackStringDecode { public: - Http2HpackStringDecode(Http2EventGen* events, Http2Infractions* infractions); + Http2HpackStringDecode() : decode7(7) { } bool translate(const uint8_t* in_buff, const uint32_t in_len, uint32_t& bytes_consumed, - uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written); - ~Http2HpackStringDecode(); + uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written, + Http2EventGen* events, Http2Infractions* infractions) const; private: bool get_string(const uint8_t* in_buff, const uint32_t encoded_len, uint32_t& bytes_consumed, - uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written); + uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written, Http2EventGen* events, + Http2Infractions* infractions) const; bool get_huffman_string(const uint8_t* in_buff, const uint32_t encoded_len, - uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written); + uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& + bytes_written, Http2EventGen* events, Http2Infractions* infractions) const; bool get_next_byte(const uint8_t* in_buff, const uint32_t last_byte, - uint32_t& bytes_consumed, uint8_t& cur_bit, uint8_t match_len, uint8_t& byte, bool& another_search); + uint32_t& bytes_consumed, uint8_t& cur_bit, uint8_t match_len, uint8_t& byte, + bool& another_search) const; - Http2HpackIntDecode* const decode7; - Http2EventGen* const events; - Http2Infractions* const infractions; + const Http2HpackIntDecode decode7; }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_inspect.cc b/src/service_inspectors/http2_inspect/http2_inspect.cc index a43b109c9..bbe8d3c2c 100644 --- a/src/service_inspectors/http2_inspect/http2_inspect.cc +++ b/src/service_inspectors/http2_inspect/http2_inspect.cc @@ -140,8 +140,7 @@ void Http2Inspect::clear(Packet* p) delete[] session_data->http2_decoded_header[source_id]; session_data->http2_decoded_header[source_id] = nullptr; session_data->continuation_expected[source_id] = false; - delete[] session_data->currently_processing_frame_header[source_id]; - session_data->currently_processing_frame_header[source_id] = nullptr; session_data->frames_aggregated[source_id] = 0; + session_data->header_octets_seen[source_id] = 0; } diff --git a/src/service_inspectors/http2_inspect/http2_stream_splitter.h b/src/service_inspectors/http2_inspect/http2_stream_splitter.h index 167353e61..2a351e7ae 100644 --- a/src/service_inspectors/http2_inspect/http2_stream_splitter.h +++ b/src/service_inspectors/http2_inspect/http2_stream_splitter.h @@ -25,6 +25,8 @@ #include "http2_enum.h" #include "http2_flow_data.h" +#include "http2_hpack_int_decode.h" +#include "http2_hpack_string_decode.h" class Http2Inspect; @@ -44,8 +46,24 @@ public: // FIXIT-M should return actual packet buffer size unsigned max(snort::Flow*) override { return Http2Enums::MAX_OCTETS; } + friend bool decode_literal_header_line(Http2FlowData* session_data, + HttpCommon::SourceId source_id, const uint8_t* encoded_header_buffer, + const uint32_t encoded_header_length, const uint8_t name_index_mask, + const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, + uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, + uint32_t &bytes_written); + friend bool decode_header_line(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, + uint32_t& bytes_consumed, uint8_t* decoded_header_buffer, + const uint32_t decoded_header_length, uint32_t& bytes_written); + private: const HttpCommon::SourceId source_id; + static Http2HpackIntDecode decode_int7; + static Http2HpackIntDecode decode_int6; + static Http2HpackIntDecode decode_int5; + static Http2HpackIntDecode decode_int4; + static Http2HpackStringDecode decode_string; }; snort::StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t* data, @@ -53,6 +71,8 @@ snort::StreamSplitter::Status implement_scan(Http2FlowData* session_data, const const snort::StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned total, unsigned offset, const uint8_t* data, unsigned len, uint32_t flags, HttpCommon::SourceId source_id); +bool decode_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* raw_header_buffer, const uint32_t header_length, bool field_name); enum ValidationResult { V_GOOD, V_BAD, V_TBD }; @@ -60,4 +80,3 @@ ValidationResult validate_preface(const uint8_t* data, const uint32_t length, const uint32_t octets_seen); #endif - diff --git a/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc b/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc index 7566f58ea..890e2e5ec 100644 --- a/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc +++ b/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc @@ -23,22 +23,40 @@ #include -#include "http2_stream_splitter.h" #include "protocols/packet.h" #include "service_inspectors/http_inspect/http_common.h" +#include "service_inspectors/http_inspect/http_field.h" +#include "service_inspectors/http_inspect/http_test_input.h" +#include "service_inspectors/http_inspect/http_test_manager.h" #include "http2_flow_data.h" +#include "http2_hpack_int_decode.h" +#include "http2_hpack_string_decode.h" +#include "http2_stream_splitter.h" using namespace snort; using namespace HttpCommon; using namespace Http2Enums; -static uint32_t get_frame_length(const uint8_t *frame_buffer) +#define STATIC_TABLE_MAX_INDEX 61 + +// FIXIT-H remove these declarations once implemented, for some reason this makes the compiler +// happy for build alt +bool decode_static_table_index(void); +bool decode_dynamic_table_index(void); + +Http2HpackIntDecode Http2StreamSplitter::decode_int7(7); +Http2HpackIntDecode Http2StreamSplitter::decode_int6(6); +Http2HpackIntDecode Http2StreamSplitter::decode_int5(5); +Http2HpackIntDecode Http2StreamSplitter::decode_int4(4); +Http2HpackStringDecode Http2StreamSplitter::decode_string; + +static uint32_t get_frame_length(const uint8_t* frame_buffer) { return (frame_buffer[0] << 16) + (frame_buffer[1] << 8) + frame_buffer[2]; } -static uint8_t get_frame_type(const uint8_t *frame_buffer) +static uint8_t get_frame_type(const uint8_t* frame_buffer) { const uint8_t frame_type_index = 3; if (frame_buffer) @@ -47,7 +65,7 @@ static uint8_t get_frame_type(const uint8_t *frame_buffer) return FT__NONE; } -static uint8_t get_frame_flags(const uint8_t *frame_buffer) +static uint8_t get_frame_flags(const uint8_t* frame_buffer) { const uint8_t frame_flags_index = 4; if (frame_buffer) @@ -108,41 +126,57 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t // Have full inspection section, flush and update leftover *flush_offset = session_data->inspection_section_length[source_id]; - session_data->leftover_data[source_id] -= session_data->inspection_section_length[source_id]; + session_data->leftover_data[source_id] -= + session_data->inspection_section_length[source_id]; session_data->octets_seen[source_id] = 0; } else { // frame with header - // If there is a header frame followed by a continuation frame in the same tcp segment, - // need to process multiple frames in a single scan + // If there is a header frame followed by a continuation frame in the same tcp segment, need + // to process multiple frames in a single scan *flush_offset = 0; + uint32_t remaining_length = length; + const uint8_t *data_pos = data; do { - if (session_data->currently_processing_frame_header[source_id] == nullptr) + if (session_data->header_octets_seen[source_id] == 0) { session_data->header_coming[source_id] = true; - session_data->currently_processing_frame_header[source_id] = new uint8_t[FRAME_HEADER_LENGTH]; session_data->octets_seen[source_id] = 0; session_data->header_octets_seen[source_id] = 0; - session_data->frames_aggregated[source_id] = 1; } - // The first nine bytes are the frame header. But all nine might not all be present in the - // first TCP segment we receive. - for (uint32_t k = 0; (k < length) && (session_data->header_octets_seen[source_id] < - FRAME_HEADER_LENGTH); k++, session_data->header_octets_seen[source_id]++) + // The first nine bytes are the frame header. But all nine might not all be present in + // the first TCP segment we receive. + if (session_data->header_octets_seen[source_id] < FRAME_HEADER_LENGTH) { - session_data->currently_processing_frame_header[source_id] - [session_data->header_octets_seen[source_id]] = data[k]; + uint32_t remaining_header = FRAME_HEADER_LENGTH - + session_data->header_octets_seen[source_id]; + uint32_t remaining_header_in_data = remaining_header > remaining_length ? + remaining_length : remaining_header; + memcpy(session_data->currently_processing_frame_header[source_id] + + session_data->header_octets_seen[source_id], + data_pos, remaining_header_in_data); + session_data->header_octets_seen[source_id] += remaining_header_in_data; + if (session_data->header_octets_seen[source_id] < FRAME_HEADER_LENGTH) + { + session_data->octets_seen[source_id] += remaining_header_in_data; + status = StreamSplitter::SEARCH; + break; + } + session_data->frames_aggregated[source_id] += 1; } - if (session_data->header_octets_seen[source_id] < FRAME_HEADER_LENGTH) + + uint8_t type = get_frame_type(session_data->currently_processing_frame_header[source_id]); + + if (session_data->continuation_expected[source_id] && type != FT_CONTINUATION) { - session_data->octets_seen[source_id] += length; - status = StreamSplitter::SEARCH; + *session_data->infractions[source_id] += INF_MISSING_CONTINUATION; + session_data->events[source_id]->create_event(EVENT_MISSING_CONTINUATION); + status = StreamSplitter::ABORT; break; } - uint8_t type = get_frame_type(session_data->currently_processing_frame_header[source_id]); // Frame length does not include the frame header uint32_t const frame_length = get_frame_length(session_data-> @@ -154,7 +188,8 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t if ((type == FT_DATA) && (frame_length > DATA_SECTION_SIZE)) { // Break up long data frames into pieces for detection - session_data->inspection_section_length[source_id] = DATA_SECTION_SIZE + FRAME_HEADER_LENGTH; + session_data->inspection_section_length[source_id] = DATA_SECTION_SIZE + + FRAME_HEADER_LENGTH; } else if (frame_length + FRAME_HEADER_LENGTH > MAX_OCTETS) { @@ -174,7 +209,8 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t else { // we have the full frame section to flush to detection - *flush_offset += session_data->inspection_section_length[source_id] - session_data->octets_seen[source_id]; + *flush_offset += session_data->inspection_section_length[source_id] - + session_data->octets_seen[source_id]; session_data->leftover_data[source_id] = frame_length + FRAME_HEADER_LENGTH - session_data->inspection_section_length[source_id]; session_data->octets_seen[source_id] = 0; @@ -189,18 +225,18 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t session_data->continuation_expected[source_id] = true; session_data->header_octets_seen[source_id] = 0; - session_data->frames_aggregated[source_id] += 1; status = StreamSplitter::SEARCH; - data += frame_length + FRAME_HEADER_LENGTH; + data_pos = data + *flush_offset; + remaining_length = length - *flush_offset; } else if ( type == FT_CONTINUATION && session_data->continuation_expected[source_id]) { if (!(frame_flags & END_HEADERS)) { session_data->header_octets_seen[source_id] = 0; - session_data->frames_aggregated[source_id] += 1; status = StreamSplitter::SEARCH; - data += frame_length + FRAME_HEADER_LENGTH; + data_pos = data + *flush_offset; + remaining_length = length - *flush_offset; } else { @@ -217,17 +253,13 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t session_data->events[source_id]->create_event(EVENT_UNEXPECTED_CONTINUATION); status = StreamSplitter::ABORT; } - else if (session_data->continuation_expected[source_id]) - { - *session_data->infractions[source_id] += INF_MISSING_CONTINUATION; - session_data->events[source_id]->create_event(EVENT_MISSING_CONTINUATION); - status = StreamSplitter::ABORT; - } - } while (status != StreamSplitter::FLUSH && *flush_offset < length); + } while (status == StreamSplitter::SEARCH && remaining_length > 0); } return status; } +// FIXIT-M If there are any errors in header decoding, this currently tells stream not to send +// headers to detection. This behavior may need to be changed. const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned total, unsigned offset, const uint8_t* data, unsigned len, uint32_t flags, HttpCommon::SourceId source_id) @@ -377,13 +409,12 @@ const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned to if (type == FT_HEADERS || type == FT_CONTINUATION) { assert(session_data->http2_decoded_header[source_id] == nullptr); - session_data->http2_decoded_header[source_id] = new uint8_t[MAX_OCTETS]; //FIXIT-H This will eventually be the decoded header buffer. For now just copy //directly - memcpy(session_data->http2_decoded_header[source_id], - session_data->frame_data[source_id], session_data->frame_data_size[source_id]); - session_data->http2_decoded_header_size[source_id] = session_data->frame_data_size[source_id]; + if (!decode_headers(session_data, source_id, session_data->frame_data[source_id], + session_data->frame_data_size[source_id])) + return frame_buf; } } // Return 0-length non-null buffer to stream which signals detection required, but don't @@ -418,3 +449,297 @@ ValidationResult validate_preface(const uint8_t* data, const uint32_t length, return V_GOOD; } + +bool write_decoded_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* in_buffer, const uint32_t in_length, + uint8_t* decoded_header_buffer, uint32_t decoded_header_length, + uint32_t &bytes_written) +{ + bool ret = true; + uint32_t length = in_length; + bytes_written = 0; + + if (in_length > decoded_header_length) + { + length = MAX_OCTETS - session_data->http2_decoded_header_size[source_id]; + *session_data->infractions[source_id] += INF_DECODED_HEADER_BUFF_OUT_OF_SPACE; + session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2); + ret = false; + } + + memcpy((void*)decoded_header_buffer, (void*) in_buffer, length); + bytes_written = length; + return ret; +} + +bool decode_string_literal(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, + const Http2HpackStringDecode &decode_string, bool is_field_name, uint32_t &bytes_consumed, + uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, + uint32_t &bytes_written) +{ + uint32_t decoded_bytes_written; + uint32_t encoded_bytes_consumed; + uint32_t encoded_header_offset = 0; + bytes_written = 0; + bytes_consumed = 0; + + if (is_field_name) + { + // skip over parsed pattern and zeroed index + encoded_header_offset++; + bytes_consumed++; + } + + if (!decode_string.translate(encoded_header_buffer + encoded_header_offset, + encoded_header_length, encoded_bytes_consumed, decoded_header_buffer, + decoded_header_length, decoded_bytes_written, session_data->events[source_id], + session_data->infractions[source_id])) + { + return false; + } + + bytes_consumed += encoded_bytes_consumed; + bytes_written += decoded_bytes_written; + + if (is_field_name) + { + if (!write_decoded_headers(session_data, source_id, (const uint8_t*)": ", 2, + decoded_header_buffer + bytes_written, decoded_header_length - + bytes_written, decoded_bytes_written)) + return false; + } + else + { + if (!write_decoded_headers(session_data, source_id, (const uint8_t*)"\r\n", 2, + decoded_header_buffer + bytes_written, decoded_header_length - + bytes_written, decoded_bytes_written)) + return false; + } + + bytes_written += decoded_bytes_written; + + return true; +} + +// FIXIT-H implement +bool decode_static_table_index(void) +{ + return false; +} + +// FIXIT-H implement +bool decode_dynamic_table_index(void) +{ + return false; +} + +// FIXIT-H Will be incrementally updated to actually decode indexes. For now just copies encoded +// index directly to decoded_header_buffer +bool decode_index(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, + const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, + uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, + uint32_t &bytes_written) +{ + uint64_t index; + bytes_written = 0; + bytes_consumed = 0; + + if (!decode_int.translate(encoded_header_buffer, encoded_header_length, + bytes_consumed, index, session_data->events[source_id], + session_data->infractions[source_id])) + { + return false; + } + + if (index <= STATIC_TABLE_MAX_INDEX) + decode_static_table_index(); + else + decode_dynamic_table_index(); + + if (!write_decoded_headers(session_data, source_id, encoded_header_buffer, bytes_consumed, + decoded_header_buffer, decoded_header_length, bytes_written)) + return false; + + return true; +} + +bool decode_literal_header_line(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, + const uint8_t name_index_mask, const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, + uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, + uint32_t &bytes_written) +{ + bytes_written = 0; + bytes_consumed = 0; + uint32_t partial_bytes_consumed; + uint32_t partial_bytes_written; + + // indexed field name + if (encoded_header_buffer[0] & name_index_mask) + { + if (!decode_index(session_data, source_id, encoded_header_buffer, + encoded_header_length, decode_int, partial_bytes_consumed, + decoded_header_buffer, decoded_header_length, partial_bytes_written)) + return false; + } + // literal field name + else + { + if (!decode_string_literal(session_data, source_id, encoded_header_buffer, + encoded_header_length, Http2StreamSplitter::decode_string, true, + partial_bytes_consumed, decoded_header_buffer, decoded_header_length, + partial_bytes_written)) + return false; + } + + bytes_consumed += partial_bytes_consumed; + bytes_written += partial_bytes_written; + + // value is always literal + if (!decode_string_literal(session_data, source_id, encoded_header_buffer + + partial_bytes_consumed, encoded_header_length - partial_bytes_consumed, + Http2StreamSplitter::decode_string, false, partial_bytes_consumed, + decoded_header_buffer + partial_bytes_written, decoded_header_length - + partial_bytes_written, partial_bytes_written)) + return false; + + bytes_consumed += partial_bytes_consumed; + bytes_written += partial_bytes_written; + + return true; +} + +// FIXIT-M Will be updated to actually update dynamic table size. For now just skips over +bool handle_dynamic_size_update(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, + const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, uint32_t &bytes_written) +{ + uint64_t decoded_int; + uint32_t encoded_bytes_consumed; + bytes_consumed = 0; + bytes_written = 0; + + if (!decode_int.translate(encoded_header_buffer, encoded_header_length, + encoded_bytes_consumed, decoded_int, session_data->events[source_id], + session_data->infractions[source_id])) + { + return false; + } +#ifdef REG_TEST + //FIXIT-M remove when dynamic size updates are handled + if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2)) + { + fprintf(HttpTestManager::get_output_file(), + "Skipping HPACK dynamic size update: %lu\n", decoded_int); + } +#endif + bytes_consumed += encoded_bytes_consumed; + + return true; +} + +bool decode_header_line(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, + uint32_t& bytes_consumed, uint8_t* decoded_header_buffer, + const uint32_t decoded_header_length, uint32_t& bytes_written) +{ + const uint8_t index_mask = 0x80; + const uint8_t literal_index_mask = 0x40; + const uint8_t literal_index_name_index_mask = 0x3f; + const uint8_t literal_no_index_mask = 0xf0; + const uint8_t literal_never_index_pattern = 0x10; + const uint8_t literal_no_index_name_index_mask = 0x0f; + + // indexed header representation + if (encoded_header_buffer[0] & index_mask) + return decode_index(session_data, source_id, encoded_header_buffer, encoded_header_length, + Http2StreamSplitter::decode_int7, bytes_consumed, decoded_header_buffer, + decoded_header_length, bytes_written); + + // literal header representation to be added to dynamic table + else if (encoded_header_buffer[0] & literal_index_mask) + return decode_literal_header_line(session_data, source_id, encoded_header_buffer, + encoded_header_length, literal_index_name_index_mask, + Http2StreamSplitter::decode_int6, bytes_consumed, decoded_header_buffer, + decoded_header_length, bytes_written); + + // literal header field representation not to be added to dynamic table + // Note that this includes two representation types from the RFC - literal without index and + // literal never index. From a decoding standpoint these are identical. + else if ((encoded_header_buffer[0] & literal_no_index_mask) == 0 or + (encoded_header_buffer[0] & literal_no_index_mask) == literal_never_index_pattern) + return decode_literal_header_line(session_data, source_id, encoded_header_buffer, + encoded_header_length, literal_no_index_name_index_mask, + Http2StreamSplitter::decode_int4, bytes_consumed, decoded_header_buffer, + decoded_header_length, bytes_written); + else + // FIXIT-M dynamic table size update not yet supported, just skip + return handle_dynamic_size_update(session_data, source_id, encoded_header_buffer, + encoded_header_length, Http2StreamSplitter::decode_int5, bytes_consumed, bytes_written); +} + +//FIXIT-H This will eventually be the decoded header buffer. For now only string literals are +//decoded +bool decode_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id, + const uint8_t* encoded_header_buffer, const uint32_t header_length) +{ + + uint32_t total_bytes_consumed = 0; + uint32_t line_bytes_consumed = 0; + uint32_t line_bytes_written = 0; + bool success = true; + session_data->http2_decoded_header[source_id] = new uint8_t[MAX_OCTETS]; + session_data->http2_decoded_header_size[source_id] = 0; + + while (total_bytes_consumed < header_length) + { + if (!decode_header_line(session_data, source_id, + encoded_header_buffer + total_bytes_consumed, header_length - total_bytes_consumed, + line_bytes_consumed, session_data->http2_decoded_header[source_id] + + session_data->http2_decoded_header_size[source_id], MAX_OCTETS - + session_data->http2_decoded_header_size[source_id], line_bytes_written)) + { + success = false; + break; + } + total_bytes_consumed += line_bytes_consumed; + session_data->http2_decoded_header_size[source_id] += line_bytes_written; + } + + if (!success) + { +#ifdef REG_TEST + if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2)) + { + fprintf(HttpTestManager::get_output_file(), + "Error decoding headers. "); + if(session_data->http2_decoded_header_size[source_id] > 0) + Field(session_data->http2_decoded_header_size[source_id], + session_data->http2_decoded_header[source_id]).print( + HttpTestManager::get_output_file(), "Partially Decoded Header"); + } +#endif + return false; + } + + // write the last crlf to end the header + if (!write_decoded_headers(session_data, source_id, (const uint8_t*)"\r\n", 2, + session_data->http2_decoded_header[source_id] + + session_data->http2_decoded_header_size[source_id], MAX_OCTETS - + session_data->http2_decoded_header_size[source_id], line_bytes_written)) + return false; + session_data->http2_decoded_header_size[source_id] += line_bytes_written; + +#ifdef REG_TEST + if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2)) + { + Field(session_data->http2_decoded_header_size[source_id], + session_data->http2_decoded_header[source_id]).print(HttpTestManager::get_output_file(), + "Decoded Header"); + } +#endif + + return success; +} diff --git a/src/service_inspectors/http2_inspect/http2_tables.cc b/src/service_inspectors/http2_inspect/http2_tables.cc index 8620f5f6e..510df8ccb 100644 --- a/src/service_inspectors/http2_inspect/http2_tables.cc +++ b/src/service_inspectors/http2_inspect/http2_tables.cc @@ -35,6 +35,7 @@ const snort::RuleMap Http2Module::http2_events[] = { EVENT_STRING_DECODE_FAILURE, "error in HPACK string value" }, { EVENT_MISSING_CONTINUATION, "missing continuation frame"}, { EVENT_UNEXPECTED_CONTINUATION, "unexpected continuation frame"}, + { EVENT_MISFORMATTED_HTTP2, "misformatted HTTP/2 traffic"}, { EVENT_PREFACE_MATCH_FAILURE, "HTTP/2 connection preface does not match"}, { 0, nullptr } }; diff --git a/src/service_inspectors/http2_inspect/test/CMakeLists.txt b/src/service_inspectors/http2_inspect/test/CMakeLists.txt index 81320eea2..0b312e509 100644 --- a/src/service_inspectors/http2_inspect/test/CMakeLists.txt +++ b/src/service_inspectors/http2_inspect/test/CMakeLists.txt @@ -9,6 +9,9 @@ add_cpputest( http2_inspect_impl_test add_cpputest( http2_stream_splitter_impl_test SOURCES ../http2_flow_data.cc + ../http2_hpack_int_decode.cc + ../http2_hpack_string_decode.cc + ../http2_huffman_state_machine.cc ../http2_stream_splitter_impl.cc ../http2_module.cc ../http2_tables.cc diff --git a/src/service_inspectors/http2_inspect/test/http2_hpack_int_decode_test.cc b/src/service_inspectors/http2_inspect/test/http2_hpack_int_decode_test.cc index 706f1f8b2..411d12c57 100644 --- a/src/service_inspectors/http2_inspect/test/http2_hpack_int_decode_test.cc +++ b/src/service_inspectors/http2_inspect/test/http2_hpack_int_decode_test.cc @@ -44,7 +44,7 @@ TEST_GROUP(http2_hpack_int_decode_success) { Http2EventGen events; Http2Infractions inf; - Http2HpackIntDecode* const decode = new Http2HpackIntDecode(5, &events, &inf); + Http2HpackIntDecode* const decode = new Http2HpackIntDecode(5); void teardown() override { @@ -61,7 +61,7 @@ TEST(http2_hpack_int_decode_success, 10_using_5_bits) // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(&buf, 1, bytes_processed, res); + bool success = decode->translate(&buf, 1, bytes_processed, res, &events, &inf); // check results CHECK(success == true); CHECK(res == 10); @@ -75,7 +75,7 @@ TEST(http2_hpack_int_decode_success, 10_using_5_bits_wtail) // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(buf, 2, bytes_processed, res); + bool success = decode->translate(buf, 2, bytes_processed, res, &events, &inf); // check results CHECK(success == true); CHECK(res == 10); @@ -89,7 +89,7 @@ TEST(http2_hpack_int_decode_success, 1337_using_5_bits) // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(buf, 3, bytes_processed, res); + bool success = decode->translate(buf, 3, bytes_processed, res, &events, &inf); // check results CHECK(success == true); CHECK(res == 1337); @@ -99,13 +99,13 @@ TEST(http2_hpack_int_decode_success, 1337_using_5_bits) TEST(http2_hpack_int_decode_success, 42_using_8_bits) { // prepare decode object - Http2HpackIntDecode decode_8(8, &events, &inf); + Http2HpackIntDecode decode_8(8); // prepare buf to decode - example from RFC 7541 c.1.3 uint8_t buf = 42; // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode_8.translate(&buf, 1, bytes_processed, res); + bool success = decode_8.translate(&buf, 1, bytes_processed, res, &events, &inf); // check results CHECK(success == true); CHECK(res == 42); @@ -119,7 +119,7 @@ TEST(http2_hpack_int_decode_success, max_val_using_5_bit) // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(buf, 11, bytes_processed, res); + bool success = decode->translate(buf, 11, bytes_processed, res, &events, &inf); // check results CHECK(success == true); CHECK(res == 0xFFFFFFFFFFFFFFFF); @@ -133,7 +133,7 @@ TEST(http2_hpack_int_decode_success, 31_using_5_bits) // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(buf, 2, bytes_processed, res); + bool success = decode->translate(buf, 2, bytes_processed, res, &events, &inf); // check results CHECK(success == true); CHECK(res == 31); @@ -147,7 +147,7 @@ TEST(http2_hpack_int_decode_success, 0_using_5_bits) // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(&buf, 1, bytes_processed, res); + bool success = decode->translate(&buf, 1, bytes_processed, res, &events, &inf); // check results CHECK(success == true); CHECK(res == 0); @@ -166,13 +166,13 @@ TEST(http2_hpack_int_decode_failure, 0_len_field) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackIntDecode decode_8(8, &local_events, &local_inf); + Http2HpackIntDecode decode_8(8); // prepare buf to decode - use buf length 0 uint8_t buf = 42; // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode_8.translate(&buf, 0, bytes_processed, res); + bool success = decode_8.translate(&buf, 0, bytes_processed, res, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 0); @@ -185,13 +185,13 @@ TEST(http2_hpack_int_decode_failure, too_short) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackIntDecode local_decode(5, &local_events, &local_inf); + Http2HpackIntDecode local_decode(5); // prepare buf to decode - buffer ends before decode finished uint8_t buf[3] = { 31, 0x9a, 10 }; // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(buf, 2, bytes_processed, res); + bool success = local_decode.translate(buf, 2, bytes_processed, res, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 2); @@ -204,13 +204,13 @@ TEST(http2_hpack_int_decode_failure, multiplier_bigger_than_63) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackIntDecode local_decode(5, &local_events, &local_inf); + Http2HpackIntDecode local_decode(5); // prepare buf to decode - multiplier > 63 uint8_t buf[13] = { 31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 1 }; // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(buf, 13, bytes_processed, res); + bool success = local_decode.translate(buf, 13, bytes_processed, res, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 11); @@ -223,13 +223,13 @@ TEST(http2_hpack_int_decode_failure, add_val_overflow) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackIntDecode local_decode(5, &local_events, &local_inf); + Http2HpackIntDecode local_decode(5); // prepare buf to decode - value to add itself is already creating overflow uint8_t buf[12] = { 31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF, 1 }; // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(buf, 12, bytes_processed, res); + bool success = local_decode.translate(buf, 12, bytes_processed, res, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 11); @@ -242,13 +242,13 @@ TEST(http2_hpack_int_decode_failure, add_val_overflow2) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackIntDecode local_decode(5, &local_events, &local_inf); + Http2HpackIntDecode local_decode(5); // prepare buf to decode - adding value to result kept so far creates overflow uint8_t buf[11] = { 31, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1 }; // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(buf, 11, bytes_processed, res); + bool success = local_decode.translate(buf, 11, bytes_processed, res, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 11); @@ -261,13 +261,13 @@ TEST(http2_hpack_int_decode_failure, 2_64_using_5_bit) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackIntDecode local_decode(5, &local_events, &local_inf); + Http2HpackIntDecode local_decode(5); // prepare buf to decode - 2^64 uint8_t buf[11] = { 31, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1 }; // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(buf, 11, bytes_processed, res); + bool success = local_decode.translate(buf, 11, bytes_processed, res, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 11); @@ -288,13 +288,13 @@ TEST(http2_hpack_int_decode_leading_zeros, leading_zeros) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackIntDecode local_decode(5, &local_events, &local_inf); + Http2HpackIntDecode local_decode(5); // prepare buf to decode - MSB is zero uint8_t buf[3] = { 31, 0x80, 0 }; // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(buf, 3, bytes_processed, res); + bool success = local_decode.translate(buf, 3, bytes_processed, res, &local_events, &local_inf); // check results CHECK(success == true); CHECK(res == 31); @@ -308,14 +308,14 @@ TEST(http2_hpack_int_decode_leading_zeros, leading_0_byte_11) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackIntDecode local_decode(5, &local_events, &local_inf); + Http2HpackIntDecode local_decode(5); // prepare buf to decode - multiplier 63 doesn't create overflow, should alert on // leading 0 uint8_t buf[11] = { 31, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0 }; // decode uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(buf, 11, bytes_processed, res); + bool success = local_decode.translate(buf, 11, bytes_processed, res, &local_events, &local_inf); // check results CHECK(success == true); CHECK(res == 0x7FFFFFFFFFFFFFFF); diff --git a/src/service_inspectors/http2_inspect/test/http2_hpack_string_decode_test.cc b/src/service_inspectors/http2_inspect/test/http2_hpack_string_decode_test.cc index 16e4eff08..a6b5867ca 100644 --- a/src/service_inspectors/http2_inspect/test/http2_hpack_string_decode_test.cc +++ b/src/service_inspectors/http2_inspect/test/http2_hpack_string_decode_test.cc @@ -48,7 +48,7 @@ TEST_GROUP(http2_hpack_string_decode_success) { Http2EventGen events; Http2Infractions inf; - Http2HpackStringDecode* const decode = new Http2HpackStringDecode(&events, &inf); + Http2HpackStringDecode* const decode = new Http2HpackStringDecode(); void teardown() override { @@ -65,7 +65,7 @@ TEST(http2_hpack_string_decode_success, custom_key_len_10) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[10]; - bool success = decode->translate(buf, 11, bytes_processed, res, 10, bytes_written); + bool success = decode->translate(buf, 11, bytes_processed, res, 10, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(memcmp(res, "custom-key", 10) == 0); @@ -80,7 +80,7 @@ TEST(http2_hpack_string_decode_success, custom_key_len_10_wtail) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[12]; - bool success = decode->translate(buf, 12, bytes_processed, res, 12, bytes_written); + bool success = decode->translate(buf, 12, bytes_processed, res, 12, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(memcmp(res, "custom-key", 10) == 0); @@ -98,7 +98,7 @@ TEST(http2_hpack_string_decode_success, int_is_more_than_1_byte) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[130]; - bool success = decode->translate(buf, 130, bytes_processed, res, 130, bytes_written); + bool success = decode->translate(buf, 130, bytes_processed, res, 130, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 130); @@ -113,7 +113,7 @@ TEST(http2_hpack_string_decode_success, empty_string) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res = 10; // random value, just to check it wasn't overwritten - bool success = decode->translate(&buf, 1, bytes_processed, &res, 1, bytes_written); + bool success = decode->translate(&buf, 1, bytes_processed, &res, 1, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 1); @@ -128,7 +128,7 @@ TEST(http2_hpack_string_decode_success, string_len_1) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res = 0; - bool success = decode->translate(buf, 2, bytes_processed, &res, 1, bytes_written); + bool success = decode->translate(buf, 2, bytes_processed, &res, 1, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 2); @@ -149,7 +149,7 @@ TEST(http2_hpack_string_decode_success, max_field_length) uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[MAX_OCTETS]; bool success = decode->translate(buf, MAX_OCTETS, bytes_processed, res, - MAX_OCTETS, bytes_written); + MAX_OCTETS, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == MAX_OCTETS); @@ -164,7 +164,7 @@ TEST(http2_hpack_string_decode_success, huffman_1_byte) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[1]; - bool success = decode->translate(buf, 2, bytes_processed, res, 1, bytes_written); + bool success = decode->translate(buf, 2, bytes_processed, res, 1, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 2); @@ -179,7 +179,7 @@ TEST(http2_hpack_string_decode_success, huffman_1_byte_star) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[1]; - bool success = decode->translate(buf, 2, bytes_processed, res, 1, bytes_written); + bool success = decode->translate(buf, 2, bytes_processed, res, 1, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 2); @@ -194,7 +194,7 @@ TEST(http2_hpack_string_decode_success, huffman_2_bytes_aligned) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[3]; - bool success = decode->translate(buf, 3, bytes_processed, res, 3, bytes_written); + bool success = decode->translate(buf, 3, bytes_processed, res, 3, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 3); @@ -209,7 +209,7 @@ TEST(http2_hpack_string_decode_success, huffman_2_bytes_unaligned) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[3]; - bool success = decode->translate(buf, 3, bytes_processed, res, 3, bytes_written); + bool success = decode->translate(buf, 3, bytes_processed, res, 3, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 3); @@ -224,7 +224,7 @@ TEST(http2_hpack_string_decode_success, huffman_rfc_example1) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[19]; - bool success = decode->translate(buf, 13, bytes_processed, res, 19, bytes_written); + bool success = decode->translate(buf, 13, bytes_processed, res, 19, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 13); @@ -239,7 +239,7 @@ TEST(http2_hpack_string_decode_success, huffman_rfc_example2) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[9]; - bool success = decode->translate(buf, 7, bytes_processed, res, 9, bytes_written); + bool success = decode->translate(buf, 7, bytes_processed, res, 9, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 7); @@ -254,7 +254,7 @@ TEST(http2_hpack_string_decode_success, huffman_rfc_example3) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[12]; - bool success = decode->translate(buf, 9, bytes_processed, res, 12, bytes_written); + bool success = decode->translate(buf, 9, bytes_processed, res, 12, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 9); @@ -269,7 +269,7 @@ TEST(http2_hpack_string_decode_success, huffman_rfc_example4) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[14]; - bool success = decode->translate(buf, 10, bytes_processed, res, 14, bytes_written); + bool success = decode->translate(buf, 10, bytes_processed, res, 14, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 10); @@ -285,7 +285,7 @@ TEST(http2_hpack_string_decode_success, huffman_rfc_example5) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[35]; - bool success = decode->translate(buf, 23, bytes_processed, res, 35, bytes_written); + bool success = decode->translate(buf, 23, bytes_processed, res, 35, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 23); @@ -303,7 +303,7 @@ TEST(http2_hpack_string_decode_success, huffman_rfc_example6) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[73]; - bool success = decode->translate(buf, 46, bytes_processed, res, 73, bytes_written); + bool success = decode->translate(buf, 46, bytes_processed, res, 73, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 46); @@ -318,7 +318,7 @@ TEST(http2_hpack_string_decode_success, huffman_unaligned_then_aligned) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[6]; - bool success = decode->translate(buf, 5, bytes_processed, res, 6, bytes_written); + bool success = decode->translate(buf, 5, bytes_processed, res, 6, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 5); @@ -334,7 +334,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_1) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[32]; - bool success = decode->translate(buf, 21, bytes_processed, res, 32, bytes_written); + bool success = decode->translate(buf, 21, bytes_processed, res, 32, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 21); @@ -350,7 +350,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_2) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[36]; - bool success = decode->translate(buf, 24, bytes_processed, res, 36, bytes_written); + bool success = decode->translate(buf, 24, bytes_processed, res, 36, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 24); @@ -365,7 +365,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_3) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[12]; - bool success = decode->translate(buf, 9, bytes_processed, res, 12, bytes_written); + bool success = decode->translate(buf, 9, bytes_processed, res, 12, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 9); @@ -380,7 +380,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_4) // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[70]; - bool success = decode->translate(buf, 45, bytes_processed, res, 70, bytes_written); + bool success = decode->translate(buf, 45, bytes_processed, res, 70, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 45); @@ -396,7 +396,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[88]; uint8_t expected[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; - bool success = decode->translate(buf, 55, bytes_processed, res, 88, bytes_written); + bool success = decode->translate(buf, 55, bytes_processed, res, 88, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 55); @@ -412,7 +412,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[93]; uint8_t expected[16] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; - bool success = decode->translate(buf, 58, bytes_processed, res, 93, bytes_written); + bool success = decode->translate(buf, 58, bytes_processed, res, 93, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 58); @@ -428,7 +428,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[79]; uint8_t expected[17] = {0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F}; - bool success = decode->translate(buf, 49, bytes_processed, res, 79, bytes_written); + bool success = decode->translate(buf, 49, bytes_processed, res, 79, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 49); @@ -445,7 +445,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[76]; uint8_t expected[16] = {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F}; - bool success = decode->translate(buf, 47, bytes_processed, res, 76, bytes_written); + bool success = decode->translate(buf, 47, bytes_processed, res, 76, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 47); @@ -461,7 +461,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[74]; uint8_t expected[16] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF}; - bool success = decode->translate(buf, 46, bytes_processed, res, 74, bytes_written); + bool success = decode->translate(buf, 46, bytes_processed, res, 74, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 46); @@ -478,7 +478,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[73]; uint8_t expected[16] = {0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF}; - bool success = decode->translate(buf, 45, bytes_processed, res, 73, bytes_written); + bool success = decode->translate(buf, 45, bytes_processed, res, 73, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 45); @@ -495,7 +495,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[80]; uint8_t expected[16] = {0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF}; - bool success = decode->translate(buf, 50, bytes_processed, res, 80, bytes_written); + bool success = decode->translate(buf, 50, bytes_processed, res, 80, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 50); @@ -512,7 +512,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[82]; uint8_t expected[16] = {0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF}; - bool success = decode->translate(buf, 51, bytes_processed, res, 82, bytes_written); + bool success = decode->translate(buf, 51, bytes_processed, res, 82, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 51); @@ -529,7 +529,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[76]; uint8_t expected[16] = {0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF}; - bool success = decode->translate(buf, 47, bytes_processed, res, 76, bytes_written); + bool success = decode->translate(buf, 47, bytes_processed, res, 76, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 47); @@ -546,7 +546,7 @@ TEST(http2_hpack_string_decode_success, huffman_decoding_all_possible_symbols_he uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[88]; uint8_t expected[16] = {0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF}; - bool success = decode->translate(buf, 55, bytes_processed, res, 88, bytes_written); + bool success = decode->translate(buf, 55, bytes_processed, res, 88, bytes_written, &events, &inf); // check results CHECK(success == true); CHECK(bytes_processed == 55); @@ -566,13 +566,13 @@ TEST(http2_hpack_string_decode_infractions, 0_len_field) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackStringDecode local_decode(&local_events, &local_inf); + Http2HpackStringDecode local_decode; // prepare buf to decode - use field length 0 uint8_t buf = 0; // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res; - bool success = local_decode.translate(&buf, 0, bytes_processed, &res, 1, bytes_written); + bool success = local_decode.translate(&buf, 0, bytes_processed, &res, 1, bytes_written, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 0); @@ -586,13 +586,13 @@ TEST(http2_hpack_string_decode_infractions, missing_bytes) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackStringDecode local_decode(&local_events, &local_inf); + Http2HpackStringDecode local_decode; // prepare buf to decode - length is 1, no string uint8_t buf = 1; // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[2]; - bool success = local_decode.translate(&buf, 1, bytes_processed, res, 2, bytes_written); + bool success = local_decode.translate(&buf, 1, bytes_processed, res, 2, bytes_written, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_written == 0); @@ -606,13 +606,13 @@ TEST(http2_hpack_string_decode_infractions, bad_int) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackStringDecode local_decode(&local_events, &local_inf); + Http2HpackStringDecode local_decode; // prepare buf to decode - bad int uint8_t buf[2] = { 0x7f, 0x80 }; // decode uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[2]; - bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written); + bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 2); @@ -626,7 +626,7 @@ TEST(http2_hpack_string_decode_infractions, max_field_length_plus_1) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackStringDecode local_decode(&local_events, &local_inf); + Http2HpackStringDecode local_decode; // prepare buf to decode - int + string == MAX_OCTETS+1 (Field limitation + 1) uint8_t buf[MAX_OCTETS]; buf[0] = 0x7F; @@ -638,7 +638,7 @@ TEST(http2_hpack_string_decode_infractions, max_field_length_plus_1) uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[MAX_OCTETS]; bool success = local_decode.translate(buf, MAX_OCTETS, bytes_processed, res, - MAX_OCTETS, bytes_written); + MAX_OCTETS, bytes_written, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 4); @@ -652,7 +652,7 @@ TEST(http2_hpack_string_decode_infractions, out_buf_out_of_space) // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; - Http2HpackStringDecode local_decode(&local_events, &local_inf); + Http2HpackStringDecode local_decode; // prepare buf to decode uint8_t buf[MAX_OCTETS]; buf[0] = 0x7F; @@ -664,13 +664,13 @@ TEST(http2_hpack_string_decode_infractions, out_buf_out_of_space) uint32_t bytes_processed = 0, bytes_written = 0; uint8_t res[MAX_OCTETS-5]; bool success = local_decode.translate(buf, MAX_OCTETS, bytes_processed, res, - MAX_OCTETS-5, bytes_written); + MAX_OCTETS-5, bytes_written, &local_events, &local_inf); // check results CHECK(success == false); CHECK(bytes_processed == 4); CHECK(bytes_written == 0); - CHECK(local_inf.get_raw() == (1< #include +#include + using namespace snort; using namespace HttpCommon; using namespace Http2Enums; @@ -44,6 +47,7 @@ using namespace Http2Enums; unsigned HttpTestManager::test_input = IN_NONE; unsigned HttpTestManager::test_output = IN_NONE; int DetectionEngine::queue_event(unsigned int, unsigned int, Actions::Type) { return 0; } +void Field::print(FILE*, char const*) const {} TEST_GROUP(http2_get_buf_test) { diff --git a/src/service_inspectors/http2_inspect/test/http2_stream_splitter_impl_test.cc b/src/service_inspectors/http2_inspect/test/http2_stream_splitter_impl_test.cc index 85f7d8460..50b5d14ec 100644 --- a/src/service_inspectors/http2_inspect/test/http2_stream_splitter_impl_test.cc +++ b/src/service_inspectors/http2_inspect/test/http2_stream_splitter_impl_test.cc @@ -23,13 +23,15 @@ #include "config.h" #endif -#include "service_inspectors/http2_inspect/http2_stream_splitter.h" #include "protocols/packet.h" #include "service_inspectors/http_inspect/http_common.h" #include "service_inspectors/http_inspect/http_test_manager.h" #include "service_inspectors/http2_inspect/http2_enum.h" #include "service_inspectors/http2_inspect/http2_flow_data.h" +#include "service_inspectors/http2_inspect/http2_hpack_int_decode.h" +#include "service_inspectors/http2_inspect/http2_hpack_string_decode.h" +#include "service_inspectors/http2_inspect/http2_stream_splitter.h" #include "http2_flow_data_test.h" @@ -44,7 +46,9 @@ using namespace Http2Enums; // Stubs whose sole purpose is to make the test code link unsigned HttpTestManager::test_input = IN_NONE; unsigned HttpTestManager::test_output = IN_NONE; +FILE* HttpTestManager::test_out = nullptr ; int DetectionEngine::queue_event(unsigned int, unsigned int, Actions::Type) { return 0; } +void Field::print(_IO_FILE*, char const*) const {} TEST_GROUP(http2_scan_test) {