From: Mike Stepanek (mstepane) Date: Mon, 25 Nov 2019 14:28:51 +0000 (+0000) Subject: Merge pull request #1830 in SNORT/snort3 from ~KATHARVE/snort3:h2i_response_start_lin... X-Git-Tag: 3.0.0-266~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=227d58bcf861b408792ad42dc25cb945704b9e70;p=thirdparty%2Fsnort3.git Merge pull request #1830 in SNORT/snort3 from ~KATHARVE/snort3:h2i_response_start_line2 to master Squashed commit of the following: commit 0a5f41439490f3dad02b91ae6358c448af539553 Author: Katura Harvey Date: Wed Oct 30 17:09:13 2019 -0400 http2_inspect: generate status lines for responses and be more lenient on RFC violations --- diff --git a/src/service_inspectors/http2_inspect/CMakeLists.txt b/src/service_inspectors/http2_inspect/CMakeLists.txt index 53ff980a8..9f8357e38 100644 --- a/src/service_inspectors/http2_inspect/CMakeLists.txt +++ b/src/service_inspectors/http2_inspect/CMakeLists.txt @@ -28,6 +28,8 @@ set (FILE_LIST http2_request_line.h http2_start_line.cc http2_start_line.h + http2_status_line.cc + http2_status_line.h http2_stream_splitter.cc http2_stream_splitter_impl.cc http2_stream_splitter.h diff --git a/src/service_inspectors/http2_inspect/dev_notes.txt b/src/service_inspectors/http2_inspect/dev_notes.txt index 60b474047..f3e76a10d 100644 --- a/src/service_inspectors/http2_inspect/dev_notes.txt +++ b/src/service_inspectors/http2_inspect/dev_notes.txt @@ -35,8 +35,10 @@ indexed, except the header line is not added to the dynamic table. HTTP/2 uses pseudo-headers to convey the information included in an HTTP/1.1 start-line. Pseudo-headers are not valid HTTP/1.1 headers, so must be translated into a start-line before the decoded headers can be passed to NHI. The two Http2StartLine subclasses, Http2RequestLine and -Http2ResponseLine (not yet implemented) perform this translation and generate the start-line, which -is stored in a new buffer inside the Http2StartLine object. The start-line buffer must be passed to -NHI before the http2_decoded_header buffer, which contains the regular HTTP/1.1 headers. +Http2ResponseLine perform this translation and generate the start-line, which is stored in a new +buffer inside the Http2StartLine object. The start-line buffer must be passed to NHI before the +http2_decoded_header buffer, which contains the regular HTTP/1.1 headers. Note that HTTP/2 does not +use status reason phrases, so the status line passed to NHI will not include one. It will include +only the HTTP version and the status code. H2I supports the NHI test tool. See ../http_inspect/dev_notes.txt for usage instructions. diff --git a/src/service_inspectors/http2_inspect/http2_enum.h b/src/service_inspectors/http2_inspect/http2_enum.h index 76a86ce3e..9a0306fdf 100644 --- a/src/service_inspectors/http2_inspect/http2_enum.h +++ b/src/service_inspectors/http2_inspect/http2_enum.h @@ -55,6 +55,9 @@ enum EventSid EVENT_UNEXPECTED_CONTINUATION = 5, EVENT_MISFORMATTED_HTTP2 = 6, EVENT_PREFACE_MATCH_FAILURE = 7, + EVENT_REQUEST_WITHOUT_REQUIRED_FIELD = 9, + EVENT_RESPONSE_WITHOUT_STATUS = 10, + EVENT_INVALID_HEADER = 11, EVENT__MAX_VALUE }; @@ -78,6 +81,7 @@ enum Infraction INF_INVALID_PSEUDO_HEADER = 13, INF_PSEUDO_HEADER_AFTER_REGULAR_HEADER = 14, INF_PSEUDO_HEADER_URI_FORM_MISMATCH = 15, + INF_RESPONSE_WITHOUT_STATUS = 16, INF__MAX_VALUE }; @@ -92,7 +96,8 @@ enum HeaderFrameFlags enum PseudoHeaders { - HEADER_NONE = 0, + HEADER__INVALID = -1, + HEADER__NONE = 0, AUTHORITY = 1, METHOD = 3, PATH = 5, diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.h b/src/service_inspectors/http2_inspect/http2_flow_data.h index 6d324c471..8a0aa94ee 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.h +++ b/src/service_inspectors/http2_inspect/http2_flow_data.h @@ -48,20 +48,21 @@ public: static unsigned inspector_id; static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } - friend class Http2Inspect; - friend class Http2StreamSplitter; - friend class Http2Hpack; - friend class Http2StartLine; - friend class Http2RequestLine; friend class Http2Frame; friend class Http2HeadersFrame; + friend class Http2Hpack; + friend class Http2Inspect; + friend class Http2RequestLine; + friend class Http2StartLine; + friend class Http2StatusLine; + friend class Http2StreamSplitter; + friend void implement_eval(Http2FlowData* session_data, HttpCommon::SourceId source_id); + friend bool implement_get_buf(unsigned id, Http2FlowData*, HttpCommon::SourceId, + snort::InspectionBuffer&); friend const snort::StreamBuffer implement_reassemble(Http2FlowData*, unsigned, unsigned, const uint8_t*, unsigned, uint32_t, HttpCommon::SourceId); friend snort::StreamSplitter::Status implement_scan(Http2FlowData*, const uint8_t*, uint32_t, uint32_t*, HttpCommon::SourceId); - friend bool implement_get_buf(unsigned id, Http2FlowData*, HttpCommon::SourceId, - snort::InspectionBuffer&); - friend void implement_eval(Http2FlowData* session_data, HttpCommon::SourceId source_id); size_t size_of() override { return sizeof(*this); } diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame.cc b/src/service_inspectors/http2_inspect/http2_headers_frame.cc index 9b22f4f12..be115eee6 100644 --- a/src/service_inspectors/http2_inspect/http2_headers_frame.cc +++ b/src/service_inspectors/http2_inspect/http2_headers_frame.cc @@ -26,7 +26,6 @@ #include "http2_enum.h" #include "http2_flow_data.h" #include "http2_hpack.h" -#include "http2_request_line.h" #include "http2_start_line.h" using namespace HttpCommon; @@ -50,10 +49,8 @@ Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t decoded_headers = new uint8_t[MAX_OCTETS]; decoded_headers_size = 0; - // FIXIT-H Implement response start line - if (source_id == SRC_CLIENT) - start_line_generator = new Http2RequestLine(session_data->events[source_id], - session_data->infractions[source_id]); + 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() - @@ -63,9 +60,7 @@ Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t session_data->frame_type[source_id] = FT__ABORT; error_during_decode = true; } - //FIXIT-H remove condition once status lines implemented - if (source_id == SRC_CLIENT) - start_line = hpack_decoder->get_start_line(); + start_line = hpack_decoder->get_start_line(); http2_decoded_header = hpack_decoder->get_decoded_headers(decoded_headers); } diff --git a/src/service_inspectors/http2_inspect/http2_hpack.cc b/src/service_inspectors/http2_inspect/http2_hpack.cc index 004b37a73..bd1ec8858 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack.cc +++ b/src/service_inspectors/http2_inspect/http2_hpack.cc @@ -27,7 +27,7 @@ #include "service_inspectors/http_inspect/http_test_manager.h" #include "http2_enum.h" -#include "http2_request_line.h" +#include "http2_start_line.h" using namespace HttpCommon; using namespace Http2Enums; @@ -119,17 +119,15 @@ bool Http2HpackDecoder::decode_static_table_index(const uint64_t index, const bo assert(index > 0); // If this is a pseudo-header, pass it to the start line - // Remove second condition after response start line implemented - if (index < PSEUDO_HEADER_MAX_INDEX and start_line) + if (index < PSEUDO_HEADER_MAX_INDEX) { - if (start_line and !start_line->process_pseudo_header_name(index)) - return false; + start_line->process_pseudo_header_name(index); } // If this is a regular header, write header name + ': ' to decoded headers else { - if (start_line and !start_line->is_finalized()) + if (!start_line->is_finalized()) { if (!finalize_start_line()) return false; @@ -157,8 +155,7 @@ bool Http2HpackDecoder::decode_static_table_index(const uint64_t index, const bo return false; } - // Remove second condition after response start line implemented - if (index < PSEUDO_HEADER_MAX_INDEX and start_line) + if (index < PSEUDO_HEADER_MAX_INDEX) { start_line->process_pseudo_header_value( (const uint8_t*)entry->value, strlen(entry->value)); @@ -189,8 +186,8 @@ bool Http2HpackDecoder::decode_dynamic_table_index(const uint64_t index, UNUSED(index); UNUSED(decode_full_line); - //FIXIT-H finalize header_start_line only for regular headers - if (start_line and !start_line->is_finalized()) + //FIXIT-H finalize start_line only for regular headers + if (!start_line->is_finalized()) { if (!finalize_start_line()) return false; @@ -263,18 +260,17 @@ bool Http2HpackDecoder::decode_literal_header_line(const uint8_t* encoded_header return false; } // If this was a pseudo-header value, give it to the start-line. - if (start_line and start_line->is_pseudo_name( + if (start_line->is_pseudo_name( (const char*) decoded_header_buffer)) { // don't include the ': ' that was written following the header name - if (!start_line->process_pseudo_header_name( - decoded_header_buffer, partial_bytes_written - 2)) - return false; + start_line->process_pseudo_header_name( + decoded_header_buffer, partial_bytes_written - 2); } // If not a pseudo-header value, keep it in the decoded headers else { - if (start_line and !start_line->is_finalized()) + if (!start_line->is_finalized()) { if (!finalize_start_line()) return false; @@ -296,7 +292,7 @@ bool Http2HpackDecoder::decode_literal_header_line(const uint8_t* encoded_header } // If this was a pseudo-header value, give it to the start-line. - if (start_line and start_line->is_pseudo_value()) + if (start_line->is_pseudo_value()) { // Subtract 2 from the length to remove the trailing CRLF before passing to the start line start_line->process_pseudo_header_value( @@ -397,7 +393,7 @@ bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers, } // If there were only pseudo-headers, finalize never got called, so create the start-line - if (start_line and !start_line->is_finalized()) + if (!start_line->is_finalized()) { success &= finalize_start_line(); } diff --git a/src/service_inspectors/http2_inspect/http2_request_line.cc b/src/service_inspectors/http2_inspect/http2_request_line.cc index 61473ad0d..6e602dbb1 100644 --- a/src/service_inspectors/http2_inspect/http2_request_line.cc +++ b/src/service_inspectors/http2_inspect/http2_request_line.cc @@ -42,10 +42,9 @@ const char* Http2RequestLine::SCHEME_NAME = ":scheme"; const char* Http2RequestLine::OPTIONS = "OPTIONS"; const char* Http2RequestLine::CONNECT = "CONNECT"; -bool Http2RequestLine::process_pseudo_header_name(const uint64_t index) +void Http2RequestLine::process_pseudo_header_name(const uint64_t index) { - if (!process_pseudo_header_precheck()) - return false; + process_pseudo_header_precheck(); if (index <= AUTHORITY and authority.length() <= 0) value_coming = AUTHORITY; @@ -58,16 +57,14 @@ bool Http2RequestLine::process_pseudo_header_name(const uint64_t index) else { infractions += INF_INVALID_PSEUDO_HEADER; - events->create_event(EVENT_MISFORMATTED_HTTP2); - return false; + events->create_event(EVENT_INVALID_HEADER); + value_coming = HEADER__INVALID; } - return true; } -bool Http2RequestLine::process_pseudo_header_name(const uint8_t* const& name, uint32_t length) +void Http2RequestLine::process_pseudo_header_name(const uint8_t* const& name, uint32_t length) { - if (!process_pseudo_header_precheck()) - return false; + process_pseudo_header_precheck(); if (length == AUTHORITY_NAME_LENGTH and memcmp(name, AUTHORITY_NAME, length) == 0 and authority.length() <= 0) @@ -84,10 +81,9 @@ bool Http2RequestLine::process_pseudo_header_name(const uint8_t* const& name, ui else { infractions += INF_INVALID_PSEUDO_HEADER; - events->create_event(EVENT_MISFORMATTED_HTTP2); - return false; + events->create_event(EVENT_INVALID_HEADER); + value_coming = HEADER__INVALID; } - return true; } void Http2RequestLine::process_pseudo_header_value(const uint8_t* const& value, const uint32_t length) @@ -107,10 +103,10 @@ void Http2RequestLine::process_pseudo_header_value(const uint8_t* const& value, scheme.set(length, value); break; default: - // should never get here - assert(false); + // ignore invalid pseudo-header value - alert generated in process_pseudo_header_name + break; } - value_coming = HEADER_NONE; + value_coming = HEADER__NONE; } // This is called on the first non-pseudo-header. Select the appropriate URI form based on the @@ -125,11 +121,11 @@ bool Http2RequestLine::generate_start_line() if (method.length() <= 0) { infractions += INF_PSEUDO_HEADER_URI_FORM_MISMATCH; - events->create_event(EVENT_MISFORMATTED_HTTP2); + events->create_event(EVENT_REQUEST_WITHOUT_REQUIRED_FIELD); return false; } start_line_length = method.length() + path.length() + http_version_length + - start_line_extra_chars; + NUM_REQUEST_LINE_EXTRA_CHARS; start_line_buffer = new uint8_t[start_line_length]; memcpy(start_line_buffer, method.start(), method.length()); @@ -147,16 +143,22 @@ bool Http2RequestLine::generate_start_line() else if (method.length() == CONNECT_LENGTH and memcmp(method.start(), CONNECT, method.length()) == 0) { - // Must have an authority and not have a scheme or path + // Must have an authority // FIXIT-L May want to be more lenient than RFC on generating start line - if ( scheme.length() > 0 or path.length() > 0 or authority.length() <= 0) + if (authority.length() <= 0) { infractions += INF_PSEUDO_HEADER_URI_FORM_MISMATCH; - events->create_event(EVENT_MISFORMATTED_HTTP2); + events->create_event(EVENT_REQUEST_WITHOUT_REQUIRED_FIELD); return false; } + // Should not have a scheme or path + if ( scheme.length() > 0 or path.length() > 0) + { + infractions += INF_PSEUDO_HEADER_URI_FORM_MISMATCH; + events->create_event(EVENT_INVALID_HEADER); + } start_line_length = method.length() + authority.length() + http_version_length + - start_line_extra_chars; + NUM_REQUEST_LINE_EXTRA_CHARS; start_line_buffer = new uint8_t[start_line_length]; memcpy(start_line_buffer, method.start(), method.length()); @@ -177,8 +179,8 @@ bool Http2RequestLine::generate_start_line() if (authority.length() > 0) { start_line_length = method.length() + scheme.length() + authority.length() + - path.length() + http_version_length + start_line_extra_chars + - absolute_form_extra_chars_num; + path.length() + http_version_length + NUM_REQUEST_LINE_EXTRA_CHARS + + NUM_ABSOLUTE_FORM_EXTRA_CHARS; start_line_buffer = new uint8_t[start_line_length]; memcpy(start_line_buffer, method.start(), method.length()); @@ -202,7 +204,7 @@ bool Http2RequestLine::generate_start_line() else { start_line_length = method.length() + path.length() + http_version_length + - start_line_extra_chars; + NUM_REQUEST_LINE_EXTRA_CHARS; start_line_buffer = new uint8_t[start_line_length]; memcpy(start_line_buffer, method.start(), method.length()); diff --git a/src/service_inspectors/http2_inspect/http2_request_line.h b/src/service_inspectors/http2_inspect/http2_request_line.h index 3ef8e35d4..22693fc94 100644 --- a/src/service_inspectors/http2_inspect/http2_request_line.h +++ b/src/service_inspectors/http2_inspect/http2_request_line.h @@ -29,15 +29,18 @@ class Http2RequestLine : public Http2StartLine { public: - Http2RequestLine(Http2EventGen* events, Http2Infractions* infractions) : Http2StartLine(events, - infractions) { } - - bool process_pseudo_header_name(const uint64_t index) override; - bool process_pseudo_header_name(const uint8_t* const& name, uint32_t length) override; + void process_pseudo_header_name(const uint64_t index) override; + void process_pseudo_header_name(const uint8_t* const& name, uint32_t length) override; void process_pseudo_header_value(const uint8_t* const& value, const uint32_t length) override; bool generate_start_line() override; + friend Http2StartLine* Http2StartLine::new_start_line_generator(HttpCommon::SourceId source_id, + Http2EventGen* events, Http2Infractions* infractions); + private: + Http2RequestLine(Http2EventGen* events, Http2Infractions* infractions) : Http2StartLine(events, + infractions) { } + Field method; Field path; Field scheme; @@ -58,8 +61,10 @@ private: static const char* CONNECT; static const int32_t CONNECT_LENGTH = 7; + // Account for two spaces, and trailing crlf + static const uint8_t NUM_REQUEST_LINE_EXTRA_CHARS = 4; // absolute form adds '://' between scheme and authority - static const uint32_t absolute_form_extra_chars_num = 3; + static const uint32_t NUM_ABSOLUTE_FORM_EXTRA_CHARS = 3; }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_start_line.cc b/src/service_inspectors/http2_inspect/http2_start_line.cc index 04c95c9cb..1336b732a 100644 --- a/src/service_inspectors/http2_inspect/http2_start_line.cc +++ b/src/service_inspectors/http2_inspect/http2_start_line.cc @@ -28,6 +28,8 @@ #include "http2_enum.h" #include "http2_flow_data.h" +#include "http2_request_line.h" +#include "http2_status_line.h" using namespace HttpCommon; using namespace Http2Enums; @@ -39,15 +41,22 @@ Http2StartLine::~Http2StartLine() delete[] start_line_buffer; } -bool Http2StartLine::process_pseudo_header_precheck() +Http2StartLine* Http2StartLine::new_start_line_generator(SourceId source_id, + Http2EventGen* events, Http2Infractions* infractions) +{ + if (source_id == SRC_CLIENT) + return new Http2RequestLine(events, infractions); + else + return new Http2StatusLine(events, infractions); +} + +void Http2StartLine::process_pseudo_header_precheck() { if (finalized) { infractions += INF_PSEUDO_HEADER_AFTER_REGULAR_HEADER; - events->create_event(EVENT_MISFORMATTED_HTTP2); - return false; + events->create_event(EVENT_INVALID_HEADER); } - return true; } bool Http2StartLine::finalize() diff --git a/src/service_inspectors/http2_inspect/http2_start_line.h b/src/service_inspectors/http2_inspect/http2_start_line.h index 08927b66e..f48a9ba99 100644 --- a/src/service_inspectors/http2_inspect/http2_start_line.h +++ b/src/service_inspectors/http2_inspect/http2_start_line.h @@ -36,24 +36,27 @@ class Http2FlowData; class Http2StartLine { public: - Http2StartLine(Http2EventGen* events, Http2Infractions* infractions) : events(events), - infractions(infractions) { } + static Http2StartLine* new_start_line_generator(HttpCommon::SourceId source_id, + Http2EventGen* events, Http2Infractions* infractions); virtual ~Http2StartLine(); friend class Http2Hpack; const Field* get_start_line(); - virtual bool process_pseudo_header_name(const uint64_t index) = 0; - virtual bool process_pseudo_header_name(const uint8_t* const& name, uint32_t length) = 0; + virtual void process_pseudo_header_name(const uint64_t index) = 0; + virtual void process_pseudo_header_name(const uint8_t* const& name, uint32_t length) = 0; virtual void process_pseudo_header_value(const uint8_t* const& value, const uint32_t length) = 0; bool finalize(); bool is_finalized() { return finalized; } - bool is_pseudo_value() { return value_coming != Http2Enums::HEADER_NONE; } + bool is_pseudo_value() { return value_coming != Http2Enums::HEADER__NONE; } bool is_pseudo_name(const char* const& name) { return name[0] == ':'; } protected: - bool process_pseudo_header_precheck(); + Http2StartLine(Http2EventGen* events, Http2Infractions* infractions) : events(events), + infractions(infractions) { } + + void process_pseudo_header_precheck(); virtual bool generate_start_line() = 0; Http2EventGen* events; @@ -61,14 +64,12 @@ protected: bool finalized = false; uint32_t start_line_length = 0; uint8_t *start_line_buffer = nullptr; - Http2Enums::PseudoHeaders value_coming = Http2Enums::HEADER_NONE; + Http2Enums::PseudoHeaders value_coming = Http2Enums::HEADER__NONE; uint32_t pseudo_header_fragment_size = 0; // Version string is HTTP/1.1 static const char* http_version_string; static const uint8_t http_version_length = 8; - // Account for two spaces, and trailing crlf - static const uint8_t start_line_extra_chars = 4; }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_status_line.cc b/src/service_inspectors/http2_inspect/http2_status_line.cc new file mode 100644 index 000000000..fa6951862 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_status_line.cc @@ -0,0 +1,106 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// http2_status_line.cc author Katura Harvey + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "http2_status_line.h" + +#include + +#include "service_inspectors/http_inspect/http_common.h" +#include "service_inspectors/http_inspect/http_field.h" + +#include "http2_enum.h" +#include "http2_flow_data.h" + +using namespace HttpCommon; +using namespace Http2Enums; + +const char* Http2StatusLine::STATUS_NAME = ":status"; + +void Http2StatusLine::process_pseudo_header_name(const uint64_t index) +{ + process_pseudo_header_precheck(); + + if (index >= RESPONSE_PSEUDO_MIN_INDEX and index <= STATUS and status.length() <= 0) + value_coming = STATUS; + else + { + infractions += INF_INVALID_PSEUDO_HEADER; + events->create_event(EVENT_INVALID_HEADER); + value_coming = HEADER__INVALID; + } +} + +void Http2StatusLine::process_pseudo_header_name(const uint8_t* const& name, uint32_t length) +{ + process_pseudo_header_precheck(); + + if (length == STATUS_NAME_LENGTH and memcmp(name, STATUS_NAME, length) == 0 and + status.length() <= 0) + value_coming = STATUS; + else + { + infractions += INF_INVALID_PSEUDO_HEADER; + events->create_event(EVENT_INVALID_HEADER); + value_coming = HEADER__INVALID; + } +} + +void Http2StatusLine::process_pseudo_header_value(const uint8_t* const& value, const uint32_t length) +{ + // ignore invalid pseudo-header value - alert generated in process_pseudo_header_name + if (value_coming == STATUS) + status.set(length, (const uint8_t*) value); + + value_coming = HEADER__NONE; +} + +// This is called on the first non-pseudo-header. +bool Http2StatusLine::generate_start_line() +{ + uint32_t bytes_written = 0; + + // Account for one space and trailing crlf + static const uint8_t NUM_RESPONSE_LINE_EXTRA_CHARS = 3; + + if (status.length() <= 0) + { + infractions += INF_RESPONSE_WITHOUT_STATUS; + events->create_event(EVENT_RESPONSE_WITHOUT_STATUS); + return false; + } + + start_line_length = http_version_length + status.length() + NUM_RESPONSE_LINE_EXTRA_CHARS; + start_line_buffer = new uint8_t[start_line_length]; + + memcpy(start_line_buffer + bytes_written, http_version_string, http_version_length); + bytes_written += http_version_length; + memcpy(start_line_buffer + bytes_written, " ", 1); + bytes_written += 1; + memcpy(start_line_buffer + bytes_written, status.start(), status.length()); + bytes_written += status.length(); + memcpy(start_line_buffer + bytes_written, "\r\n", 2); + bytes_written += 2; + assert(bytes_written == start_line_length); + + return true; +} diff --git a/src/service_inspectors/http2_inspect/http2_status_line.h b/src/service_inspectors/http2_inspect/http2_status_line.h new file mode 100644 index 000000000..e0a22addb --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_status_line.h @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// http2_status_line.h author Katura Harvey + +#ifndef HTTP2_STATUS_LINE_H +#define HTTP2_STATUS_LINE_H + +#include "service_inspectors/http_inspect/http_common.h" +#include "service_inspectors/http_inspect/http_field.h" + +#include "http2_start_line.h" + +class Http2StatusLine : public Http2StartLine +{ +public: + void process_pseudo_header_name(const uint64_t index) override; + void process_pseudo_header_name(const uint8_t* const& name, uint32_t length) override; + void process_pseudo_header_value(const uint8_t* const& value, const uint32_t length) override; + bool generate_start_line() override; + + friend Http2StartLine* Http2StartLine::new_start_line_generator(HttpCommon::SourceId source_id, + Http2EventGen* events, Http2Infractions* infractions); + +private: + Http2StatusLine(Http2EventGen* events, Http2Infractions* infractions) : Http2StartLine(events, + infractions) { } + + Field status; + + static const char* STATUS_NAME; + static const uint32_t STATUS_NAME_LENGTH = 7; + static const uint32_t RESPONSE_PSEUDO_MIN_INDEX = 8; + +}; + +#endif diff --git a/src/service_inspectors/http2_inspect/http2_tables.cc b/src/service_inspectors/http2_inspect/http2_tables.cc index 04a44eb70..8ba1c37c4 100644 --- a/src/service_inspectors/http2_inspect/http2_tables.cc +++ b/src/service_inspectors/http2_inspect/http2_tables.cc @@ -32,12 +32,15 @@ using namespace snort; const RuleMap Http2Module::http2_events[] = { { EVENT_INT_DECODE_FAILURE, "error in HPACK integer value" }, - { EVENT_INT_LEADING_ZEROS, "integer value has leading zeros" }, + { EVENT_INT_LEADING_ZEROS, "HPACK integer value has leading zeros" }, { 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"}, + { EVENT_MISSING_CONTINUATION, "missing HTTP/2 continuation frame" }, + { EVENT_UNEXPECTED_CONTINUATION, "unexpected HTTP/2 continuation frame" }, + { EVENT_MISFORMATTED_HTTP2, "misformatted HTTP/2 traffic" }, + { EVENT_PREFACE_MATCH_FAILURE, "HTTP/2 connection preface does not match" }, + { EVENT_REQUEST_WITHOUT_REQUIRED_FIELD, "HTTP/2 request missing required header field" }, + { EVENT_RESPONSE_WITHOUT_STATUS, "HTTP/2 response has no status code" }, + { EVENT_INVALID_HEADER, "invalid HTTP/2 header field" }, { 0, nullptr } };