From: Mike Stepanek (mstepane) Date: Mon, 13 Jan 2020 14:27:15 +0000 (+0000) Subject: Merge pull request #1919 in SNORT/snort3 from ~MDAGON/snort3:integration to master X-Git-Tag: 3.0.0-268~61 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=377c4ab99a8f7a428f0ebcc5938db3377e4858d3;p=thirdparty%2Fsnort3.git Merge pull request #1919 in SNORT/snort3 from ~MDAGON/snort3:integration to master Squashed commit of the following: commit 5135fa3bf40d3e6ae2e8e12ff92f4ed79d264fb6 Author: Mike Stepanek (mstepane) Date: Mon Dec 9 17:08:40 2019 +0000 http2_inspect: integration with NHI --- diff --git a/src/service_inspectors/http2_inspect/http2_enum.h b/src/service_inspectors/http2_inspect/http2_enum.h index 37c822c7f..c305228bf 100644 --- a/src/service_inspectors/http2_inspect/http2_enum.h +++ b/src/service_inspectors/http2_inspect/http2_enum.h @@ -27,6 +27,7 @@ namespace Http2Enums static const int MAX_OCTETS = 63780; static const int DATA_SECTION_SIZE = 16384; static const int FRAME_HEADER_LENGTH = 9; +static const uint32_t NO_STREAM_ID = 0xFFFFFFFF; static const uint32_t HTTP2_GID = 121; diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.cc b/src/service_inspectors/http2_inspect/http2_flow_data.cc index 57540a803..9e9cbee37 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.cc +++ b/src/service_inspectors/http2_inspect/http2_flow_data.cc @@ -23,6 +23,7 @@ #include "http2_flow_data.h" +#include "service_inspectors/http_inspect/http_inspect.h" #include "service_inspectors/http_inspect/http_test_manager.h" #include "http2_enum.h" @@ -41,8 +42,17 @@ unsigned Http2FlowData::inspector_id = 0; uint64_t Http2FlowData::instance_count = 0; #endif -Http2FlowData::Http2FlowData() : FlowData(inspector_id), stream(new Http2Stream(this)) +Http2FlowData::Http2FlowData(Flow* flow_) : + FlowData(inspector_id), + flow(flow_), + hi((HttpInspect*)(flow->assistant_gadget)) { + if (hi != nullptr) + { + hi_ss[SRC_CLIENT] = hi->get_splitter(true); + hi_ss[SRC_SERVER] = hi->get_splitter(false); + } + #ifdef REG_TEST seq_num = ++instance_count; if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2) && @@ -75,8 +85,68 @@ Http2FlowData::~Http2FlowData() { delete infractions[k]; delete events[k]; + delete hi_ss[k]; } +} + +HttpFlowData* Http2FlowData::get_hi_flow_data() const +{ + assert(stream_in_hi != Http2Enums::NO_STREAM_ID); + Http2Stream* stream = get_hi_stream(); + return stream->get_hi_flow_data(); +} + +void Http2FlowData::set_hi_flow_data(HttpFlowData* flow) +{ + assert(stream_in_hi != Http2Enums::NO_STREAM_ID); + Http2Stream* stream = get_hi_stream(); + stream->set_hi_flow_data(flow); +} + +HttpMsgSection* Http2FlowData::get_hi_msg_section() const +{ + Http2Stream* stream = get_hi_stream(); + if (stream == nullptr) + return nullptr; + return stream->get_hi_msg_section(); +} + +void Http2FlowData::set_hi_msg_section(HttpMsgSection* section) +{ + assert(stream_in_hi != Http2Enums::NO_STREAM_ID); + Http2Stream* stream = get_hi_stream(); + stream->set_hi_msg_section(section); +} - delete stream; +class Http2Stream* Http2FlowData::find_stream(uint32_t key) const +{ + for (const StreamInfo& stream_info : streams) + { + if (stream_info.id == key) + return stream_info.stream; + } + + return nullptr; +} + +class Http2Stream* Http2FlowData::get_stream(uint32_t key) +{ + class Http2Stream* stream = find_stream(key); + if (!stream) + { + stream = new Http2Stream(key, this); + streams.emplace_front(key, stream); + } + return stream; +} + +class Http2Stream* Http2FlowData::get_hi_stream() const +{ + return find_stream(stream_in_hi); +} + +class Http2Stream* Http2FlowData::get_current_stream(const HttpCommon::SourceId source_id) +{ + return get_stream(current_stream[source_id]); } diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.h b/src/service_inspectors/http2_inspect/http2_flow_data.h index 669319249..9a239ba79 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.h +++ b/src/service_inspectors/http2_inspect/http2_flow_data.h @@ -35,20 +35,32 @@ #include "http2_hpack_int_decode.h" #include "http2_hpack_string_decode.h" #include "http2_settings_frame.h" +#include "http2_stream.h" using Http2Infractions = Infractions; using Http2EventGen = EventGen; +class HttpFlowData; +class HttpMsgSection; +class HttpInspect; +class HttpStreamSplitter; + class Http2FlowData : public snort::FlowData { public: - Http2FlowData(); + Http2FlowData(snort::Flow* flow_); ~Http2FlowData() override; static unsigned inspector_id; static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + // Used by http_inspect to store its stuff + HttpFlowData* get_hi_flow_data() const; + void set_hi_flow_data(HttpFlowData* flow); + HttpMsgSection* get_hi_msg_section() const; + void set_hi_msg_section(HttpMsgSection* section); + friend class Http2Frame; friend class Http2HeadersFrame; friend class Http2Hpack; @@ -67,9 +79,31 @@ public: size_t size_of() override { return sizeof(*this); } + // Stream access + class StreamInfo + { + public: + const uint32_t id; + class Http2Stream* stream; + + StreamInfo(uint32_t _id, class Http2Stream* ptr) : id(_id), stream(ptr) { assert(ptr); } + ~StreamInfo() { delete stream; } + }; + class Http2Stream* get_current_stream(const HttpCommon::SourceId source_id); + protected: + snort::Flow* flow; + HttpInspect* const hi; + HttpStreamSplitter* hi_ss[2] = { nullptr, nullptr }; + // 0 element refers to client frame, 1 element refers to server frame + // Stream ID of the frame currently being read in and processed + uint32_t current_stream[2] = { Http2Enums::NO_STREAM_ID, Http2Enums::NO_STREAM_ID }; + // At any given time there may be different streams going in each direction. But only one of + // them is the stream that http_inspect is actually processing at the moment. + uint32_t stream_in_hi = Http2Enums::NO_STREAM_ID; + // Reassemble() data to eval() uint8_t* frame_header[2] = { nullptr, nullptr }; uint32_t frame_header_size[2] = { 0, 0 }; @@ -80,7 +114,7 @@ protected: bool frame_in_detection = false; Http2ConnectionSettings connection_settings[2]; Http2HpackDecoder hpack_decoder[2]; - class Http2Stream* stream; + std::list streams; // Internal to scan() bool preface[2] = { true, false }; @@ -115,6 +149,11 @@ protected: static uint64_t instance_count; uint64_t seq_num; #endif + +private: + class Http2Stream* find_stream(uint32_t key) const; + class Http2Stream* get_stream(uint32_t key); + class Http2Stream* get_hi_stream() const; }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_frame.cc b/src/service_inspectors/http2_inspect/http2_frame.cc index bde7796fb..e4453148d 100644 --- a/src/service_inspectors/http2_inspect/http2_frame.cc +++ b/src/service_inspectors/http2_inspect/http2_frame.cc @@ -48,20 +48,17 @@ Http2Frame::Http2Frame(const uint8_t* header_buffer, const int32_t header_len, Http2Frame* Http2Frame::new_frame(const uint8_t* header, const int32_t header_len, const uint8_t* data, const int32_t data_len, Http2FlowData* session_data, SourceId source_id) { - //FIXIT-H call the appropriate frame subclass constructor based on the type + // FIXIT-H call the appropriate frame subclass constructor based on the type switch(session_data->frame_type[source_id]) { case FT_HEADERS: return new Http2HeadersFrame(header, header_len, data, data_len, session_data, source_id); - break; case FT_SETTINGS: return new Http2SettingsFrame(header, header_len, data, data_len, session_data, source_id); - break; default: return new Http2Frame(header, header_len, data, data_len, session_data, source_id); - break; } } diff --git a/src/service_inspectors/http2_inspect/http2_frame.h b/src/service_inspectors/http2_inspect/http2_frame.h index 5fe1f5d4d..d1748da82 100644 --- a/src/service_inspectors/http2_inspect/http2_frame.h +++ b/src/service_inspectors/http2_inspect/http2_frame.h @@ -40,7 +40,7 @@ public: static Http2Frame* new_frame(const uint8_t* header_buffer, const int32_t header_len, const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, HttpCommon::SourceId source_id); - + virtual void clear() { } virtual const Field& get_buf(unsigned id); #ifdef REG_TEST virtual void print_frame(FILE* output); diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame.cc b/src/service_inspectors/http2_inspect/http2_headers_frame.cc index 4b8e23850..21b758b63 100644 --- a/src/service_inspectors/http2_inspect/http2_headers_frame.cc +++ b/src/service_inspectors/http2_inspect/http2_headers_frame.cc @@ -23,18 +23,24 @@ #include "http2_headers_frame.h" +#include "protocols/packet.h" + +#include "service_inspectors/http_inspect/http_inspect.h" +#include "service_inspectors/http_inspect/http_stream_splitter.h" + #include "http2_enum.h" #include "http2_flow_data.h" #include "http2_hpack.h" #include "http2_start_line.h" +using namespace snort; using namespace HttpCommon; using namespace Http2Enums; Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t header_len, - const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* ssn_data, - HttpCommon::SourceId src_id) : Http2Frame(header_buffer, header_len, data_buffer, data_len, - ssn_data, src_id) + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data_, + HttpCommon::SourceId source_id_) : + Http2Frame(header_buffer, header_len, data_buffer, data_len, session_data_, source_id_) { uint8_t hpack_headers_offset = 0; @@ -43,41 +49,130 @@ Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t hpack_headers_offset = 5; // Set up the decoding context - hpack_decoder = &session_data->hpack_decoder[source_id]; + Http2HpackDecoder& hpack_decoder = session_data->hpack_decoder[source_id]; // Allocate stuff decoded_headers = new uint8_t[MAX_OCTETS]; - decoded_headers_size = 0; start_line_generator = Http2StartLine::new_start_line_generator(source_id, session_data->events[source_id], session_data->infractions[source_id]); // Decode headers - if (!hpack_decoder->decode_headers((data.start() + hpack_headers_offset), data.length() - - hpack_headers_offset, decoded_headers, &decoded_headers_size, start_line_generator, - session_data->events[source_id], session_data->infractions[source_id])) + if (!hpack_decoder.decode_headers((data.start() + hpack_headers_offset), data.length() - + hpack_headers_offset, decoded_headers, + start_line_generator, session_data->events[source_id], + session_data->infractions[source_id])) { session_data->frame_type[source_id] = FT__ABORT; error_during_decode = true; } - start_line = hpack_decoder->get_start_line(); - http2_decoded_header = hpack_decoder->get_decoded_headers(decoded_headers); + start_line = hpack_decoder.get_start_line(); + http1_header = hpack_decoder.get_decoded_headers(decoded_headers); + + if ((error_during_decode) || (session_data->hi_ss[source_id] == nullptr)) + return; + + // http_inspect scan() of start line + session_data->stream_in_hi = session_data->current_stream[source_id]; + { + uint32_t flush_offset; + Packet dummy_pkt(false); + dummy_pkt.flow = session_data->flow; + const uint32_t unused = 0; + const StreamSplitter::Status start_scan_result = + session_data->hi_ss[source_id]->scan(&dummy_pkt, start_line->start(), + start_line->length(), unused, &flush_offset); + assert(start_scan_result == StreamSplitter::FLUSH); + UNUSED(start_scan_result); + assert((int64_t)flush_offset == start_line->length()); + } + + StreamBuffer stream_buf; + // http_inspect reassemble() of start line + { + unsigned copied; + stream_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow, + start_line->length(), 0, start_line->start(), start_line->length(), PKT_PDU_TAIL, + copied); + assert(stream_buf.data != nullptr); + assert(copied == (unsigned)start_line->length()); + } + + // http_inspect eval() and clear() of start line + { + Packet dummy_pkt(false); + dummy_pkt.flow = session_data->flow; + dummy_pkt.packet_flags = (source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER; + dummy_pkt.dsize = stream_buf.length; + dummy_pkt.data = stream_buf.data; + session_data->hi->eval(&dummy_pkt); + session_data->hi->clear(&dummy_pkt); + } + + // http_inspect scan() of headers + { + uint32_t flush_offset; + Packet dummy_pkt(false); + dummy_pkt.flow = session_data->flow; + const uint32_t unused = 0; + const StreamSplitter::Status header_scan_result = + session_data->hi_ss[source_id]->scan(&dummy_pkt, http1_header->start(), + http1_header->length(), unused, &flush_offset); + if (header_scan_result == StreamSplitter::ABORT) + { + // eval() aborted the start line? + hi_abort = true; + return; + } + assert(header_scan_result == StreamSplitter::FLUSH); + assert((int64_t)flush_offset == http1_header->length()); + } + + // http_inspect reassemble() of headers + { + unsigned copied; + stream_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow, + http1_header->length(), 0, http1_header->start(), http1_header->length(), PKT_PDU_TAIL, + copied); + assert(stream_buf.data != nullptr); + assert(copied == (unsigned)http1_header->length()); + } + + // http_inspect eval() of headers + { + Packet dummy_pkt(false); + dummy_pkt.flow = session_data->flow; + dummy_pkt.packet_flags = (source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER; + dummy_pkt.dsize = stream_buf.length; + dummy_pkt.data = stream_buf.data; + session_data->hi->eval(&dummy_pkt); + } } Http2HeadersFrame::~Http2HeadersFrame() { delete start_line; delete start_line_generator; - delete http2_decoded_header; + delete http1_header; delete[] decoded_headers; } +void Http2HeadersFrame::clear() +{ + if (error_during_decode || hi_abort || (session_data->hi == nullptr)) + return; + Packet dummy_pkt(false); + dummy_pkt.flow = session_data->flow; + session_data->hi->clear(&dummy_pkt); +} + const Field& Http2HeadersFrame::get_buf(unsigned id) { switch (id) { + // FIXIT-M need to add a buffer for the decoded start line case HTTP2_BUFFER_DECODED_HEADER: - return *http2_decoded_header; + return *http1_header; default: return Http2Frame::get_buf(id); } @@ -86,12 +181,12 @@ const Field& Http2HeadersFrame::get_buf(unsigned id) #ifdef REG_TEST void Http2HeadersFrame::print_frame(FILE* output) { - fprintf(output, "\nHEADERS frame\n"); + fprintf(output, "\nHeaders frame\n"); if (error_during_decode) fprintf(output, "Error decoding headers.\n"); if (start_line) start_line->print(output, "Decoded start-line"); - http2_decoded_header->print(output, "Decoded header"); + http1_header->print(output, "Decoded header"); Http2Frame::print_frame(output); } #endif diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame.h b/src/service_inspectors/http2_inspect/http2_headers_frame.h index 2c67822d2..da3f80f94 100644 --- a/src/service_inspectors/http2_inspect/http2_headers_frame.h +++ b/src/service_inspectors/http2_inspect/http2_headers_frame.h @@ -31,12 +31,12 @@ class Http2HeadersFrame : public Http2Frame { public: ~Http2HeadersFrame() override; + void clear() override; const Field& get_buf(unsigned id) override; - friend Http2Frame* Http2Frame::new_frame(const uint8_t* header_buffer, const int32_t header_len, - const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, - HttpCommon::SourceId source_id); + friend Http2Frame* Http2Frame::new_frame(const uint8_t*, const int32_t, const uint8_t*, + const int32_t, Http2FlowData*, HttpCommon::SourceId); #ifdef REG_TEST void print_frame(FILE* output) override; @@ -50,9 +50,9 @@ private: Http2StartLine* start_line_generator = nullptr; uint8_t* decoded_headers = nullptr; // working buffer to store decoded headers uint32_t decoded_headers_size = 0; - const Field* http2_decoded_header = nullptr; // finalized headers to be passed to NHI + const Field* http1_header = nullptr; // finalized headers to be passed to NHI const Field* start_line = nullptr; bool error_during_decode = false; - Http2HpackDecoder* hpack_decoder= nullptr; + bool hi_abort = false; }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_hpack.cc b/src/service_inspectors/http2_inspect/http2_hpack.cc index de250421c..dc5f59776 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack.cc +++ b/src/service_inspectors/http2_inspect/http2_hpack.cc @@ -329,7 +329,7 @@ bool Http2HpackDecoder::decode_header_line(const uint8_t* encoded_header_buffer, // not output the start line or decoded headers - this function must be followed by calls to // get_start_line() and get_decoded_headers() to generate and obtain these fields. bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers, - const uint32_t encoded_headers_length, uint8_t* decoded_headers, uint32_t* decoded_headers_len, + const uint32_t encoded_headers_length, uint8_t* decoded_headers, Http2StartLine *start_line_generator, Http2EventGen* stream_events, Http2Infractions* stream_infractions) { @@ -338,7 +338,7 @@ bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers, uint32_t line_bytes_written = 0; bool success = true; start_line = start_line_generator; - decoded_headers_size = decoded_headers_len; + decoded_headers_size = 0; events = stream_events; infractions = stream_infractions; pseudo_headers_fragment_size = 0; @@ -347,10 +347,10 @@ bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers, { success = decode_header_line(encoded_headers + total_bytes_consumed, encoded_headers_length - total_bytes_consumed, line_bytes_consumed, - decoded_headers + *decoded_headers_size, MAX_OCTETS - *decoded_headers_size, + decoded_headers + decoded_headers_size, MAX_OCTETS - decoded_headers_size, line_bytes_written); total_bytes_consumed += line_bytes_consumed; - *decoded_headers_size += line_bytes_written; + decoded_headers_size += line_bytes_written; } // If there were only pseudo-headers, finalize never got called, so create the start-line @@ -361,8 +361,8 @@ bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers, if (success) { success = write_decoded_headers((const uint8_t*)"\r\n", 2, decoded_headers + - *decoded_headers_size, MAX_OCTETS - *decoded_headers_size, line_bytes_written); - *decoded_headers_size += line_bytes_written; + decoded_headers_size, MAX_OCTETS - decoded_headers_size, line_bytes_written); + decoded_headers_size += line_bytes_written; } else decode_error = true; @@ -373,7 +373,7 @@ bool Http2HpackDecoder::finalize_start_line() { // Save the current position in the decoded buffer so we can set the pointer to the start // of the regular headers - pseudo_headers_fragment_size = *decoded_headers_size; + pseudo_headers_fragment_size = decoded_headers_size; return start_line->finalize(); } @@ -388,6 +388,6 @@ const Field* Http2HpackDecoder::get_decoded_headers(const uint8_t* const decoded if (decode_error) return new Field(STAT_NO_SOURCE); else - return new Field(*decoded_headers_size - pseudo_headers_fragment_size, decoded_headers + + return new Field(decoded_headers_size - pseudo_headers_fragment_size, decoded_headers + pseudo_headers_fragment_size, false); } diff --git a/src/service_inspectors/http2_inspect/http2_hpack.h b/src/service_inspectors/http2_inspect/http2_hpack.h index 97cc2e34b..cbe6a9e1c 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack.h +++ b/src/service_inspectors/http2_inspect/http2_hpack.h @@ -37,7 +37,7 @@ class Http2HpackDecoder public: Http2HpackDecoder() { } bool decode_headers(const uint8_t* encoded_headers, const uint32_t encoded_headers_length, - uint8_t* decoded_headers, uint32_t* decoded_headers_size, Http2StartLine* start_line, + uint8_t* decoded_headers, Http2StartLine* start_line, Http2EventGen* stream_events, Http2Infractions* stream_infractions); bool write_decoded_headers(const uint8_t* in_buffer, const uint32_t in_length, uint8_t* decoded_header_buffer, uint32_t decoded_header_length, uint32_t& bytes_written); @@ -71,7 +71,7 @@ public: private: Http2StartLine* start_line = nullptr; - uint32_t* decoded_headers_size; + uint32_t decoded_headers_size; uint32_t pseudo_headers_fragment_size = 0; bool decode_error = false; Http2EventGen* events; diff --git a/src/service_inspectors/http2_inspect/http2_inspect.cc b/src/service_inspectors/http2_inspect/http2_inspect.cc index ac04e98bf..44f6102d2 100644 --- a/src/service_inspectors/http2_inspect/http2_inspect.cc +++ b/src/service_inspectors/http2_inspect/http2_inspect.cc @@ -58,7 +58,7 @@ Http2Inspect::Http2Inspect(const Http2ParaList* params_) : params(params_) #endif } -bool Http2Inspect::configure(SnortConfig* ) +bool Http2Inspect::configure(SnortConfig*) { return true; } @@ -81,7 +81,9 @@ bool Http2Inspect::get_buf(unsigned id, Packet* p, InspectionBuffer& b) if (!session_data->frame_in_detection) return false; - const Field& buffer = session_data->stream->get_buf(id); + const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER; + Http2Stream* const stream = session_data->get_current_stream(source_id); + const Field& buffer = stream->get_buf(id); if (buffer.length() <= 0) return false; @@ -111,7 +113,9 @@ void Http2Inspect::eval(Packet* p) if (session_data->frame_type[source_id] == FT__NONE) return; - session_data->stream->eval_frame(session_data->frame_header[source_id], + Http2Stream* stream = session_data->get_current_stream(source_id); + + stream->eval_frame(session_data->frame_header[source_id], session_data->frame_header_size[source_id], session_data->frame_data[source_id], session_data->frame_data_size[source_id], source_id); @@ -124,7 +128,7 @@ void Http2Inspect::eval(Packet* p) #ifdef REG_TEST if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2)) { - session_data->stream->print_frame(HttpTestManager::get_output_file()); + stream->print_frame(HttpTestManager::get_output_file()); if (HttpTestManager::use_test_input(HttpTestManager::IN_HTTP2)) { printf("Finished processing section from test %" PRIi64 "\n", @@ -143,6 +147,9 @@ void Http2Inspect::clear(Packet* p) return; session_data->frame_in_detection = false; - session_data->stream->clear_frame(); + + const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER; + Http2Stream* stream = session_data->get_current_stream(source_id); + stream->clear_frame(); } diff --git a/src/service_inspectors/http2_inspect/http2_settings_frame.cc b/src/service_inspectors/http2_inspect/http2_settings_frame.cc index db2a7e9fd..c6a5c815c 100644 --- a/src/service_inspectors/http2_inspect/http2_settings_frame.cc +++ b/src/service_inspectors/http2_inspect/http2_settings_frame.cc @@ -103,7 +103,7 @@ bool Http2SettingsFrame::sanity_check() #ifdef REG_TEST void Http2SettingsFrame::print_frame(FILE* output) { - fprintf(output, "SETTINGS frame:"); + fprintf(output, "Settings frame:"); if (bad_frame) fprintf(output, " Error in settings frame."); diff --git a/src/service_inspectors/http2_inspect/http2_settings_frame.h b/src/service_inspectors/http2_inspect/http2_settings_frame.h index 3fc285871..55176f507 100644 --- a/src/service_inspectors/http2_inspect/http2_settings_frame.h +++ b/src/service_inspectors/http2_inspect/http2_settings_frame.h @@ -29,9 +29,8 @@ class Http2Frame; class Http2SettingsFrame : public Http2Frame { public: - friend Http2Frame* Http2Frame::new_frame(const uint8_t* header_buffer, const int32_t header_len, - const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, - HttpCommon::SourceId source_id); + friend Http2Frame* Http2Frame::new_frame(const uint8_t*, const int32_t, const uint8_t*, + const int32_t, Http2FlowData*, HttpCommon::SourceId); #ifdef REG_TEST void print_frame(FILE* output) override; @@ -65,7 +64,7 @@ private: 100, // Max concurrent Streams 65535, // Window size 16384, // Max frame size - 4294967295 // Max header list size + 4294967295 // Max header list size }; }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_stream.cc b/src/service_inspectors/http2_inspect/http2_stream.cc index 6fed50c9c..9f2b59deb 100644 --- a/src/service_inspectors/http2_inspect/http2_stream.cc +++ b/src/service_inspectors/http2_inspect/http2_stream.cc @@ -23,15 +23,20 @@ #include "http2_stream.h" +#include "service_inspectors/http_inspect/http_flow_data.h" + using namespace HttpCommon; -Http2Stream::Http2Stream(Http2FlowData* session_data_) : session_data(session_data_) +Http2Stream::Http2Stream(uint32_t stream_id_, Http2FlowData* session_data_) : + stream_id(stream_id_), + session_data(session_data_) { } Http2Stream::~Http2Stream() { delete current_frame; + delete hi_flow_data; } void Http2Stream::eval_frame(const uint8_t* header_buffer, int32_t header_len, @@ -44,6 +49,8 @@ void Http2Stream::eval_frame(const uint8_t* header_buffer, int32_t header_len, void Http2Stream::clear_frame() { + if (current_frame != nullptr) // FIXIT-M why is this needed? + current_frame->clear(); delete current_frame; current_frame = nullptr; } diff --git a/src/service_inspectors/http2_inspect/http2_stream.h b/src/service_inspectors/http2_inspect/http2_stream.h index 9e82fef4b..3e4ff6e13 100644 --- a/src/service_inspectors/http2_inspect/http2_stream.h +++ b/src/service_inspectors/http2_inspect/http2_stream.h @@ -23,25 +23,37 @@ #include "service_inspectors/http_inspect/http_common.h" #include "service_inspectors/http_inspect/http_field.h" -#include "http2_flow_data.h" #include "http2_frame.h" +class HttpFlowData; +class Http2FlowData; +class HttpMsgSection; + class Http2Stream { public: - Http2Stream(Http2FlowData* session_data_); + Http2Stream(uint32_t stream_id, Http2FlowData* session_data_); ~Http2Stream(); + uint32_t get_stream_id() { return stream_id; } void eval_frame(const uint8_t* header_buffer, int32_t header_len, const uint8_t* data_buffer, int32_t data_len, HttpCommon::SourceId source_id); void clear_frame(); const Field& get_buf(unsigned id); + HttpFlowData* get_hi_flow_data() const { return hi_flow_data; } + void set_hi_flow_data(HttpFlowData* flow_data) + { assert(hi_flow_data == nullptr); hi_flow_data = flow_data; } + HttpMsgSection* get_hi_msg_section() const { return hi_msg_section; } + void set_hi_msg_section(HttpMsgSection* section) { hi_msg_section = section; } #ifdef REG_TEST void print_frame(FILE* output); #endif private: + const uint32_t stream_id; Http2FlowData* const session_data; Http2Frame* current_frame = nullptr; + HttpFlowData* hi_flow_data = nullptr; + HttpMsgSection* hi_msg_section = nullptr; }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_stream_splitter.cc b/src/service_inspectors/http2_inspect/http2_stream_splitter.cc index c1b938838..6344c1014 100644 --- a/src/service_inspectors/http2_inspect/http2_stream_splitter.cc +++ b/src/service_inspectors/http2_inspect/http2_stream_splitter.cc @@ -52,10 +52,10 @@ StreamSplitter::Status Http2StreamSplitter::scan(Packet* pkt, const uint8_t* dat if (session_data == nullptr) { - pkt->flow->set_flow_data(session_data = new Http2FlowData); - Http2Module::increment_peg_counts(PEG_FLOW); AssistantGadgetEvent event(pkt, "http"); DataBus::publish(FLOW_ASSISTANT_GADGET_EVENT, event); + pkt->flow->set_flow_data(session_data = new Http2FlowData(pkt->flow)); + Http2Module::increment_peg_counts(PEG_FLOW); } // General mechanism to abort using scan 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 ff210c5c1..9e42fa0b4 100644 --- a/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc +++ b/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc @@ -59,6 +59,16 @@ static uint8_t get_frame_flags(const uint8_t* frame_buffer) return NO_HEADER; } +static uint8_t get_stream_id(const uint8_t* frame_buffer) +{ + const uint8_t stream_id_index = 5; + assert(frame_buffer != nullptr); + return ((frame_buffer[stream_id_index] & 0x7f) << 24) + + (frame_buffer[stream_id_index + 1] << 16) + + (frame_buffer[stream_id_index + 2] << 8) + + frame_buffer[stream_id_index + 3]; +} + StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t* data, uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id) { @@ -135,9 +145,9 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t // The first nine bytes are the frame header. But all nine might not all be present in // the first TCP segment we receive. - uint32_t remaining_header = FRAME_HEADER_LENGTH - + const uint32_t remaining_header = FRAME_HEADER_LENGTH - session_data->scan_octets_seen[source_id]; - uint32_t remaining_header_in_data = remaining_header > length - data_offset ? + const uint32_t remaining_header_in_data = remaining_header > length - data_offset ? length - data_offset : remaining_header; memcpy(session_data->scan_frame_header[source_id] + session_data->scan_octets_seen[source_id], data + data_offset, @@ -154,7 +164,9 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t // We have the full frame header, compute some variables const uint32_t frame_length = get_frame_length(session_data-> scan_frame_header[source_id]); - uint8_t type = get_frame_type(session_data->scan_frame_header[source_id]); + const uint8_t type = get_frame_type(session_data->scan_frame_header[source_id]); + session_data->current_stream[source_id] = + get_stream_id(session_data->scan_frame_header[source_id]); // Compute frame section length once per frame if (session_data->scan_remaining_frame_octets[source_id] == 0) diff --git a/src/service_inspectors/http_inspect/http_context_data.cc b/src/service_inspectors/http_inspect/http_context_data.cc index ff6a4ba75..15f432ed8 100644 --- a/src/service_inspectors/http_inspect/http_context_data.cc +++ b/src/service_inspectors/http_inspect/http_context_data.cc @@ -25,6 +25,7 @@ #include "http_msg_section.h" #include "protocols/packet.h" +#include "service_inspectors/http2_inspect/http2_flow_data.h" using namespace snort; @@ -32,6 +33,14 @@ unsigned HttpContextData::ips_id = 0; HttpMsgSection* HttpContextData::get_snapshot(const Packet* p) { + if (Http2FlowData::inspector_id != 0) + { + const Http2FlowData* const h2i_flow_data = + (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id); + if (h2i_flow_data != nullptr) + return h2i_flow_data->get_hi_msg_section(); + } + IpsContext* context = p ? p->context : nullptr; HttpContextData* hcd = (HttpContextData*)DetectionEngine::get_data(HttpContextData::ips_id, context); diff --git a/src/service_inspectors/http_inspect/http_flow_data.cc b/src/service_inspectors/http_inspect/http_flow_data.cc index ed494f2c5..acbdd2f65 100644 --- a/src/service_inspectors/http_inspect/http_flow_data.cc +++ b/src/service_inspectors/http_inspect/http_flow_data.cc @@ -49,8 +49,8 @@ HttpFlowData::HttpFlowData() : FlowData(inspector_id) if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP) && !HttpTestManager::use_test_input(HttpTestManager::IN_HTTP)) { - printf("Flow Data construct %" PRIu64 "\n", seq_num); - fflush(nullptr); + fprintf(HttpTestManager::get_output_file(), "Flow Data construct %" PRIu64 "\n", seq_num); + fflush(HttpTestManager::get_output_file()); } #endif HttpModule::increment_peg_counts(PEG_CONCURRENT_SESSIONS); @@ -65,8 +65,8 @@ HttpFlowData::~HttpFlowData() if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP) && !HttpTestManager::use_test_input(HttpTestManager::IN_HTTP)) { - printf("Flow Data destruct %" PRIu64 "\n", seq_num); - fflush(nullptr); + fprintf(HttpTestManager::get_output_file(), "Flow Data destruct %" PRIu64 "\n", seq_num); + fflush(HttpTestManager::get_output_file()); } #endif if (HttpModule::get_peg_counts(PEG_CONCURRENT_SESSIONS) > 0) diff --git a/src/service_inspectors/http_inspect/http_flow_data.h b/src/service_inspectors/http_inspect/http_flow_data.h index ca2d37d13..90759f16f 100644 --- a/src/service_inspectors/http_inspect/http_flow_data.h +++ b/src/service_inspectors/http_inspect/http_flow_data.h @@ -66,6 +66,8 @@ public: #endif private: + bool for_http2 = false; + // Convenience routines void half_reset(HttpCommon::SourceId source_id); void trailer_prep(HttpCommon::SourceId source_id); diff --git a/src/service_inspectors/http_inspect/http_inspect.cc b/src/service_inspectors/http_inspect/http_inspect.cc index 71d2946f9..5892bb6c4 100644 --- a/src/service_inspectors/http_inspect/http_inspect.cc +++ b/src/service_inspectors/http_inspect/http_inspect.cc @@ -27,6 +27,7 @@ #include "detection/detection_engine.h" #include "detection/detection_util.h" +#include "service_inspectors/http2_inspect/http2_flow_data.h" #include "log/unified2.h" #include "protocols/packet.h" #include "stream/stream.h" @@ -310,14 +311,38 @@ int HttpInspect::get_xtra_jsnorm(Flow*, uint8_t** buf, uint32_t* len, uint32_t* return 1; } +HttpFlowData* HttpInspect::http_get_flow_data(const Flow* flow) +{ + Http2FlowData* h2i_flow_data = nullptr; + if (Http2FlowData::inspector_id != 0) + h2i_flow_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id); + if (h2i_flow_data == nullptr) + return (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + else + return h2i_flow_data->get_hi_flow_data(); +} + +void HttpInspect::http_set_flow_data(Flow* flow, HttpFlowData* flow_data) +{ + Http2FlowData* h2i_flow_data = nullptr; + if (Http2FlowData::inspector_id != 0) + h2i_flow_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id); + if (h2i_flow_data == nullptr) + flow->set_flow_data(flow_data); + else + { + flow_data->for_http2 = true; + h2i_flow_data->set_hi_flow_data(flow_data); + } +} + void HttpInspect::eval(Packet* p) { Profile profile(HttpModule::get_profile_stats()); const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER; - HttpFlowData* session_data = - (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id); + HttpFlowData* session_data = http_get_flow_data(p->flow); // FIXIT-H Workaround for unexpected eval() calls if (session_data->section_type[source_id] == SEC__NOT_COMPUTE) @@ -340,7 +365,8 @@ void HttpInspect::eval(Packet* p) const bool buf_owner = !session_data->partial_flush[source_id]; if (!process(p->data, p->dsize, p->flow, source_id, buf_owner)) { - DetectionEngine::disable_content(p); + if (!session_data->for_http2) + DetectionEngine::disable_content(p); } #ifdef REG_TEST @@ -376,7 +402,7 @@ bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const SourceId source_id, bool buf_owner) const { HttpMsgSection* current_section; - HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + HttpFlowData* session_data = http_get_flow_data(flow); assert(session_data != nullptr); if (!session_data->partial_flush[source_id]) @@ -452,13 +478,25 @@ void HttpInspect::clear(Packet* p) { Profile profile(HttpModule::get_profile_stats()); - HttpFlowData* const session_data = - (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id); + HttpFlowData* const session_data = http_get_flow_data(p->flow); if ( session_data == nullptr ) return; - HttpMsgSection* current_section = HttpContextData::clear_snapshot(p->context); + Http2FlowData* h2i_flow_data = nullptr; + if (Http2FlowData::inspector_id != 0) + { + h2i_flow_data = (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id); + } + + HttpMsgSection* current_section = nullptr; + if (h2i_flow_data != nullptr) + { + current_section = h2i_flow_data->get_hi_msg_section(); + h2i_flow_data->set_hi_msg_section(nullptr); + } + else + current_section = HttpContextData::clear_snapshot(p->context); // FIXIT-M This test is necessary because sometimes we get extra clears // Convert to assert when that gets fixed. diff --git a/src/service_inspectors/http_inspect/http_inspect.h b/src/service_inspectors/http_inspect/http_inspect.h index db64b9eb0..4d276a60e 100644 --- a/src/service_inspectors/http_inspect/http_inspect.h +++ b/src/service_inspectors/http_inspect/http_inspect.h @@ -69,6 +69,8 @@ private: bool process(const uint8_t* data, const uint16_t dsize, snort::Flow* const flow, HttpCommon::SourceId source_id_, bool buf_owner) const; + static HttpFlowData* http_get_flow_data(const snort::Flow* flow); + static void http_set_flow_data(snort::Flow* flow, HttpFlowData* flow_data); const HttpParaList* const params; diff --git a/src/service_inspectors/http_inspect/http_msg_section.cc b/src/service_inspectors/http_inspect/http_msg_section.cc index d99577e7b..cfe8c8b7c 100644 --- a/src/service_inspectors/http_inspect/http_msg_section.cc +++ b/src/service_inspectors/http_inspect/http_msg_section.cc @@ -21,6 +21,8 @@ #include "config.h" #endif +#include "service_inspectors/http2_inspect/http2_flow_data.h" + #include "http_msg_section.h" #include "http_context_data.h" @@ -55,6 +57,17 @@ HttpMsgSection::HttpMsgSection(const uint8_t* buffer, const uint16_t buf_size, tcp_close(session_data->tcp_close[source_id]) { assert((source_id == SRC_CLIENT) || (source_id == SRC_SERVER)); + + if (Http2FlowData::inspector_id != 0) + { + Http2FlowData* const h2i_flow_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id); + if (h2i_flow_data != nullptr) + { + h2i_flow_data->set_hi_msg_section(this); + return; + } + } + HttpContextData::save_snapshot(this); } diff --git a/src/service_inspectors/http_inspect/http_stream_splitter_finish.cc b/src/service_inspectors/http_inspect/http_stream_splitter_finish.cc index 46b2c63fc..5c631bfc8 100644 --- a/src/service_inspectors/http_inspect/http_stream_splitter_finish.cc +++ b/src/service_inspectors/http_inspect/http_stream_splitter_finish.cc @@ -26,6 +26,7 @@ #include "http_common.h" #include "http_cutter.h" #include "http_enum.h" +#include "http_inspect.h" #include "http_module.h" #include "http_msg_request.h" #include "http_stream_splitter.h" @@ -39,7 +40,7 @@ bool HttpStreamSplitter::finish(Flow* flow) { Profile profile(HttpModule::get_profile_stats()); - HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + HttpFlowData* session_data = HttpInspect::http_get_flow_data(flow); // FIXIT-M - this assert has been changed to check for null session data and return false if so // due to lack of reliable feedback to stream that scan has been called...if that is // addressed in stream reassembly rewrite this can be reverted to an assert @@ -57,9 +58,10 @@ bool HttpStreamSplitter::finish(Flow* flow) } else { - printf("Finish from flow data %" PRIu64 " direction %d\n", session_data->seq_num, + fprintf(HttpTestManager::get_output_file(), + "Finish from flow data %" PRIu64 " direction %d\n", session_data->seq_num, source_id); - fflush(stdout); + fflush(HttpTestManager::get_output_file()); } } #endif @@ -166,7 +168,7 @@ bool HttpStreamSplitter::init_partial_flush(Flow* flow) return false; } - HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + HttpFlowData* session_data = HttpInspect::http_get_flow_data(flow); assert(session_data != nullptr); if ((session_data->type_expected[source_id] != SEC_BODY_CL) && (session_data->type_expected[source_id] != SEC_BODY_OLD) && @@ -180,8 +182,9 @@ bool HttpStreamSplitter::init_partial_flush(Flow* flow) if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP) && !HttpTestManager::use_test_input(HttpTestManager::IN_HTTP)) { - printf("Partial flush from flow data %" PRIu64 "\n", session_data->seq_num); - fflush(stdout); + fprintf(HttpTestManager::get_output_file(), "Partial flush from flow data %" PRIu64 "\n", + session_data->seq_num); + fflush(HttpTestManager::get_output_file()); } #endif diff --git a/src/service_inspectors/http_inspect/http_stream_splitter_reassemble.cc b/src/service_inspectors/http_inspect/http_stream_splitter_reassemble.cc index 6b1880de8..f69dc1ebb 100644 --- a/src/service_inspectors/http_inspect/http_stream_splitter_reassemble.cc +++ b/src/service_inspectors/http_inspect/http_stream_splitter_reassemble.cc @@ -231,7 +231,7 @@ const StreamBuffer HttpStreamSplitter::reassemble(Flow* flow, unsigned total, copied = len; - HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + HttpFlowData* session_data = HttpInspect::http_get_flow_data(flow); assert(session_data != nullptr); #ifdef REG_TEST @@ -268,10 +268,10 @@ const StreamBuffer HttpStreamSplitter::reassemble(Flow* flow, unsigned total, } else { - printf("Reassemble from flow data %" PRIu64 + fprintf(HttpTestManager::get_output_file(), "Reassemble from flow data %" PRIu64 " direction %d total %u length %u partial %d\n", session_data->seq_num, source_id, total, len, session_data->partial_flush[source_id]); - fflush(stdout); + fflush(HttpTestManager::get_output_file()); } } #endif diff --git a/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc b/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc index 1860518c3..5f3abef59 100644 --- a/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc +++ b/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc @@ -131,11 +131,11 @@ StreamSplitter::Status HttpStreamSplitter::scan(Packet* pkt, const uint8_t* data // This is the session state information we share with HttpInspect and store with stream. A // session is defined by a TCP connection. Since scan() is the first to see a new TCP // connection the new flow data object is created here. - HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + HttpFlowData* session_data = HttpInspect::http_get_flow_data(flow); if (session_data == nullptr) { - flow->set_flow_data(session_data = new HttpFlowData); + HttpInspect::http_set_flow_data(flow, session_data = new HttpFlowData); HttpModule::increment_peg_counts(PEG_FLOW); } @@ -163,13 +163,13 @@ StreamSplitter::Status HttpStreamSplitter::scan(Packet* pkt, const uint8_t* data if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP) && !HttpTestManager::use_test_input(HttpTestManager::IN_HTTP)) { - printf("Scan from flow data %" PRIu64 + fprintf(HttpTestManager::get_output_file(), "Scan from flow data %" PRIu64 " direction %d length %u client port %hu server port %hu\n", session_data->seq_num, source_id, length, flow->client_port, flow->server_port); - fflush(stdout); + fflush(HttpTestManager::get_output_file()); if (HttpTestManager::get_show_scan()) { - Field(length, data).print(stdout, "Scan segment"); + Field(length, data).print(HttpTestManager::get_output_file(), "Scan segment"); } } #endif diff --git a/src/service_inspectors/http_inspect/ips_http.cc b/src/service_inspectors/http_inspect/ips_http.cc index 4307a73f6..be28e7afe 100644 --- a/src/service_inspectors/http_inspect/ips_http.cc +++ b/src/service_inspectors/http_inspect/ips_http.cc @@ -27,6 +27,7 @@ #include "hash/hashfcn.h" #include "log/messages.h" #include "protocols/packet.h" +#include "service_inspectors/http2_inspect/http2_flow_data.h" #include "http_common.h" #include "http_enum.h" @@ -223,10 +224,15 @@ IpsOption::EvalStatus HttpIpsOption::eval(Cursor& c, Packet* p) if (!section_match) return NO_MATCH; + const Http2FlowData* const h2i_flow_data = + (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id); + + HttpInspect* const hi = (h2i_flow_data != nullptr) ? + (HttpInspect*)(p->flow->assistant_gadget) : (HttpInspect*)(p->flow->gadget); + InspectionBuffer hb; - if (! ((HttpInspect*)(p->flow->gadget))-> - http_get_buf((unsigned)buffer_index, sub_id, form, p, hb)) + if (! (hi->http_get_buf((unsigned)buffer_index, sub_id, form, p, hb))) return NO_MATCH; c.set(key, hb.data, hb.len);