From: Tom Peters (thopeter) Date: Mon, 10 Jan 2022 19:46:58 +0000 (+0000) Subject: Pull request #3218: US #684704: http_inspect: improve version processing X-Git-Tag: 3.1.20.0~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=407f1223c517e200bc059b8a071e561d9f805731;p=thirdparty%2Fsnort3.git Pull request #3218: US #684704: http_inspect: improve version processing Merge in SNORT/snort3 from ~MDAGON/snort3:version to master Squashed commit of the following: commit 678d5e1729f67abcbe05886aefc60485ff7e9d27 Author: Maya Dagon Date: Tue Nov 30 15:57:27 2021 -0500 http_inspect: version update, http_version_match rule option --- diff --git a/doc/reference/builtin_stubs.txt b/doc/reference/builtin_stubs.txt index bbad9a923..0254995dd 100644 --- a/doc/reference/builtin_stubs.txt +++ b/doc/reference/builtin_stubs.txt @@ -939,11 +939,6 @@ request or status line. The HTTP version in the start line begins with "HTTP/" but the remainder is not in the expected . format. -119:208 - -The HTTP version in the start line has a valid format but is not HTTP/1.0 or HTTP/1.1. This alert -does not apply to HTTP/2 or HTTP/3 traffic. - 119:209 An HTTP header line contains a format error. A well-formed header consists of a field name followed @@ -1287,6 +1282,19 @@ this alert is raised. This alert is not expected for typical network traffic and an indication that an attacker is trying to exhaust resources. This alert is raised by the enhanced JavaScript normalizer. +119:275 + +The HTTP version in the start line has a valid 1. format, but the subversion is not 0 or 1. + +119:276 + +The HTTP version in the start line has a valid format but the version is 0. + +119:277 + +The HTTP version in the start line has a valid format but the version is higher than 1. This alert +does not apply to HTTP/2 or HTTP/3 traffic. + 121:1 Invalid flag set on HTTP/2 frame header diff --git a/doc/user/http_inspect.txt b/doc/user/http_inspect.txt index c32787ccf..d47ed2641 100755 --- a/doc/user/http_inspect.txt +++ b/doc/user/http_inspect.txt @@ -692,6 +692,20 @@ trailers, respectively. Checks available: equal to "=" or just value, not "!" or "!=", less than "<", greater than ">", less or equal to "<=", less or greater than ">=", in range "<>", in range or equal to "<=>". +===== http_version_match + +Rule option that matches HTTP version to one of the listed version values. +Possible match values: 1.0, 1.1, 2.0, 0.9, other, and malformed. +When receiving a request line or status line, if the version is present +it will be used for comparison. If the version doesn't have a format of +[0-9].[0-9] it is considered malformed. A [0-9].[0-9] that is not 1.0 or 1.1 +is considered other. 0.9 refers to the original HTTP protocol version that +uses simple GET requests without headers and includes no version number. 2.0 +refers to the actual HTTP/2 protocol with framed data. Messages that follow +the general HTTP/1 format but contain version fields falsely claiming to be +HTTP/2.0 or HTTP/0.9 will match "other" as described above. The http_version +rule option is available to examine the actual bytes in the version field. + ==== Timing issues and combining rule options HTTP inspector is stateful. That means it is aware of a bigger picture than diff --git a/src/pub_sub/test/pub_sub_http_request_body_event_test.cc b/src/pub_sub/test/pub_sub_http_request_body_event_test.cc index 173a3d638..ea84a7d95 100644 --- a/src/pub_sub/test/pub_sub_http_request_body_event_test.cc +++ b/src/pub_sub/test/pub_sub_http_request_body_event_test.cc @@ -73,7 +73,7 @@ HttpMsgSection::HttpMsgSection(const uint8_t* buffer, const uint16_t buf_size, trans_num(STAT_NOT_PRESENT), status_code_num(STAT_NOT_PRESENT), source_id(source_id_), - version_id(VERS__NO_SOURCE), + version_id(VERS__NOT_PRESENT), method_id(METH__NOT_PRESENT), tcp_close(false) {} diff --git a/src/service_inspectors/http_inspect/http_api.cc b/src/service_inspectors/http_inspect/http_api.cc index 91c1327b9..a1caead7b 100644 --- a/src/service_inspectors/http_inspect/http_api.cc +++ b/src/service_inspectors/http_inspect/http_api.cc @@ -120,6 +120,7 @@ extern const BaseApi* ips_http_trailer; extern const BaseApi* ips_http_true_ip; extern const BaseApi* ips_http_uri; extern const BaseApi* ips_http_version; +extern const BaseApi* ips_http_version_match; extern const BaseApi* ips_js_data; #ifdef BUILDING_SO @@ -149,6 +150,7 @@ const BaseApi* sin_http[] = ips_http_true_ip, ips_http_uri, ips_http_version, + ips_http_version_match, ips_js_data, nullptr }; diff --git a/src/service_inspectors/http_inspect/http_enum.h b/src/service_inspectors/http_inspect/http_enum.h index 11ac86059..372aaf145 100755 --- a/src/service_inspectors/http_inspect/http_enum.h +++ b/src/service_inspectors/http_inspect/http_enum.h @@ -61,8 +61,8 @@ enum HTTP_RULE_OPT { HTTP_BUFFER_CLIENT_BODY = 1, HTTP_BUFFER_COOKIE, HTTP_BUFFE HTTP_BUFFER_RAW_HEADER, HTTP_BUFFER_RAW_REQUEST, HTTP_BUFFER_RAW_STATUS, HTTP_BUFFER_RAW_TRAILER, HTTP_BUFFER_RAW_URI, HTTP_BUFFER_STAT_CODE, HTTP_BUFFER_STAT_MSG, HTTP_BUFFER_TRAILER, HTTP_BUFFER_TRUE_IP, HTTP_BUFFER_URI, HTTP_BUFFER_VERSION, - BUFFER_JS_DATA, BUFFER_VBA_DATA , HTTP_BUFFER_MAX = BUFFER_VBA_DATA, - HTTP_RANGE_NUM_HDRS, HTTP_RANGE_NUM_TRAILERS, HTTP_MAX_RULE_OPTION }; + BUFFER_JS_DATA, BUFFER_VBA_DATA , HTTP__BUFFER_MAX = BUFFER_VBA_DATA, + HTTP_RANGE_NUM_HDRS, HTTP_RANGE_NUM_TRAILERS, HTTP_VERSION_MATCH, HTTP__MAX_RULE_OPTION }; // Peg counts // This enum must remain synchronized with HttpModule::peg_names[] in http_tables.cc @@ -83,8 +83,9 @@ enum ChunkState { CHUNK_NEWLINES, CHUNK_ZEROS, CHUNK_LEADING_WS, CHUNK_NUMBER, C CHUNK_OPTIONS, CHUNK_HCRLF, CHUNK_DATA, CHUNK_DCRLF1, CHUNK_DCRLF2, CHUNK_BAD }; // List of possible HTTP versions. -enum VersionId { VERS__NO_SOURCE=-16, VERS__NOT_COMPUTE=-14, VERS__PROBLEMATIC=-12, - VERS__NOT_PRESENT=-11, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0, VERS_0_9 }; +enum VersionId { VERS__PROBLEMATIC=-12, VERS__NOT_PRESENT=-11, VERS__OTHER=1, + VERS_1_0, VERS_1_1, VERS_2_0, VERS_0_9, VERS__MIN = VERS__PROBLEMATIC, + VERS__MAX = VERS_0_9}; // Every request method we have ever heard of enum MethodId { @@ -160,7 +161,7 @@ enum Infraction INF_TOO_MANY_HEADERS = 4, INF_BAD_HEADER = 5, INF_BAD_STAT_CODE = 6, - INF_UNKNOWN_VERSION = 7, + INF_VERSION_HIGHER_THAN_1 = 7, INF_BAD_VERSION = 8, INF_ZERO_NINE_NOT_FIRST = 9, INF_CODE_POINT_IN_URI = 10, @@ -286,6 +287,8 @@ enum Infraction INF_ACCEPT_ENCODING_CONSECUTIVE_COMMAS = 130, INF_JS_PDU_MISS = 131, INF_JS_SCOPE_NEST_OVERFLOW = 132, + INF_INVALID_SUBVERSION = 133, + INF_VERSION_0 = 134, INF__MAX_VALUE }; @@ -352,7 +355,8 @@ enum EventSid EVENT_CTRL_IN_REASON = 205, EVENT_IMPROPER_WS = 206, EVENT_BAD_VERS = 207, - EVENT_UNKNOWN_VERS = 208, + // EVENT_VERSION_HIGHER_THAN_1 = 208, // Retired. Do not reuse this number + EVENT_BAD_HEADER = 209, EVENT_CHUNK_OPTIONS = 210, EVENT_URI_BAD_FORMAT = 211, @@ -419,6 +423,9 @@ enum EventSid EVENT_ACCEPT_ENCODING_CONSECUTIVE_COMMAS = 272, EVENT_JS_PDU_MISS = 273, EVENT_JS_SCOPE_NEST_OVERFLOW = 274, + EVENT_INVALID_SUBVERSION = 275, + EVENT_VERSION_0 = 276, + EVENT_VERSION_HIGHER_THAN_1 = 277, EVENT__MAX_VALUE }; diff --git a/src/service_inspectors/http_inspect/http_flow_data.h b/src/service_inspectors/http_inspect/http_flow_data.h index c7ce3b6a3..29e6897c4 100644 --- a/src/service_inspectors/http_inspect/http_flow_data.h +++ b/src/service_inspectors/http_inspect/http_flow_data.h @@ -86,6 +86,9 @@ public: uint32_t get_h2_stream_id() const; + HttpEnums::VersionId get_version_id(HttpCommon::SourceId source_id) const + { return version_id[source_id]; } + private: // HTTP/2 handling bool for_http2 = false; diff --git a/src/service_inspectors/http_inspect/http_msg_start.cc b/src/service_inspectors/http_inspect/http_msg_start.cc index 685221863..904366ef8 100644 --- a/src/service_inspectors/http_inspect/http_msg_start.cc +++ b/src/service_inspectors/http_inspect/http_msg_start.cc @@ -52,40 +52,39 @@ void HttpMsgStart::derive_version_id() } else if ((version.start()[5] == '1') && (version.start()[7] == '1')) { - version_id = VERS_1_1; + if (session_data->for_http2) + version_id = VERS_2_0; + else + version_id = VERS_1_1; } else if ((version.start()[5] == '1') && (version.start()[7] == '0')) { version_id = VERS_1_0; } - else if ((version.start()[5] == '2') && (version.start()[7] == '0')) + else if ((version.start()[5] < '0') || (version.start()[5] > '9') || + (version.start()[7] < '0') || (version.start()[7] > '9')) { - version_id = VERS_2_0; + version_id = VERS__PROBLEMATIC; + add_infraction(INF_BAD_VERSION); + create_event(EVENT_BAD_VERS); } - else if ((version.start()[5] == '0') && (version.start()[7] == '9')) + else if ((version.start()[5] > '1') && (version.start()[5] <= '9')) { - // Real 0.9 traffic would never be labeled HTTP/0.9 because 0.9 is older than the version - // system. Aside from the possibility that someone might do this to make trouble, - // HttpStreamSplitter::reassemble() converts 0.9 responses to a simple form of 1.0 format - // to allow us to process 0.9 without a lot of extra development. Such responses are - // labeled 0.9. - // FIXIT-M the 0.9 trick opens the door to someone spoofing us with a real start line - // labeled HTTP/0.9. Need to close this weakness. - // FIXIT-M similarly "HTTP/2.0" is not a legitimate thing we could actually see. - version_id = VERS_0_9; + version_id = VERS__OTHER; + add_infraction(INF_VERSION_HIGHER_THAN_1); + create_event(EVENT_VERSION_HIGHER_THAN_1); } - else if ((version.start()[5] >= '0') && (version.start()[5] <= '9') && - (version.start()[7] >= '0') && (version.start()[7] <= '9')) + else if (version.start()[5] == '1') { version_id = VERS__OTHER; - add_infraction(INF_UNKNOWN_VERSION); - create_event(EVENT_UNKNOWN_VERS); + add_infraction(INF_INVALID_SUBVERSION); + create_event(EVENT_INVALID_SUBVERSION); } else { - version_id = VERS__PROBLEMATIC; - add_infraction(INF_BAD_VERSION); - create_event(EVENT_BAD_VERS); + version_id = VERS__OTHER; + add_infraction(INF_VERSION_0); + create_event(EVENT_VERSION_0); } } 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 eff62701d..98a79d0bf 100644 --- a/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc +++ b/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc @@ -237,12 +237,12 @@ StreamSplitter::Status HttpStreamSplitter::scan(Packet* pkt, const uint8_t* data if ((type == SEC_STATUS) && (session_data->expected_trans_num[SRC_SERVER] == session_data->zero_nine_expected)) { - // 0.9 response is a body that runs to connection end with no headers. HttpInspect does - // not support no headers. Processing this imaginary status line and empty headers allows + // 0.9 response is a body that runs to connection end with no headers. + // Processing this imaginary empty headers allows // us to overcome this limitation and reuse the entire HTTP infrastructure. - prepare_flush(session_data, nullptr, SEC_STATUS, 14, 0, 0, false, 0, 14); - my_inspector->process((const uint8_t*)"HTTP/0.9 200 .", 14, flow, SRC_SERVER, false); - session_data->transaction[SRC_SERVER]->clear_section(); + session_data->version_id[source_id] = VERS_0_9; + session_data->status_code_num = 200; + HttpModule::increment_peg_counts(PEG_RESPONSE); prepare_flush(session_data, nullptr, SEC_HEADER, 0, 0, 0, false, 0, 0); my_inspector->process((const uint8_t*)"", 0, flow, SRC_SERVER, false); session_data->transaction[SRC_SERVER]->clear_section(); diff --git a/src/service_inspectors/http_inspect/http_tables.cc b/src/service_inspectors/http_inspect/http_tables.cc index 1ff388588..4f01f8506 100755 --- a/src/service_inspectors/http_inspect/http_tables.cc +++ b/src/service_inspectors/http_inspect/http_tables.cc @@ -261,7 +261,6 @@ const RuleMap HttpModule::http_events[] = { EVENT_CTRL_IN_REASON, "control character in HTTP response reason phrase" }, { EVENT_IMPROPER_WS, "illegal extra whitespace in start line" }, { EVENT_BAD_VERS, "corrupted HTTP version" }, - { EVENT_UNKNOWN_VERS, "HTTP version in start line is not HTTP/1.0 or 1.1" }, { EVENT_BAD_HEADER, "format error in HTTP header" }, { EVENT_CHUNK_OPTIONS, "chunk header options present" }, { EVENT_URI_BAD_FORMAT, "URI badly formatted" }, @@ -335,6 +334,9 @@ const RuleMap HttpModule::http_events[] = "header" }, { EVENT_JS_PDU_MISS, "missed PDUs during JavaScript normalization" }, { EVENT_JS_SCOPE_NEST_OVERFLOW, "JavaScript scope nesting is over capacity" }, + { EVENT_INVALID_SUBVERSION, "HTTP/1 version other than 1.0 or 1.1" }, + { EVENT_VERSION_0, "HTTP version in start line is 0" }, + { EVENT_VERSION_HIGHER_THAN_1, "HTTP version in start line is higher than 1" }, { 0, nullptr } }; diff --git a/src/service_inspectors/http_inspect/http_transaction.cc b/src/service_inspectors/http_inspect/http_transaction.cc index 84ff55776..c17a5f08a 100644 --- a/src/service_inspectors/http_inspect/http_transaction.cc +++ b/src/service_inspectors/http_inspect/http_transaction.cc @@ -141,7 +141,9 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da // pipeline is empty check for a request transaction and take it. If there is no transaction // available then declare an underflow and create a new transaction specifically for the // response side. - else if (session_data->section_type[source_id] == SEC_STATUS) + else if (session_data->section_type[source_id] == SEC_STATUS || + (session_data->section_type[source_id] == SEC_HEADER && + session_data->version_id[source_id] == VERS_0_9)) { delete_transaction(session_data->transaction[SRC_SERVER], session_data); if (session_data->pipeline_underflow) diff --git a/src/service_inspectors/http_inspect/ips_http.cc b/src/service_inspectors/http_inspect/ips_http.cc index 5603318bf..ffc64fa55 100644 --- a/src/service_inspectors/http_inspect/ips_http.cc +++ b/src/service_inspectors/http_inspect/ips_http.cc @@ -69,6 +69,7 @@ bool HttpRuleOptModule::begin(const char*, int, SnortConfig*) case HTTP_BUFFER_URI: case HTTP_BUFFER_VERSION: case HTTP_RANGE_NUM_HDRS: + case HTTP_VERSION_MATCH: inspect_section = IS_FLEX_HEADER; break; case HTTP_BUFFER_CLIENT_BODY: @@ -87,6 +88,44 @@ bool HttpRuleOptModule::begin(const char*, int, SnortConfig*) return true; } +static const std::map VersionStrToEnum = +{ + { "malformed", VERS__PROBLEMATIC }, + { "other", VERS__OTHER }, + { "1.0", VERS_1_0 }, + { "1.1", VERS_1_1 }, + { "2.0", VERS_2_0 }, + { "0.9", VERS_0_9 } +}; + +bool HttpRuleOptModule::parse_version_list(Value& v) +{ + v.set_first_token(); + std::string tok; + + while ( v.get_next_token(tok) ) + { + if (tok[0] == '"') + tok.erase(0, 1); + + if (tok.length() == 0) + continue; + + if (tok[tok.length()-1] == '"') + tok.erase(tok.length()-1, 1); + + auto iter = VersionStrToEnum.find(tok); + if (iter == VersionStrToEnum.end()) + { + ParseError("Unrecognized version %s\n", tok.c_str()); + return false; + } + + para_list.version_flags[iter->second - VERS__MIN] = true; + } + return true; +} + bool HttpRuleOptModule::set(const char*, Value& v, SnortConfig*) { if (v.is("field")) @@ -171,6 +210,10 @@ bool HttpRuleOptModule::set(const char*, Value& v, SnortConfig*) { return para_list.range.validate(v.get_string(), hdrs_num_range.c_str()); } + else if (v.is("~version_list")) + { + return parse_version_list(v); + } return true; } @@ -207,6 +250,7 @@ void HttpRuleOptModule::HttpRuleParaList::reset() query = false; fragment = false; range.init(); + version_flags = 0; } uint32_t HttpIpsOption::hash() const @@ -216,6 +260,7 @@ uint32_t HttpIpsOption::hash() const uint32_t c = buffer_info.hash(); mix(a,b,c); a += range.hash(); + b += (uint32_t)version_flags.to_ulong(); finalize(a,b,c); return c; } @@ -226,7 +271,8 @@ bool HttpIpsOption::operator==(const IpsOption& ips) const return IpsOption::operator==(ips) && inspect_section == hio.inspect_section && buffer_info == hio.buffer_info && - range == hio.range; + range == hio.range && + version_flags == hio.version_flags; } bool HttpIpsOption::retry(Cursor& current_cursor, const Cursor&) @@ -241,6 +287,20 @@ bool HttpIpsOption::retry(Cursor& current_cursor, const Cursor&) return false; } +IpsOption::EvalStatus HttpIpsOption::eval_version_match(Packet* p, const Http2FlowData* h2i_flow_data) +{ + const HttpFlowData* const flow_data = (h2i_flow_data != nullptr) ? + (HttpFlowData*)h2i_flow_data->get_hi_flow_data(): + (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id); + const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER; + const VersionId version = flow_data->get_version_id(source_id); + + if (version_flags[version - HttpEnums::VERS__MIN]) + return MATCH; + + return NO_MATCH; +} + IpsOption::EvalStatus HttpIpsOption::eval(Cursor& c, Packet* p) { RuleProfile profile(HttpRuleOptModule::http_ps[psi]); @@ -262,7 +322,7 @@ IpsOption::EvalStatus HttpIpsOption::eval(Cursor& c, Packet* p) const HttpInspect* const hi = (h2i_flow_data != nullptr) ? (HttpInspect*)(p->flow->assistant_gadget) : (HttpInspect*)(p->flow->gadget); - if (buffer_info.type <= HTTP_BUFFER_MAX) + if (buffer_info.type <= HTTP__BUFFER_MAX) { const Field& http_buffer = hi->http_get_buf(c, p, buffer_info); @@ -273,6 +333,10 @@ IpsOption::EvalStatus HttpIpsOption::eval(Cursor& c, Packet* p) return MATCH; } + else if (buffer_info.type == HTTP_VERSION_MATCH) + { + return eval_version_match(p, h2i_flow_data); + } else { const int32_t num_lines = hi->http_get_num_headers(p, buffer_info); @@ -1353,6 +1417,52 @@ static const IpsApi num_trailers_api = nullptr }; +//------------------------------------------------------------------------- +// http_version_match +//------------------------------------------------------------------------- +#undef IPS_OPT +#define IPS_OPT "http_version_match" +#undef IPS_HELP +#define IPS_HELP "rule option to match version to listed values" + +static const Parameter version_match_params[] = +{ + { "~version_list", Parameter::PT_STRING, nullptr, nullptr, + "space-separated list of versions to match" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +static Module* version_match_mod_ctor() +{ + return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_VERSION_MATCH, CAT_SET_OTHER, + PSI_VERSION_MATCH, version_match_params); +} + +static const IpsApi version_match_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + version_match_mod_ctor, + HttpRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpIpsOption::opt_ctor, + HttpIpsOption::opt_dtor, + nullptr +}; + //------------------------------------------------------------------------- // plugins //------------------------------------------------------------------------- @@ -1377,5 +1487,5 @@ const BaseApi* ips_http_trailer = &trailer_api.base; const BaseApi* ips_http_true_ip = &true_ip_api.base; const BaseApi* ips_http_uri = &uri_api.base; const BaseApi* ips_http_version = &version_api.base; +const BaseApi* ips_http_version_match = &version_match_api.base; const BaseApi* ips_js_data = &js_data_api.base; - diff --git a/src/service_inspectors/http_inspect/ips_http.h b/src/service_inspectors/http_inspect/ips_http.h index 46e30e16f..eda137cbf 100644 --- a/src/service_inspectors/http_inspect/ips_http.h +++ b/src/service_inspectors/http_inspect/ips_http.h @@ -31,12 +31,13 @@ #include "http_enum.h" class HttpInspect; +class Http2FlowData; enum PsIdx { PSI_CLIENT_BODY, PSI_COOKIE, PSI_HEADER, PSI_METHOD, PSI_PARAM, PSI_RAW_BODY, PSI_RAW_COOKIE, PSI_RAW_HEADER, PSI_RAW_REQUEST, PSI_RAW_STATUS, PSI_RAW_TRAILER, PSI_RAW_URI, PSI_STAT_CODE, PSI_STAT_MSG, PSI_TRAILER, PSI_TRUE_IP, PSI_URI, PSI_VERSION, PSI_JS_DATA, PSI_VBA_DATA, - PSI_RANGE_NUM_HDRS, PSI_RANGE_NUM_TRAILERS, PSI_MAX }; + PSI_RANGE_NUM_HDRS, PSI_RANGE_NUM_TRAILERS, PSI_VERSION_MATCH, PSI_MAX }; class HttpRuleOptModule : public snort::Module { @@ -61,6 +62,7 @@ public: private: friend class HttpIpsOption; static THREAD_LOCAL std::array http_ps; + static const int version_size = HttpEnums::VERS__MAX - HttpEnums::VERS__MIN + 1; struct HttpRuleParaList { @@ -79,6 +81,7 @@ private: bool query; bool fragment; snort::RangeCheck range; + std::bitset version_flags; void reset(); }; @@ -92,6 +95,8 @@ private: HttpEnums::InspectSection inspect_section; uint64_t sub_id; uint64_t form; + + bool parse_version_list(snort::Value& v); }; class HttpIpsOption : public snort::IpsOption @@ -102,7 +107,8 @@ public: key(cm->key), cat(cm->cat), psi(cm->psi), inspect_section(cm->inspect_section), buffer_info(cm->rule_opt_index, cm->sub_id, cm->form, - cm->para_list.param, cm->para_list.nocase), range(cm->para_list.range){} + cm->para_list.param, cm->para_list.nocase), range(cm->para_list.range), + version_flags(cm->para_list.version_flags){} snort::CursorActionType get_cursor_type() const override { return cat; } EvalStatus eval(Cursor&, snort::Packet*) override; uint32_t hash() const override; @@ -119,6 +125,9 @@ private: const HttpEnums::InspectSection inspect_section; HttpBufferInfo buffer_info; const snort::RangeCheck range; + const std::bitset version_flags; + + IpsOption::EvalStatus eval_version_match(snort::Packet* p, const Http2FlowData* h2i_flow_data); }; #endif