From: Tom Peters (thopeter) Date: Tue, 22 Feb 2022 23:04:21 +0000 (+0000) Subject: Pull request #3270: US 727968: http_inspect: refactor HttpIpsOption X-Git-Tag: 3.1.24.0~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d53d9eae2d617d5bc24b4cb1a8069dab3560b089;p=thirdparty%2Fsnort3.git Pull request #3270: US 727968: http_inspect: refactor HttpIpsOption Merge in SNORT/snort3 from ~MDAGON/snort3:refactor_ips to master Squashed commit of the following: commit 2791042eff639fe3d50139a9b63396841ee1a862 Author: Maya Dagon Date: Thu Feb 10 17:26:51 2022 -0500 http_inspect: refactor rule options --- diff --git a/doc/user/http_inspect.txt b/doc/user/http_inspect.txt index d47ed2641..107e26029 100755 --- a/doc/user/http_inspect.txt +++ b/doc/user/http_inspect.txt @@ -685,7 +685,7 @@ The vba_data will contain the decompressed Visual Basic for Applications (vba) macro data embedded in MS office files. It requires decompress_zip and decompress_vba options enabled. -===== num_headers and num_trailers +===== http_num_headers and http_num_trailers These rule options are used to check the number of headers and trailers, respectively. Checks available: equal to "=" or just value, diff --git a/src/service_inspectors/http_inspect/CMakeLists.txt b/src/service_inspectors/http_inspect/CMakeLists.txt index d4972a001..0a151aac0 100644 --- a/src/service_inspectors/http_inspect/CMakeLists.txt +++ b/src/service_inspectors/http_inspect/CMakeLists.txt @@ -72,6 +72,14 @@ set (FILE_LIST http_event.h http_js_norm.cc http_js_norm.h + ips_http_buffer.cc + ips_http_buffer.h + ips_http_num_hdrs.cc + ips_http_num_hdrs.h + ips_http_param.cc + ips_http_param.h + ips_http_version.cc + ips_http_version.h ) #if (STATIC_INSPECTORS) diff --git a/src/service_inspectors/http_inspect/dev_notes.txt b/src/service_inspectors/http_inspect/dev_notes.txt index 802f25826..aa451bae3 100755 --- a/src/service_inspectors/http_inspect/dev_notes.txt +++ b/src/service_inspectors/http_inspect/dev_notes.txt @@ -336,6 +336,12 @@ generated and processing continues normally. If there is no separator at all tha Then we return to #1 as the next chunk begins. In particular extra separators beyond the two expected are attributed to the beginning of the next chunk. +Rule options: + +HttpIpsOption is the base class for http rule options. It supports the commonly used parameters: +field, request, with_body, with_header and with_trailer. HttpBufferIpsOption is a rule option that +sets a buffer. It implements most of the rule options. + Test tool usage instructions: The HI test tool consists of two features. test_output provides extensive information about the diff --git a/src/service_inspectors/http_inspect/http_buffer_info.cc b/src/service_inspectors/http_inspect/http_buffer_info.cc index fdb5f97cb..c8443981c 100644 --- a/src/service_inspectors/http_inspect/http_buffer_info.cc +++ b/src/service_inspectors/http_inspect/http_buffer_info.cc @@ -33,37 +33,16 @@ uint32_t HttpBufferInfo::hash() const uint32_t c = sub_id & 0xFFFFFFFF; uint32_t d = form >> 32; uint32_t e = form & 0xFFFFFFFF; - uint32_t f = 0; mix(a,b,c); - if (param) - f = param->is_nocase() ? 1 : 0; - mix(d,e,f); - mix(a,c,f); - if (param) - mix_str(a,c,f,param->c_str(),param->length()); - finalize(a,c,f); - return f; + mix(d,e,a); + finalize(d,e,a); + return d; } bool HttpBufferInfo::operator==(const HttpBufferInfo& rhs) const { - bool param_match = false; - - if (param && rhs.param) - { - HttpParam& lhs_param = *param; - HttpParam& rhs_param = *rhs.param; - - param_match = (lhs_param == rhs_param); - } - else if (!param && !rhs.param) - { - param_match = true; - } - - return type == rhs.type && - sub_id == rhs.sub_id && - form == rhs.form && - param_match; + return (type == rhs.type && + sub_id == rhs.sub_id && + form == rhs.form); } diff --git a/src/service_inspectors/http_inspect/http_buffer_info.h b/src/service_inspectors/http_inspect/http_buffer_info.h index df7df0525..cda4a2823 100644 --- a/src/service_inspectors/http_inspect/http_buffer_info.h +++ b/src/service_inspectors/http_inspect/http_buffer_info.h @@ -20,37 +20,20 @@ #ifndef HTTP_BUFFER_INFO_H #define HTTP_BUFFER_INFO_H -#include - -#include "http_enum.h" -#include "http_param.h" - class HttpBufferInfo { public: HttpBufferInfo(unsigned type_, uint64_t sub_id_ = 0, uint64_t form_ = 0) : type(type_), sub_id(sub_id_), form(form_) {} - HttpBufferInfo(unsigned type_, uint64_t sub_id_, uint64_t form_, - const std::string& param_str, bool nocase) - : type(type_), sub_id(sub_id_), form(form_) - { - if (param_str.length() > 0) - param = new HttpParam(param_str, nocase); - } - - ~HttpBufferInfo() - { delete param; } - uint32_t hash() const; bool operator==(const HttpBufferInfo& rhs) const; public: - unsigned type; - uint64_t sub_id; - uint64_t form; - HttpParam* param = nullptr; + const unsigned type; + const uint64_t sub_id; + const uint64_t form; }; #endif diff --git a/src/service_inspectors/http_inspect/http_inspect.cc b/src/service_inspectors/http_inspect/http_inspect.cc index 6cbde96c9..fbb373d33 100755 --- a/src/service_inspectors/http_inspect/http_inspect.cc +++ b/src/service_inspectors/http_inspect/http_inspect.cc @@ -48,6 +48,7 @@ #include "http_msg_request.h" #include "http_msg_status.h" #include "http_msg_trailer.h" +#include "http_param.h" #include "http_test_manager.h" using namespace snort; @@ -261,10 +262,9 @@ bool HttpInspect::get_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffe bool HttpInspect::get_buf(unsigned id, Packet* p, InspectionBuffer& b) { - Cursor c; HttpBufferInfo buffer_info(id); - const Field& http_buffer = http_get_buf(c, p, buffer_info); + const Field& http_buffer = http_get_buf(p, buffer_info); if (http_buffer.length() <= 0) return false; @@ -274,15 +274,25 @@ bool HttpInspect::get_buf(unsigned id, Packet* p, InspectionBuffer& b) return true; } -const Field& HttpInspect::http_get_buf(Cursor& c, Packet* p, - const HttpBufferInfo& buffer_info) const +const Field& HttpInspect::http_get_buf(Packet* p, const HttpBufferInfo& buffer_info) const +{ + HttpMsgSection* const current_section = HttpContextData::get_snapshot(p); + + if (current_section == nullptr) + return Field::FIELD_NULL; + + return current_section->get_classic_buffer(buffer_info); +} + +const Field& HttpInspect::http_get_param_buf(Cursor& c, Packet* p, + const HttpParam& param) const { HttpMsgSection* const current_section = HttpContextData::get_snapshot(p); if (current_section == nullptr) return Field::FIELD_NULL; - return current_section->get_classic_buffer(c, buffer_info); + return current_section->get_param_buffer(c, param); } int32_t HttpInspect::http_get_num_headers(Packet* p, @@ -296,14 +306,15 @@ int32_t HttpInspect::http_get_num_headers(Packet* p, return current_section->get_num_headers(buffer_info); } -VersionId HttpInspect::http_get_version_id(Packet* p) const +VersionId HttpInspect::http_get_version_id(Packet* p, + const HttpBufferInfo& buffer_info) const { const HttpMsgSection* const current_section = HttpContextData::get_snapshot(p); if (current_section == nullptr) return VERS__NOT_PRESENT; - return current_section->get_version_id(); + return current_section->get_version_id(buffer_info); } bool HttpInspect::get_fp_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b) diff --git a/src/service_inspectors/http_inspect/http_inspect.h b/src/service_inspectors/http_inspect/http_inspect.h index 0f80b421f..44021533a 100644 --- a/src/service_inspectors/http_inspect/http_inspect.h +++ b/src/service_inspectors/http_inspect/http_inspect.h @@ -37,6 +37,7 @@ #include "http_stream_splitter.h" class HttpApi; +class HttpParam; class HttpInspect : public snort::Inspector { @@ -47,10 +48,12 @@ public: bool get_buf(snort::InspectionBuffer::Type ibt, snort::Packet* p, snort::InspectionBuffer& b) override; bool get_buf(unsigned id, snort::Packet* p, snort::InspectionBuffer& b) override; - const Field& http_get_buf(Cursor& c, snort::Packet* p, - const HttpBufferInfo& buffer_info) const; + const Field& http_get_buf(snort::Packet* p, const HttpBufferInfo& buffer_info) const; + const Field& http_get_param_buf(Cursor& c, snort::Packet* p, + const HttpParam& param) const; int32_t http_get_num_headers(snort::Packet* p, const HttpBufferInfo& buffer_info) const; - HttpEnums::VersionId http_get_version_id(snort::Packet* p) const; + HttpEnums::VersionId http_get_version_id(snort::Packet* p, + const HttpBufferInfo& buffer_info) const; bool get_fp_buf(snort::InspectionBuffer::Type ibt, snort::Packet* p, snort::InspectionBuffer& b) override; bool configure(snort::SnortConfig*) override; diff --git a/src/service_inspectors/http_inspect/http_msg_section.cc b/src/service_inspectors/http_inspect/http_msg_section.cc index 0599c8043..486539b8b 100644 --- a/src/service_inspectors/http_inspect/http_msg_section.cc +++ b/src/service_inspectors/http_inspect/http_msg_section.cc @@ -155,13 +155,12 @@ const Field& HttpMsgSection::classic_normalize(const Field& raw, Field& norm, const Field& HttpMsgSection::get_classic_buffer(unsigned id, uint64_t sub_id, uint64_t form) { - Cursor c; HttpBufferInfo buffer_info(id, sub_id, form); - return get_classic_buffer(c, buffer_info); + return get_classic_buffer(buffer_info); } -const Field& HttpMsgSection::get_classic_buffer(Cursor& c, const HttpBufferInfo& buf) +const Field& HttpMsgSection::get_classic_buffer(const HttpBufferInfo& buf) { // buffer_side replaces source_id for buffers that support the request option const SourceId buffer_side = (buf.form & FORM_REQUEST) ? SRC_CLIENT : source_id; @@ -197,120 +196,6 @@ const Field& HttpMsgSection::get_classic_buffer(Cursor& c, const HttpBufferInfo& { return (request != nullptr) ? request->get_method() : Field::FIELD_NULL; } - case HTTP_BUFFER_PARAM: - { - if (buf.param == nullptr || request == nullptr) - return Field::FIELD_NULL; - - HttpUri* query = request->get_http_uri(); - HttpMsgBody* body = (source_id == SRC_CLIENT) ? get_body() : nullptr; - - if (query == nullptr && body == nullptr) - return Field::FIELD_NULL; - - const HttpParaList::UriParam& uri_config = params->uri_param; - - ParameterMap& query_params = request->get_query_params(); - ParameterMap& body_params = request->get_body_params(); - - // cache lookup - HttpParam& param = *buf.param; - ParameterData& query_data = query_params[param.str_upper()]; - ParameterData& body_data = body_params[param.str_upper()]; - - if (!query_data.parsed && query != nullptr) - { - // query has not been parsed for this parameter - const Field& rq = query->get_query(); - const Field& nq = query->get_norm_query(); - - if (rq.length() > 0 && nq.length() > 0) - { - HttpQueryParser parser(rq.start(), rq.length(), - nq.start(), nq.length(), uri_config, - session_data, source_id); - - parser.parse(param, query_data); - query_data.parsed = true; - } - } - - if (!body_data.parsed && body != nullptr) - { - // body has not been parsed for this parameter - const Field& rb = body->get_detect_data(); - const Field& nb = body->get_classic_client_body(); - - if (rb.length() > 0 && nb.length() > 0 && body->is_first()) - { - HttpQueryParser parser(rb.start(), rb.length(), - nb.start(), nb.length(), uri_config, - session_data, source_id); - - parser.parse(param, body_data); - body_data.parsed = true; - } - } - - KeyValueVec& query_kv = query_data.kv_vec; - KeyValueVec& body_kv = body_data.kv_vec; - - unsigned num_query_params = query_kv.size(); - unsigned num_body_params = body_kv.size(); - - if (num_query_params == 0 && num_body_params == 0) - return Field::FIELD_NULL; - - // get data stored on the cursor - HttpCursorData* cd = (HttpCursorData*)c.get_data(HttpCursorData::id); - - if (!cd) - { - cd = new HttpCursorData(); - c.set_data(cd); - } - - // save the parameter count on the cursor - cd->num_query_params = num_query_params; - cd->num_body_params = num_body_params; - - unsigned& query_index = cd->query_index; - unsigned& body_index = cd->body_index; - - while (query_index < num_query_params) - { - KeyValue* fields = query_kv[query_index]; - - Field& key = fields->key; - Field& value = fields->value; - - ++query_index; - - if (param.is_nocase()) - return value; - - if (!memcmp(key.start(), param.c_str(), key.length())) - return value; - } - - while (body_index < num_body_params) - { - KeyValue* fields = body_kv[body_index]; - - Field& key = fields->key; - Field& value = fields->value; - - ++body_index; - - if (param.is_nocase()) - return value; - - if (!memcmp(key.start(), param.c_str(), key.length())) - return value; - } - - return Field::FIELD_NULL; - } case HTTP_BUFFER_RAW_BODY: { return (get_body() != nullptr) ? get_body()->msg_text : Field::FIELD_NULL; @@ -404,6 +289,120 @@ const Field& HttpMsgSection::get_classic_buffer(Cursor& c, const HttpBufferInfo& } } +const Field& HttpMsgSection::get_param_buffer(Cursor& c, const HttpParam& param) +{ + if (request == nullptr) + return Field::FIELD_NULL; + + HttpUri* query = request->get_http_uri(); + HttpMsgBody* body = (source_id == SRC_CLIENT) ? get_body() : nullptr; + + if (query == nullptr && body == nullptr) + return Field::FIELD_NULL; + + const HttpParaList::UriParam& uri_config = params->uri_param; + + ParameterMap& query_params = request->get_query_params(); + ParameterMap& body_params = request->get_body_params(); + + // cache lookup + ParameterData& query_data = query_params[param.str_upper()]; + ParameterData& body_data = body_params[param.str_upper()]; + + if (!query_data.parsed && query != nullptr) + { + // query has not been parsed for this parameter + const Field& rq = query->get_query(); + const Field& nq = query->get_norm_query(); + + if (rq.length() > 0 && nq.length() > 0) + { + HttpQueryParser parser(rq.start(), rq.length(), + nq.start(), nq.length(), uri_config, + session_data, source_id); + + parser.parse(param, query_data); + query_data.parsed = true; + } + } + + if (!body_data.parsed && body != nullptr) + { + // body has not been parsed for this parameter + const Field& rb = body->get_detect_data(); + const Field& nb = body->get_classic_client_body(); + + if (rb.length() > 0 && nb.length() > 0 && body->is_first()) + { + HttpQueryParser parser(rb.start(), rb.length(), + nb.start(), nb.length(), uri_config, + session_data, source_id); + + parser.parse(param, body_data); + body_data.parsed = true; + } + } + + KeyValueVec& query_kv = query_data.kv_vec; + KeyValueVec& body_kv = body_data.kv_vec; + + unsigned num_query_params = query_kv.size(); + unsigned num_body_params = body_kv.size(); + + if (num_query_params == 0 && num_body_params == 0) + return Field::FIELD_NULL; + + // get data stored on the cursor + HttpCursorData* cd = (HttpCursorData*)c.get_data(HttpCursorData::id); + + if (!cd) + { + cd = new HttpCursorData(); + c.set_data(cd); + } + + // save the parameter count on the cursor + cd->num_query_params = num_query_params; + cd->num_body_params = num_body_params; + + unsigned& query_index = cd->query_index; + unsigned& body_index = cd->body_index; + + while (query_index < num_query_params) + { + KeyValue* fields = query_kv[query_index]; + + Field& key = fields->key; + Field& value = fields->value; + + ++query_index; + + if (param.is_nocase()) + return value; + + if (!memcmp(key.start(), param.c_str(), key.length())) + return value; + } + + while (body_index < num_body_params) + { + KeyValue* fields = body_kv[body_index]; + + Field& key = fields->key; + Field& value = fields->value; + + ++body_index; + + if (param.is_nocase()) + return value; + + if (!memcmp(key.start(), param.c_str(), key.length())) + return value; + } + + return Field::FIELD_NULL; +} + int32_t HttpMsgSection::get_num_headers(const HttpBufferInfo& buf) const { // buffer_side replaces source_id for buffers that support the request option @@ -418,6 +417,16 @@ int32_t HttpMsgSection::get_num_headers(const HttpBufferInfo& buf) const return head->get_num_headers(); } +VersionId HttpMsgSection::get_version_id(const HttpBufferInfo& buf) const +{ + // buffer_side replaces source_id for buffers that support the request option + const SourceId buffer_side = (buf.form & FORM_REQUEST) ? SRC_CLIENT : source_id; + HttpMsgStart* start = (buffer_side == SRC_CLIENT) ? + (HttpMsgStart*)request : (HttpMsgStart*)status; + + return (start != nullptr) ? start->get_version_id() : VERS__NOT_PRESENT; +} + void HttpMsgSection::get_related_sections() { // When a message section is created these relationships become fixed so we make copies for diff --git a/src/service_inspectors/http_inspect/http_msg_section.h b/src/service_inspectors/http_inspect/http_msg_section.h index 838ff75b5..58d6c39f7 100644 --- a/src/service_inspectors/http_inspect/http_msg_section.h +++ b/src/service_inspectors/http_inspect/http_msg_section.h @@ -30,6 +30,7 @@ #include "http_field.h" #include "http_flow_data.h" #include "http_module.h" +#include "http_param.h" #include "http_transaction.h" //------------------------------------------------------------------------- @@ -67,7 +68,8 @@ public: virtual void update_flow() = 0; const Field& get_classic_buffer(unsigned id, uint64_t sub_id, uint64_t form); - const Field& get_classic_buffer(Cursor& c, const HttpBufferInfo& buf); + const Field& get_classic_buffer(const HttpBufferInfo& buf); + const Field& get_param_buffer(Cursor& c, const HttpParam& param); HttpEnums::MethodId get_method_id() const { return method_id; } @@ -81,7 +83,7 @@ public: uint64_t get_transaction_id() { return trans_num; } int32_t get_num_headers(const HttpBufferInfo& buf) const; - HttpEnums::VersionId get_version_id() const { return version_id; } + HttpEnums::VersionId get_version_id(const HttpBufferInfo& buf) const; HttpMsgSection* next = nullptr; diff --git a/src/service_inspectors/http_inspect/http_msg_start.h b/src/service_inspectors/http_inspect/http_msg_start.h index ceabd6a63..6d046292f 100644 --- a/src/service_inspectors/http_inspect/http_msg_start.h +++ b/src/service_inspectors/http_inspect/http_msg_start.h @@ -34,6 +34,7 @@ public: void analyze() override; bool detection_required() const override { return false; } const Field& get_version() const { return version; } + HttpEnums::VersionId get_version_id() const { return version_id; } protected: HttpMsgStart(const uint8_t* buffer, const uint16_t buf_size, HttpFlowData* session_data_, diff --git a/src/service_inspectors/http_inspect/ips_http.cc b/src/service_inspectors/http_inspect/ips_http.cc index 49898ab54..7408ad05e 100644 --- a/src/service_inspectors/http_inspect/ips_http.cc +++ b/src/service_inspectors/http_inspect/ips_http.cc @@ -35,94 +35,18 @@ #include "http_flow_data.h" #include "http_inspect.h" #include "http_msg_head_shared.h" -#include "http_param.h" using namespace snort; using namespace HttpCommon; using namespace HttpEnums; -THREAD_LOCAL std::array HttpRuleOptModule::http_ps; - -const std::string hdrs_num_range = "0:" + std::to_string(HttpMsgHeadShared::MAX_HEADERS); bool HttpRuleOptModule::begin(const char*, int, SnortConfig*) { para_list.reset(); sub_id = 0; form = 0; - switch (rule_opt_index) - { - case HTTP_BUFFER_RAW_STATUS: - case HTTP_BUFFER_STAT_CODE: - case HTTP_BUFFER_STAT_MSG: - inspect_section = IS_HEADER; - break; - case HTTP_BUFFER_COOKIE: - case HTTP_BUFFER_HEADER: - case HTTP_BUFFER_METHOD: - case HTTP_BUFFER_PARAM: - case HTTP_BUFFER_RAW_COOKIE: - case HTTP_BUFFER_RAW_HEADER: - case HTTP_BUFFER_RAW_REQUEST: - case HTTP_BUFFER_RAW_URI: - case HTTP_BUFFER_TRUE_IP: - 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: - case HTTP_BUFFER_RAW_BODY: - case BUFFER_JS_DATA: - inspect_section = IS_BODY; - break; - case HTTP_BUFFER_RAW_TRAILER: - case HTTP_BUFFER_TRAILER: - case HTTP_RANGE_NUM_TRAILERS: - inspect_section = IS_TRAILER; - break; - default: - assert(false); - } - 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; - } + is_trailer_opt = false; return true; } @@ -145,17 +69,6 @@ bool HttpRuleOptModule::set(const char*, Value& v, SnortConfig*) if (sub_id == STAT_OTHER) ParseError("Unrecognized header field name"); } - else if (v.is("~param")) - { - std::string bc = v.get_string(); - bool negated = false; - if (!parse_byte_code(bc.c_str(), negated, para_list.param) or negated) - ParseError("Invalid http_param"); - } - else if (v.is("nocase")) - { - para_list.nocase = true; - } else if (v.is("request")) { para_list.request = true; @@ -176,44 +89,6 @@ bool HttpRuleOptModule::set(const char*, Value& v, SnortConfig*) para_list.with_trailer = true; inspect_section = IS_TRAILER; } - else if (v.is("scheme")) - { - para_list.scheme = true; - sub_id = UC_SCHEME; - } - else if (v.is("host")) - { - para_list.host = true; - sub_id = UC_HOST; - } - else if (v.is("port")) - { - para_list.port = true; - sub_id = UC_PORT; - } - else if (v.is("path")) - { - para_list.path = true; - sub_id = UC_PATH; - } - else if (v.is("query")) - { - para_list.query = true; - sub_id = UC_QUERY; - } - else if (v.is("fragment")) - { - para_list.fragment = true; - sub_id = UC_FRAGMENT; - } - else if (v.is("~range")) - { - 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; } @@ -222,35 +97,19 @@ bool HttpRuleOptModule::end(const char*, int, SnortConfig*) // Check for option conflicts if (para_list.with_header + para_list.with_body + para_list.with_trailer > 1) ParseError("Only specify one with_ option. Use the one that happens last."); - if (((rule_opt_index == HTTP_BUFFER_TRAILER) || (rule_opt_index == HTTP_BUFFER_RAW_TRAILER) || (rule_opt_index == HTTP_RANGE_NUM_TRAILERS)) && - (para_list.with_header || para_list.with_body) && + if ( is_trailer_opt && (para_list.with_header || para_list.with_body) && !para_list.request) ParseError("Trailers with with_ option must also specify request"); - if (para_list.scheme + para_list.host + para_list.port + para_list.path + para_list.query + - para_list.fragment > 1) - ParseError("Only specify one part of the URI"); - if (rule_opt_index == HTTP_BUFFER_PARAM && para_list.param.length() == 0) - ParseError("Specify parameter name"); return true; } void HttpRuleOptModule::HttpRuleParaList::reset() { field.clear(); - param.clear(); - nocase = false; request = false; with_header = false; with_body = false; with_trailer = false; - scheme = false; - host = false; - port = false; - path = false; - query = false; - fragment = false; - range.init(); - version_flags = 0; } uint32_t HttpIpsOption::hash() const @@ -259,8 +118,6 @@ uint32_t HttpIpsOption::hash() const uint32_t b = (uint32_t)inspect_section; 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; } @@ -270,39 +127,14 @@ bool HttpIpsOption::operator==(const IpsOption& ips) const const HttpIpsOption& hio = static_cast(ips); return IpsOption::operator==(ips) && inspect_section == hio.inspect_section && - buffer_info == hio.buffer_info && - range == hio.range && - version_flags == hio.version_flags; -} - -bool HttpIpsOption::retry(Cursor& current_cursor, const Cursor&) -{ - if (buffer_info.type == HTTP_BUFFER_PARAM) - { - HttpCursorData* cd = (HttpCursorData*)current_cursor.get_data(HttpCursorData::id); - - if (cd) - return cd->retry(); - } - return false; -} - -IpsOption::EvalStatus HttpIpsOption::eval_version_match(Packet* p, const HttpInspect* hi) -{ - const VersionId version = hi->http_get_version_id(p); - - if (version_flags[version - HttpEnums::VERS__MIN]) - return MATCH; - - return NO_MATCH; + buffer_info == hio.buffer_info; } -IpsOption::EvalStatus HttpIpsOption::eval(Cursor& c, Packet* p) +// Verify inspect_section matches. If it does get inspector pointer. +HttpInspect const* HttpIpsOption::eval_helper(Packet* p) { - RuleProfile profile(HttpRuleOptModule::http_ps[psi]); - if (!p->flow || !p->flow->gadget || (HttpInspect::get_latest_is(p) == IS_NONE)) - return NO_MATCH; + return nullptr; const bool section_match = (HttpInspect::get_latest_is(p) == inspect_section) || @@ -310,7 +142,7 @@ IpsOption::EvalStatus HttpIpsOption::eval(Cursor& c, Packet* p) ((HttpInspect::get_latest_is(p) == IS_FIRST_BODY) && (inspect_section == IS_BODY)) || ((HttpInspect::get_latest_src(p) == SRC_CLIENT) && (inspect_section == IS_FLEX_HEADER)); if (!section_match) - return NO_MATCH; + return nullptr; const Http2FlowData* const h2i_flow_data = (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id); @@ -318,1170 +150,6 @@ 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) - { - const Field& http_buffer = hi->http_get_buf(c, p, buffer_info); - - if (http_buffer.length() <= 0) - return NO_MATCH; - - c.set(key, http_buffer.start(), http_buffer.length()); - - return MATCH; - } - else if (buffer_info.type == HTTP_VERSION_MATCH) - { - return eval_version_match(p, hi); - } - else - { - const int32_t num_lines = hi->http_get_num_headers(p, buffer_info); - if (num_lines != HttpCommon::STAT_NOT_PRESENT && range.eval(num_lines)) - return MATCH; - - return NO_MATCH; - } -} - -//------------------------------------------------------------------------- -// http_client_body -//------------------------------------------------------------------------- - -#undef IPS_OPT -#define IPS_OPT "http_client_body" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the request body" - -static Module* client_body_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_CLIENT_BODY, CAT_SET_BODY, - PSI_CLIENT_BODY); -} - -static const IpsApi client_body_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - client_body_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_cookie -//------------------------------------------------------------------------- - -static const Parameter http_cookie_params[] = -{ - { "request", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against the cookie from the request message even when examining the response" }, - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_cookie" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the HTTP cookie" - -static Module* cookie_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_COOKIE, CAT_SET_COOKIE, PSI_COOKIE, - http_cookie_params); -} - -static const IpsApi cookie_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - cookie_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_header -//------------------------------------------------------------------------- - -// FIXIT-M add match_unknown option to look at HEAD__UNKNOWN. - -// FIXIT-M if http_header is the fast pattern buffer and the content to be -// matched appears in the normalized field but not in the raw field -// detection will fail. - -static const Parameter http_header_params[] = -{ - { "field", Parameter::PT_STRING, nullptr, nullptr, - "restrict to given header. Header name is case insensitive." }, - { "request", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against the headers from the request message even when examining the response" }, - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_header" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the normalized headers" - -static Module* header_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_HEADER, CAT_SET_HEADER, - PSI_HEADER, http_header_params); -} - -static const IpsApi header_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - header_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_method -//------------------------------------------------------------------------- - -static const Parameter http_method_params[] = -{ - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_method" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the HTTP request method" - -static Module* method_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_METHOD, CAT_SET_METHOD, PSI_METHOD, - http_method_params); -} - -static const IpsApi method_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - method_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_param -//------------------------------------------------------------------------- - -static const Parameter http_param_params[] = -{ - { "~param", Parameter::PT_STRING, nullptr, nullptr, - "parameter to match" }, - { "nocase", Parameter::PT_IMPLIED, nullptr, nullptr, - "case insensitive match" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_param" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the value of the specified HTTP parameter key which may be in the query or body" - -static Module* param_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_PARAM, CAT_SET_OTHER, PSI_PARAM, - http_param_params); -} - -static const IpsApi param_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - param_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_raw_body -//------------------------------------------------------------------------- - -#undef IPS_OPT -#define IPS_OPT "http_raw_body" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the unnormalized message body" - -static Module* raw_body_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_BODY, CAT_SET_OTHER, - PSI_RAW_BODY); -} - -static const IpsApi raw_body_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - raw_body_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_raw_cookie -//------------------------------------------------------------------------- - -static const Parameter http_raw_cookie_params[] = -{ - { "request", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against the cookie from the request message even when examining the response" }, - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_raw_cookie" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the unnormalized cookie" - -static Module* raw_cookie_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_COOKIE, CAT_SET_OTHER, - PSI_RAW_COOKIE, http_raw_cookie_params); -} - -static const IpsApi raw_cookie_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - raw_cookie_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_raw_header -//------------------------------------------------------------------------- - -static const Parameter http_raw_header_params[] = -{ - { "field", Parameter::PT_STRING, nullptr, nullptr, - "restrict to given header. Header name is case insensitive." }, - { "request", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against the headers from the request message even when examining the response" }, - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_raw_header" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the unnormalized headers" - -static Module* raw_header_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_HEADER, CAT_SET_RAW_HEADER, - PSI_RAW_HEADER, http_raw_header_params); + return hi; } -static const IpsApi raw_header_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - raw_header_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_raw_request -//------------------------------------------------------------------------- - -static const Parameter http_raw_request_params[] = -{ - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_raw_request" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the unnormalized request line" - -static Module* raw_request_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_REQUEST, CAT_SET_OTHER, - PSI_RAW_REQUEST, http_raw_request_params); -} - -static const IpsApi raw_request_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - raw_request_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_raw_status -//------------------------------------------------------------------------- - -static const Parameter http_raw_status_params[] = -{ - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_raw_status" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the unnormalized status line" - -static Module* raw_status_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_STATUS, CAT_SET_OTHER, - PSI_RAW_STATUS, http_raw_status_params); -} - -static const IpsApi raw_status_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - raw_status_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_raw_trailer -//------------------------------------------------------------------------- - -static const Parameter http_raw_trailer_params[] = -{ - { "field", Parameter::PT_STRING, nullptr, nullptr, - "restrict to given trailer. Trailer name is case insensitive." }, - { "request", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against the trailers from the request message even when examining the response" }, - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP response message headers (must be combined with request)" - }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP response message body (must be combined with request)" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_raw_trailer" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the unnormalized trailers" - -static Module* raw_trailer_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_TRAILER, CAT_SET_RAW_HEADER, - PSI_RAW_TRAILER, http_raw_trailer_params); -} - -static const IpsApi raw_trailer_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - raw_trailer_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_raw_uri -//------------------------------------------------------------------------- - -static const Parameter http_raw_uri_params[] = -{ - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { "scheme", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against scheme section of URI only" }, - { "host", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against host section of URI only" }, - { "port", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against port section of URI only" }, - { "path", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against path section of URI only" }, - { "query", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against query section of URI only" }, - { "fragment", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against fragment section of URI only" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_raw_uri" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the unnormalized URI" - -static Module* raw_uri_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_URI, CAT_SET_RAW_KEY, - PSI_RAW_URI, http_raw_uri_params); -} - -static const IpsApi raw_uri_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - raw_uri_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_stat_code -//------------------------------------------------------------------------- - -static const Parameter http_stat_code_params[] = -{ - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_stat_code" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the HTTP status code" - -static Module* stat_code_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_STAT_CODE, CAT_SET_STAT_CODE, - PSI_STAT_CODE, http_stat_code_params); -} - -static const IpsApi stat_code_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - stat_code_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_stat_msg -//------------------------------------------------------------------------- - -static const Parameter http_stat_msg_params[] = -{ - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_stat_msg" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the HTTP status message" - -static Module* stat_msg_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_STAT_MSG, CAT_SET_STAT_MSG, - PSI_STAT_MSG, http_stat_msg_params); -} - -static const IpsApi stat_msg_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - stat_msg_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_trailer -//------------------------------------------------------------------------- - -static const Parameter http_trailer_params[] = -{ - { "field", Parameter::PT_STRING, nullptr, nullptr, "restrict to given trailer" }, - { "request", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against the trailers from the request message even when examining the response" }, - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP response message headers (must be combined with request)" - }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body (must be combined with request)" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_trailer" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the normalized trailers" - -static Module* trailer_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_TRAILER, CAT_SET_HEADER, - PSI_TRAILER, http_trailer_params); -} - -static const IpsApi trailer_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - trailer_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_true_ip -//------------------------------------------------------------------------- - -static const Parameter http_true_ip_params[] = -{ - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_true_ip" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the final client IP address" - -static Module* true_ip_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_TRUE_IP, CAT_SET_OTHER, - PSI_TRUE_IP, http_true_ip_params); -} - -static const IpsApi true_ip_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - true_ip_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_uri -//------------------------------------------------------------------------- - -static const Parameter http_uri_params[] = -{ - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { "scheme", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against scheme section of URI only" }, - { "host", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against host section of URI only" }, - { "port", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against port section of URI only" }, - { "path", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against path section of URI only" }, - { "query", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against query section of URI only" }, - { "fragment", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against fragment section of URI only" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_uri" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the normalized URI buffer" - -static Module* uri_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_URI, CAT_SET_KEY, PSI_URI, - http_uri_params); -} - -static const IpsApi uri_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - uri_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// http_version -//------------------------------------------------------------------------- - -static const Parameter http_version_params[] = -{ - { "request", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against the version from the request message even when examining the response" }, - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#undef IPS_OPT -#define IPS_OPT "http_version" -#undef IPS_HELP -#define IPS_HELP "rule option to set the detection cursor to the version buffer" - -static Module* version_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_VERSION, CAT_SET_OTHER, - PSI_VERSION, http_version_params); -} - -static const IpsApi version_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - version_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// js_data -//------------------------------------------------------------------------- -// - -#undef IPS_OPT -#define IPS_OPT "js_data" -#undef IPS_HELP -#define IPS_HELP "rule option to set detection cursor to normalized JavaScript data" -static Module* js_data_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, BUFFER_JS_DATA, CAT_SET_JS_DATA, - PSI_JS_DATA); -} - -static const IpsApi js_data_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - js_data_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// num_header_lines -//------------------------------------------------------------------------- -#undef IPS_OPT -#define IPS_OPT "num_headers" -#undef IPS_HELP -#define IPS_HELP "rule option to perform range check on number of headers" - -static const Parameter http_num_hdrs_params[] = -{ - { "~range", Parameter::PT_INTERVAL, hdrs_num_range.c_str(), nullptr, - "check that number of headers of current buffer are in given range" }, - { "request", Parameter::PT_IMPLIED, nullptr, nullptr, - "match against the version from the request message even when examining the response" }, - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "this rule is limited to examining HTTP message headers" }, - { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message body" }, - { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, - "parts of this rule examine HTTP message trailers" }, - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -static Module* num_hdrs_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_RANGE_NUM_HDRS, CAT_SET_OTHER, - PSI_RANGE_NUM_HDRS, http_num_hdrs_params); -} - -static const IpsApi num_headers_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - num_hdrs_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - nullptr -}; - -//------------------------------------------------------------------------- -// num_trailer_lines -//------------------------------------------------------------------------- -#undef IPS_OPT -#define IPS_OPT "num_trailers" -#undef IPS_HELP -#define IPS_HELP "rule option to perform range check on number of trailers" - -static Module* num_trailers_mod_ctor() -{ - return new HttpRuleOptModule(IPS_OPT, IPS_HELP, HTTP_RANGE_NUM_TRAILERS, CAT_SET_OTHER, - PSI_RANGE_NUM_TRAILERS, http_num_hdrs_params); -} - -static const IpsApi num_trailers_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 1, - API_RESERVED, - API_OPTIONS, - IPS_OPT, - IPS_HELP, - num_trailers_mod_ctor, - HttpRuleOptModule::mod_dtor - }, - OPT_TYPE_DETECTION, - 0, PROTO_BIT__TCP, - nullptr, - nullptr, - nullptr, - nullptr, - HttpIpsOption::opt_ctor, - HttpIpsOption::opt_dtor, - 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 -//------------------------------------------------------------------------- - -const BaseApi* ips_http_client_body = &client_body_api.base; -const BaseApi* ips_http_cookie = &cookie_api.base; -const BaseApi* ips_http_header = &header_api.base; -const BaseApi* ips_http_method = &method_api.base; -const BaseApi* ips_http_num_headers = &num_headers_api.base; -const BaseApi* ips_http_num_trailers = &num_trailers_api.base; -const BaseApi* ips_http_param = ¶m_api.base; -const BaseApi* ips_http_raw_body = &raw_body_api.base; -const BaseApi* ips_http_raw_cookie = &raw_cookie_api.base; -const BaseApi* ips_http_raw_header = &raw_header_api.base; -const BaseApi* ips_http_raw_request = &raw_request_api.base; -const BaseApi* ips_http_raw_status = &raw_status_api.base; -const BaseApi* ips_http_raw_trailer = &raw_trailer_api.base; -const BaseApi* ips_http_raw_uri = &raw_uri_api.base; -const BaseApi* ips_http_stat_code = &stat_code_api.base; -const BaseApi* ips_http_stat_msg = &stat_msg_api.base; -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 2d49b122e..b9e6d36a3 100644 --- a/src/service_inspectors/http_inspect/ips_http.h +++ b/src/service_inspectors/http_inspect/ips_http.h @@ -32,24 +32,18 @@ class HttpInspect; -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_VERSION_MATCH, PSI_MAX }; - class HttpRuleOptModule : public snort::Module { public: HttpRuleOptModule(const char* key_, const char* help, HttpEnums::HTTP_RULE_OPT rule_opt_index_, - snort::CursorActionType cat_, PsIdx psi_) - : snort::Module(key_, help), key(key_), rule_opt_index(rule_opt_index_), - cat(cat_), psi(psi_) {} + snort::CursorActionType cat_) + : snort::Module(key_, help), rule_opt_index(rule_opt_index_), key(key_), + cat(cat_) {} HttpRuleOptModule(const char* key_, const char* help, HttpEnums::HTTP_RULE_OPT rule_opt_index_, - snort::CursorActionType cat_, PsIdx psi_, const snort::Parameter params[]) - : snort::Module(key_, help, params), key(key_), rule_opt_index(rule_opt_index_), - cat(cat_), psi(psi_) {} - snort::ProfileStats* get_profile() const override { return &http_ps[psi]; } + snort::CursorActionType cat_, const snort::Parameter params[]) + : snort::Module(key_, help, params), rule_opt_index(rule_opt_index_), + key(key_), cat(cat_) {} + snort::ProfileStats* get_profile() const = 0; static void mod_dtor(snort::Module* m) { delete m; } bool begin(const char*, int, snort::SnortConfig*) override; bool set(const char*, snort::Value&, snort::SnortConfig*) override; @@ -58,75 +52,53 @@ public: Usage get_usage() const override { return DETECT; } +protected: + HttpEnums::InspectSection inspect_section; + const HttpEnums::HTTP_RULE_OPT rule_opt_index; + const char* const key; + uint64_t sub_id; + bool is_trailer_opt; // used by ::end for with_* validation + 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 { public: std::string field; // provide buffer containing specific header field - std::string param; // provide buffer containing specific parameter - bool nocase; // case insensitive match bool request; // provide buffer from request not response bool with_header; // provide buffer with a later section than it appears in bool with_body; bool with_trailer; - bool scheme; // provide buffer with one of the six URI subcomponents - bool host; - bool port; - bool path; - bool query; - bool fragment; - snort::RangeCheck range; - std::bitset version_flags; void reset(); }; - const char* const key; - const HttpEnums::HTTP_RULE_OPT rule_opt_index; const snort::CursorActionType cat; - const PsIdx psi; - HttpRuleParaList para_list; - HttpEnums::InspectSection inspect_section; - uint64_t sub_id; uint64_t form; - - bool parse_version_list(snort::Value& v); }; class HttpIpsOption : public snort::IpsOption { public: - HttpIpsOption(const HttpRuleOptModule* cm) : - snort::IpsOption(cm->key, RULE_OPTION_TYPE_BUFFER_SET), - 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), - version_flags(cm->para_list.version_flags){} + HttpIpsOption(const HttpRuleOptModule* cm, option_type_t type) : + snort::IpsOption(cm->key, type), + buffer_info(cm->rule_opt_index, cm->sub_id, cm->form), + cat(cm->cat), inspect_section(cm->inspect_section) {} snort::CursorActionType get_cursor_type() const override { return cat; } - EvalStatus eval(Cursor&, snort::Packet*) override; + EvalStatus eval(Cursor&, snort::Packet*) = 0; uint32_t hash() const override; bool operator==(const snort::IpsOption& ips) const override; - bool retry(Cursor&, const Cursor&) override; - static IpsOption* opt_ctor(snort::Module* m, OptTreeNode*) - { return new HttpIpsOption((HttpRuleOptModule*)m); } - static void opt_dtor(snort::IpsOption* p) { delete p; } + +protected: + const HttpBufferInfo buffer_info; + + HttpInspect const* eval_helper(snort::Packet* p); private: - const char* const key; const snort::CursorActionType cat; - const PsIdx psi; 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 HttpInspect* hi); }; #endif diff --git a/src/service_inspectors/http_inspect/ips_http_buffer.cc b/src/service_inspectors/http_inspect/ips_http_buffer.cc new file mode 100644 index 000000000..9326dfde3 --- /dev/null +++ b/src/service_inspectors/http_inspect/ips_http_buffer.cc @@ -0,0 +1,1104 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2022 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. +//-------------------------------------------------------------------------- +// ips_http_buffer.cc author Maya Dagon +// Refactored from ips_http.cc author Tom Peters + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ips_http_buffer.h" + +#include "framework/cursor.h" +#include "hash/hash_key_operations.h" +#include "log/messages.h" +#include "parser/parse_utils.h" +#include "protocols/packet.h" + +#include "http_common.h" +#include "http_enum.h" +#include "http_inspect.h" + +using namespace snort; +using namespace HttpCommon; +using namespace HttpEnums; + +THREAD_LOCAL std::array HttpBufferRuleOptModule::http_buffer_ps; + +bool HttpBufferRuleOptModule::begin(const char*, int, SnortConfig*) +{ + HttpRuleOptModule::begin(nullptr, 0, nullptr); + scheme = false; + host = false; + port = false; + path = false; + query = false; + fragment = false; + + switch (rule_opt_index) + { + case HTTP_BUFFER_RAW_STATUS: + case HTTP_BUFFER_STAT_CODE: + case HTTP_BUFFER_STAT_MSG: + inspect_section = IS_HEADER; + break; + case HTTP_BUFFER_COOKIE: + case HTTP_BUFFER_HEADER: + case HTTP_BUFFER_METHOD: + case HTTP_BUFFER_RAW_COOKIE: + case HTTP_BUFFER_RAW_HEADER: + case HTTP_BUFFER_RAW_REQUEST: + case HTTP_BUFFER_RAW_URI: + case HTTP_BUFFER_TRUE_IP: + case HTTP_BUFFER_URI: + case HTTP_BUFFER_VERSION: + inspect_section = IS_FLEX_HEADER; + break; + case HTTP_BUFFER_CLIENT_BODY: + case HTTP_BUFFER_RAW_BODY: + case BUFFER_JS_DATA: + inspect_section = IS_BODY; + break; + case HTTP_BUFFER_RAW_TRAILER: + case HTTP_BUFFER_TRAILER: + inspect_section = IS_TRAILER; + is_trailer_opt = true; + break; + default: + assert(false); + } + return true; +} + + +bool HttpBufferRuleOptModule::set(const char*, Value& v, SnortConfig*) +{ + if (v.is("scheme")) + { + scheme = true; + sub_id = UC_SCHEME; + } + else if (v.is("host")) + { + host = true; + sub_id = UC_HOST; + } + else if (v.is("port")) + { + port = true; + sub_id = UC_PORT; + } + else if (v.is("path")) + { + path = true; + sub_id = UC_PATH; + } + else if (v.is("query")) + { + query = true; + sub_id = UC_QUERY; + } + else if (v.is("fragment")) + { + fragment = true; + sub_id = UC_FRAGMENT; + } + else + HttpRuleOptModule::set(nullptr, v, nullptr); + + return true; +} + + +bool HttpBufferRuleOptModule::end(const char*, int, SnortConfig*) +{ + // Check for option conflicts + if (scheme + host + port + path + query + fragment > 1) + ParseError("Only specify one part of the URI"); + return HttpRuleOptModule::end(nullptr, 0, nullptr); +} + + +IpsOption::EvalStatus HttpBufferIpsOption::eval(Cursor& c, Packet* p) +{ + RuleProfile profile(HttpBufferRuleOptModule::http_buffer_ps[idx]); + + const HttpInspect* const hi = eval_helper(p); + if (hi == nullptr) + return NO_MATCH; + + const Field& http_buffer = hi->http_get_buf(p, buffer_info); + if (http_buffer.length() <= 0) + return NO_MATCH; + + c.set(key, http_buffer.start(), http_buffer.length()); + + return MATCH; +} + + +//------------------------------------------------------------------------- +// http_client_body +//------------------------------------------------------------------------- + +#undef IPS_OPT +#define IPS_OPT "http_client_body" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the request body" + +static Module* client_body_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_CLIENT_BODY, CAT_SET_BODY, + BUFFER_PSI_CLIENT_BODY); +} + +static const IpsApi client_body_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + client_body_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_cookie +//------------------------------------------------------------------------- + +static const Parameter http_cookie_params[] = +{ + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against the cookie from the request message even when examining the response" }, + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_cookie" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the HTTP cookie" + +static Module* cookie_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_COOKIE, CAT_SET_COOKIE, BUFFER_PSI_COOKIE, + http_cookie_params); +} + +static const IpsApi cookie_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + cookie_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_header +//------------------------------------------------------------------------- + +// FIXIT-M add match_unknown option to look at HEAD__UNKNOWN. + +// FIXIT-M if http_header is the fast pattern buffer and the content to be +// matched appears in the normalized field but not in the raw field +// detection will fail. + +static const Parameter http_header_params[] = +{ + { "field", Parameter::PT_STRING, nullptr, nullptr, + "restrict to given header. Header name is case insensitive." }, + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against the headers from the request message even when examining the response" }, + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_header" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the normalized headers" + +static Module* header_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_HEADER, CAT_SET_HEADER, + BUFFER_PSI_HEADER, http_header_params); +} + +static const IpsApi header_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + header_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_method +//------------------------------------------------------------------------- + +static const Parameter http_method_params[] = +{ + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_method" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the HTTP request method" + +static Module* method_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_METHOD, CAT_SET_METHOD, BUFFER_PSI_METHOD, + http_method_params); +} + +static const IpsApi method_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + method_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_raw_body +//------------------------------------------------------------------------- + +#undef IPS_OPT +#define IPS_OPT "http_raw_body" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the unnormalized message body" + +static Module* raw_body_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_BODY, CAT_SET_OTHER, + BUFFER_PSI_RAW_BODY); +} + +static const IpsApi raw_body_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + raw_body_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_raw_cookie +//------------------------------------------------------------------------- + +static const Parameter http_raw_cookie_params[] = +{ + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against the cookie from the request message even when examining the response" }, + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_raw_cookie" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the unnormalized cookie" + +static Module* raw_cookie_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_COOKIE, CAT_SET_OTHER, + BUFFER_PSI_RAW_COOKIE, http_raw_cookie_params); +} + +static const IpsApi raw_cookie_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + raw_cookie_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_raw_header +//------------------------------------------------------------------------- + +static const Parameter http_raw_header_params[] = +{ + { "field", Parameter::PT_STRING, nullptr, nullptr, + "restrict to given header. Header name is case insensitive." }, + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against the headers from the request message even when examining the response" }, + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_raw_header" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the unnormalized headers" + +static Module* raw_header_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_HEADER, CAT_SET_RAW_HEADER, + BUFFER_PSI_RAW_HEADER, http_raw_header_params); +} + +static const IpsApi raw_header_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + raw_header_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_raw_request +//------------------------------------------------------------------------- + +static const Parameter http_raw_request_params[] = +{ + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_raw_request" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the unnormalized request line" + +static Module* raw_request_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_REQUEST, CAT_SET_OTHER, + BUFFER_PSI_RAW_REQUEST, http_raw_request_params); +} + +static const IpsApi raw_request_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + raw_request_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_raw_status +//------------------------------------------------------------------------- + +static const Parameter http_raw_status_params[] = +{ + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_raw_status" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the unnormalized status line" + +static Module* raw_status_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_STATUS, CAT_SET_OTHER, + BUFFER_PSI_RAW_STATUS, http_raw_status_params); +} + +static const IpsApi raw_status_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + raw_status_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_raw_trailer +//------------------------------------------------------------------------- + +static const Parameter http_raw_trailer_params[] = +{ + { "field", Parameter::PT_STRING, nullptr, nullptr, + "restrict to given trailer. Trailer name is case insensitive." }, + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against the trailers from the request message even when examining the response" }, + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP response message headers (must be combined with request)" + }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP response message body (must be combined with request)" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_raw_trailer" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the unnormalized trailers" + +static Module* raw_trailer_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_TRAILER, CAT_SET_RAW_HEADER, + BUFFER_PSI_RAW_TRAILER, http_raw_trailer_params); +} + +static const IpsApi raw_trailer_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + raw_trailer_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_raw_uri +//------------------------------------------------------------------------- + +static const Parameter http_raw_uri_params[] = +{ + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { "scheme", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against scheme section of URI only" }, + { "host", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against host section of URI only" }, + { "port", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against port section of URI only" }, + { "path", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against path section of URI only" }, + { "query", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against query section of URI only" }, + { "fragment", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against fragment section of URI only" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_raw_uri" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the unnormalized URI" + +static Module* raw_uri_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_RAW_URI, CAT_SET_RAW_KEY, + BUFFER_PSI_RAW_URI, http_raw_uri_params); +} + +static const IpsApi raw_uri_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + raw_uri_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_stat_code +//------------------------------------------------------------------------- + +static const Parameter http_stat_code_params[] = +{ + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_stat_code" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the HTTP status code" + +static Module* stat_code_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_STAT_CODE, CAT_SET_STAT_CODE, + BUFFER_PSI_STAT_CODE, http_stat_code_params); +} + +static const IpsApi stat_code_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + stat_code_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_stat_msg +//------------------------------------------------------------------------- + +static const Parameter http_stat_msg_params[] = +{ + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_stat_msg" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the HTTP status message" + +static Module* stat_msg_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_STAT_MSG, CAT_SET_STAT_MSG, + BUFFER_PSI_STAT_MSG, http_stat_msg_params); +} + +static const IpsApi stat_msg_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + stat_msg_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_trailer +//------------------------------------------------------------------------- + +static const Parameter http_trailer_params[] = +{ + { "field", Parameter::PT_STRING, nullptr, nullptr, "restrict to given trailer" }, + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against the trailers from the request message even when examining the response" }, + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP response message headers (must be combined with request)" + }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body (must be combined with request)" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_trailer" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the normalized trailers" + +static Module* trailer_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_TRAILER, CAT_SET_HEADER, + BUFFER_PSI_TRAILER, http_trailer_params); +} + +static const IpsApi trailer_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + trailer_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_true_ip +//------------------------------------------------------------------------- + +static const Parameter http_true_ip_params[] = +{ + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_true_ip" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the final client IP address" + +static Module* true_ip_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_TRUE_IP, CAT_SET_OTHER, + BUFFER_PSI_TRUE_IP, http_true_ip_params); +} + +static const IpsApi true_ip_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + true_ip_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_uri +//------------------------------------------------------------------------- + +static const Parameter http_uri_params[] = +{ + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { "scheme", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against scheme section of URI only" }, + { "host", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against host section of URI only" }, + { "port", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against port section of URI only" }, + { "path", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against path section of URI only" }, + { "query", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against query section of URI only" }, + { "fragment", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against fragment section of URI only" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_uri" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the normalized URI buffer" + +static Module* uri_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_URI, CAT_SET_KEY, BUFFER_PSI_URI, + http_uri_params); +} + +static const IpsApi uri_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + uri_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// http_version +//------------------------------------------------------------------------- + +static const Parameter http_version_params[] = +{ + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against the version from the request message even when examining the response" }, + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_version" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the version buffer" + +static Module* version_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_VERSION, CAT_SET_OTHER, + BUFFER_PSI_VERSION, http_version_params); +} + +static const IpsApi version_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + version_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// js_data +//------------------------------------------------------------------------- +// + +#undef IPS_OPT +#define IPS_OPT "js_data" +#undef IPS_HELP +#define IPS_HELP "rule option to set detection cursor to normalized JavaScript data" +static Module* js_data_mod_ctor() +{ + return new HttpBufferRuleOptModule(IPS_OPT, IPS_HELP, BUFFER_JS_DATA, CAT_SET_JS_DATA, + BUFFER_PSI_JS_DATA); +} + +static const IpsApi js_data_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + js_data_mod_ctor, + HttpBufferRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpBufferIpsOption::opt_ctor, + HttpBufferIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// plugins +//------------------------------------------------------------------------- + +const BaseApi* ips_http_client_body = &client_body_api.base; +const BaseApi* ips_http_cookie = &cookie_api.base; +const BaseApi* ips_http_header = &header_api.base; +const BaseApi* ips_http_method = &method_api.base; +const BaseApi* ips_http_raw_body = &raw_body_api.base; +const BaseApi* ips_http_raw_cookie = &raw_cookie_api.base; +const BaseApi* ips_http_raw_header = &raw_header_api.base; +const BaseApi* ips_http_raw_request = &raw_request_api.base; +const BaseApi* ips_http_raw_status = &raw_status_api.base; +const BaseApi* ips_http_raw_trailer = &raw_trailer_api.base; +const BaseApi* ips_http_raw_uri = &raw_uri_api.base; +const BaseApi* ips_http_stat_code = &stat_code_api.base; +const BaseApi* ips_http_stat_msg = &stat_msg_api.base; +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_js_data = &js_data_api.base; diff --git a/src/service_inspectors/http_inspect/ips_http_buffer.h b/src/service_inspectors/http_inspect/ips_http_buffer.h new file mode 100644 index 000000000..cd568db32 --- /dev/null +++ b/src/service_inspectors/http_inspect/ips_http_buffer.h @@ -0,0 +1,86 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2022 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. +//-------------------------------------------------------------------------- +// ips_http_buffer.h author Maya Dagon +// Refactored from ips_http.h author Tom Peters + +#ifndef IPS_HTTP_BUFFER_H +#define IPS_HTTP_BUFFER_H + +#include + +#include "profiler/profiler.h" +#include "framework/ips_option.h" +#include "framework/module.h" + +#include "ips_http.h" + +enum BufferPsIdx { BUFFER_PSI_CLIENT_BODY, BUFFER_PSI_COOKIE, BUFFER_PSI_HEADER, BUFFER_PSI_METHOD, + BUFFER_PSI_RAW_BODY, BUFFER_PSI_RAW_COOKIE, BUFFER_PSI_RAW_HEADER, BUFFER_PSI_RAW_REQUEST, + BUFFER_PSI_RAW_STATUS, BUFFER_PSI_RAW_TRAILER, BUFFER_PSI_RAW_URI, BUFFER_PSI_STAT_CODE, + BUFFER_PSI_STAT_MSG, BUFFER_PSI_TRAILER, BUFFER_PSI_TRUE_IP, BUFFER_PSI_URI, BUFFER_PSI_VERSION, + BUFFER_PSI_JS_DATA, BUFFER_PSI_VBA_DATA, BUFFER_PSI_MAX }; + +class HttpBufferRuleOptModule : public HttpRuleOptModule +{ +public: + HttpBufferRuleOptModule(const char* key_, const char* help, HttpEnums::HTTP_RULE_OPT rule_opt_index_, + snort::CursorActionType cat_, BufferPsIdx idx_) + : HttpRuleOptModule(key_, help, rule_opt_index_, cat_), idx(idx_) {} + HttpBufferRuleOptModule(const char* key_, const char* help, HttpEnums::HTTP_RULE_OPT rule_opt_index_, + snort::CursorActionType cat_, BufferPsIdx idx_, const snort::Parameter params[]) + : HttpRuleOptModule(key_, help, rule_opt_index_, cat_, params), idx(idx_) {} + snort::ProfileStats* get_profile() const override { return &http_buffer_ps[idx]; } + static void mod_dtor(snort::Module* m) { delete m; } + bool begin(const char*, int, snort::SnortConfig*) override; + bool set(const char*, snort::Value&, snort::SnortConfig*) override; + bool end(const char*, int, snort::SnortConfig*) override; + +private: + friend class HttpBufferIpsOption; + static THREAD_LOCAL std::array http_buffer_ps; + const BufferPsIdx idx; + + // URI related params. These affect the sub_id while parsed. + // These values are saved to alert on conflicts, only used by ::end + bool scheme; + bool host; + bool port; + bool path; + bool query; + bool fragment; +}; + +class HttpBufferIpsOption : public HttpIpsOption +{ +public: + HttpBufferIpsOption(const HttpBufferRuleOptModule* cm) : + HttpIpsOption(cm, RULE_OPTION_TYPE_BUFFER_SET), idx(cm->idx), + key(cm->key) {} + EvalStatus eval(Cursor&, snort::Packet*) override; + + static IpsOption* opt_ctor(snort::Module* m, OptTreeNode*) + { return new HttpBufferIpsOption((HttpBufferRuleOptModule*)m); } + + static void opt_dtor(snort::IpsOption* p) { delete p; } + +private: + const BufferPsIdx idx; + const char* const key; +}; + +#endif diff --git a/src/service_inspectors/http_inspect/ips_http_num_hdrs.cc b/src/service_inspectors/http_inspect/ips_http_num_hdrs.cc new file mode 100644 index 000000000..545167d22 --- /dev/null +++ b/src/service_inspectors/http_inspect/ips_http_num_hdrs.cc @@ -0,0 +1,202 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2022 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. +//-------------------------------------------------------------------------- +// ips_http_num_hdrs.cc author Maya Dagon +// Refactored from ips_http.cc author Tom Peters + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ips_http_num_hdrs.h" + +#include "framework/cursor.h" +#include "hash/hash_key_operations.h" +#include "log/messages.h" +#include "parser/parse_utils.h" +#include "protocols/packet.h" +#include "service_inspectors/http2_inspect/http2_flow_data.h" + +#include "http_common.h" +#include "http_enum.h" +#include "http_flow_data.h" +#include "http_inspect.h" +#include "http_msg_head_shared.h" + +using namespace snort; +using namespace HttpCommon; +using namespace HttpEnums; + +THREAD_LOCAL std::array HttpNumHdrsRuleOptModule::http_num_hdrs_ps; + +const std::string hdrs_num_range = "0:" + std::to_string(HttpMsgHeadShared::MAX_HEADERS); + +bool HttpNumHdrsRuleOptModule::begin(const char*, int, SnortConfig*) +{ + HttpRuleOptModule::begin(nullptr, 0, nullptr); + range.init(); + if (rule_opt_index == HTTP_RANGE_NUM_HDRS) + inspect_section = IS_FLEX_HEADER; + else + { + inspect_section = IS_TRAILER; + is_trailer_opt = true; + } + + return true; +} + +bool HttpNumHdrsRuleOptModule::set(const char*, Value& v, SnortConfig*) +{ + if (v.is("~range")) + return range.validate(v.get_string(), hdrs_num_range.c_str()); + + return HttpRuleOptModule::set(nullptr, v, nullptr); +} + + +uint32_t HttpNumHdrsIpsOption::hash() const +{ + uint32_t a = HttpIpsOption::hash(); + uint32_t b = range.hash(); + uint32_t c = 0; + mix(a,b,c); + finalize(a,b,c); + return c; +} + +bool HttpNumHdrsIpsOption::operator==(const IpsOption& ips) const +{ + const HttpNumHdrsIpsOption& hio = static_cast(ips); + return HttpIpsOption::operator==(static_cast(ips)) && + range == hio.range; +} + +IpsOption::EvalStatus HttpNumHdrsIpsOption::eval(Cursor&, Packet* p) +{ + RuleProfile profile(HttpNumHdrsRuleOptModule::http_num_hdrs_ps[idx]); + + const HttpInspect* const hi = eval_helper(p); + if (hi == nullptr) + return NO_MATCH; + + const int32_t num_lines = hi->http_get_num_headers(p, buffer_info); + if (num_lines != HttpCommon::STAT_NOT_PRESENT && range.eval(num_lines)) + return MATCH; + + return NO_MATCH; +} + +//------------------------------------------------------------------------- +// num_header_lines +//------------------------------------------------------------------------- +#undef IPS_OPT +#define IPS_OPT "http_num_headers" +#undef IPS_HELP +#define IPS_HELP "rule option to perform range check on number of headers" + +static const Parameter http_num_hdrs_params[] = +{ + { "~range", Parameter::PT_INTERVAL, hdrs_num_range.c_str(), nullptr, + "check that number of headers of current buffer are in given range" }, + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against the version from the request message even when examining the response" }, + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +static Module* num_hdrs_mod_ctor() +{ + return new HttpNumHdrsRuleOptModule(IPS_OPT, IPS_HELP, HTTP_RANGE_NUM_HDRS, CAT_SET_OTHER, + NUM_HDRS_PSI_HDRS, http_num_hdrs_params); +} + +static const IpsApi num_headers_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + num_hdrs_mod_ctor, + HttpNumHdrsRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpNumHdrsIpsOption::opt_ctor, + HttpNumHdrsIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// num_trailer_lines +//------------------------------------------------------------------------- +#undef IPS_OPT +#define IPS_OPT "http_num_trailers" +#undef IPS_HELP +#define IPS_HELP "rule option to perform range check on number of trailers" + +static Module* num_trailers_mod_ctor() +{ + return new HttpNumHdrsRuleOptModule(IPS_OPT, IPS_HELP, HTTP_RANGE_NUM_TRAILERS, CAT_SET_OTHER, + NUM_HDRS_PSI_TRAILERS, http_num_hdrs_params); +} + +static const IpsApi num_trailers_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + num_trailers_mod_ctor, + HttpNumHdrsRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpNumHdrsIpsOption::opt_ctor, + HttpNumHdrsIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// plugins +//------------------------------------------------------------------------- +const BaseApi* ips_http_num_headers = &num_headers_api.base; +const BaseApi* ips_http_num_trailers = &num_trailers_api.base; + diff --git a/src/service_inspectors/http_inspect/ips_http_num_hdrs.h b/src/service_inspectors/http_inspect/ips_http_num_hdrs.h new file mode 100644 index 000000000..54a8da205 --- /dev/null +++ b/src/service_inspectors/http_inspect/ips_http_num_hdrs.h @@ -0,0 +1,77 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2022 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. +//-------------------------------------------------------------------------- +// ips_http_num_hdrs.h author Maya Dagon +// Refactored from ips_http.h author Tom Peters + +#ifndef IPS_HTTP_NUM_HDRS_H +#define IPS_HTTP_NUM_HDRS_H + +#include + +#include "profiler/profiler.h" +#include "framework/ips_option.h" +#include "framework/module.h" +#include "framework/range.h" + +#include "http_enum.h" +#include "ips_http.h" + +enum NumHdrsPsIdx { NUM_HDRS_PSI_HDRS, NUM_HDRS_PSI_TRAILERS, NUM_HDRS_PSI_MAX }; + +class HttpNumHdrsRuleOptModule : public HttpRuleOptModule +{ +public: + HttpNumHdrsRuleOptModule(const char* key_, const char* help, HttpEnums::HTTP_RULE_OPT rule_opt_index_, + snort::CursorActionType cat_, NumHdrsPsIdx idx_, const snort::Parameter params[]) + : HttpRuleOptModule(key_, help, rule_opt_index_, cat_, params), idx(idx_) {} + + snort::ProfileStats* get_profile() const override + { return &http_num_hdrs_ps[idx]; } + + static void mod_dtor(snort::Module* m) { delete m; } + bool begin(const char*, int, snort::SnortConfig*) override; + bool set(const char*, snort::Value&, snort::SnortConfig*) override; + +private: + friend class HttpNumHdrsIpsOption; + static THREAD_LOCAL std::array http_num_hdrs_ps; + const NumHdrsPsIdx idx; + snort::RangeCheck range; +}; + +class HttpNumHdrsIpsOption : public HttpIpsOption +{ +public: + HttpNumHdrsIpsOption(const HttpNumHdrsRuleOptModule* cm) : + HttpIpsOption(cm, RULE_OPTION_TYPE_OTHER), idx(cm->idx), range(cm->range) {} + EvalStatus eval(Cursor&, snort::Packet*) override; + uint32_t hash() const override; + bool operator==(const snort::IpsOption& ips) const override; + + static IpsOption* opt_ctor(snort::Module* m, OptTreeNode*) + { return new HttpNumHdrsIpsOption((HttpNumHdrsRuleOptModule*)m); } + + static void opt_dtor(snort::IpsOption* p) { delete p; } + +private: + const NumHdrsPsIdx idx; + const snort::RangeCheck range; +}; + +#endif + diff --git a/src/service_inspectors/http_inspect/ips_http_param.cc b/src/service_inspectors/http_inspect/ips_http_param.cc new file mode 100644 index 000000000..17bc5286b --- /dev/null +++ b/src/service_inspectors/http_inspect/ips_http_param.cc @@ -0,0 +1,174 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2022 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. +//-------------------------------------------------------------------------- +// ips_http_param.cc author Maya Dagon +// Refactored from ips_http.cc author Tom Peters + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ips_http_param.h" + +#include "framework/cursor.h" +#include "hash/hash_key_operations.h" +#include "log/messages.h" +#include "parser/parse_utils.h" +#include "protocols/packet.h" + +#include "http_common.h" +#include "http_enum.h" +#include "http_inspect.h" +#include "http_param.h" + +using namespace snort; +using namespace HttpCommon; +using namespace HttpEnums; + +THREAD_LOCAL ProfileStats HttpParamRuleOptModule::http_param_ps; + +bool HttpParamRuleOptModule::begin(const char*, int, SnortConfig*) +{ + HttpRuleOptModule::begin(nullptr, 0, nullptr); + param.clear(); + nocase = false; + inspect_section = IS_FLEX_HEADER; + return true; +} + +bool HttpParamRuleOptModule::set(const char*, Value& v, SnortConfig*) +{ + if (v.is("~param")) + { + std::string bc = v.get_string(); + bool negated = false; + if (!parse_byte_code(bc.c_str(), negated, param) or negated) + ParseError("Invalid http_param"); + } + else if (v.is("nocase")) + { + nocase = true; + } + return true; +} + +uint32_t HttpParamIpsOption::hash() const +{ + uint32_t a = HttpIpsOption::hash(); + uint32_t b = http_param.is_nocase() ? 1 : 0; + uint32_t c = 0; + mix_str(a,b,c,http_param.c_str(),http_param.length()); + finalize(a,b,c); + return a; +} + +bool HttpParamRuleOptModule::end(const char*, int, SnortConfig*) +{ + if (param.length() == 0) + ParseError("Specify parameter name"); + return true; +} + +bool HttpParamIpsOption::operator==(const IpsOption& ips) const +{ + const HttpParamIpsOption& hio = static_cast(ips); + + return HttpIpsOption::operator==(static_cast(ips)) && + http_param == hio.http_param; +} + +bool HttpParamIpsOption::retry(Cursor& current_cursor, const Cursor&) +{ + HttpCursorData* cd = (HttpCursorData*)current_cursor.get_data(HttpCursorData::id); + + if (cd) + return cd->retry(); + + return false; +} + +IpsOption::EvalStatus HttpParamIpsOption::eval(Cursor& c, Packet* p) +{ + RuleProfile profile(HttpParamRuleOptModule::http_param_ps); + + const HttpInspect* const hi = eval_helper(p); + if (hi == nullptr) + return NO_MATCH; + + const Field& http_buffer = hi->http_get_param_buf(c, p, http_param); + if (http_buffer.length() <= 0) + return NO_MATCH; + + c.set(key, http_buffer.start(), http_buffer.length()); + + return MATCH; +} + +//------------------------------------------------------------------------- +// http_param +//------------------------------------------------------------------------- + +static const Parameter http_param_params[] = +{ + { "~param", Parameter::PT_STRING, nullptr, nullptr, + "parameter to match" }, + { "nocase", Parameter::PT_IMPLIED, nullptr, nullptr, + "case insensitive match" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#undef IPS_OPT +#define IPS_OPT "http_param" +#undef IPS_HELP +#define IPS_HELP "rule option to set the detection cursor to the value of the specified HTTP parameter key which may be in the query or body" + +static Module* param_mod_ctor() +{ + return new HttpParamRuleOptModule(IPS_OPT, IPS_HELP, HTTP_BUFFER_PARAM, CAT_SET_OTHER, + http_param_params); +} + +static const IpsApi param_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 1, + API_RESERVED, + API_OPTIONS, + IPS_OPT, + IPS_HELP, + param_mod_ctor, + HttpParamRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpParamIpsOption::opt_ctor, + HttpParamIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// plugins +//------------------------------------------------------------------------- + +const BaseApi* ips_http_param = ¶m_api.base; diff --git a/src/service_inspectors/http_inspect/ips_http_param.h b/src/service_inspectors/http_inspect/ips_http_param.h new file mode 100644 index 000000000..40cf593a2 --- /dev/null +++ b/src/service_inspectors/http_inspect/ips_http_param.h @@ -0,0 +1,73 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2022 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. +//-------------------------------------------------------------------------- +// ips_http_param.h author Maya Dagon +// Refactored from ips_http.h author Tom Peters + +#ifndef IPS_HTTP_PARAM_H +#define IPS_HTTP_PARAM_H + +#include "profiler/profiler.h" +#include "framework/ips_option.h" +#include "framework/module.h" + +#include "http_param.h" +#include "ips_http.h" + + +class HttpParamRuleOptModule : public HttpRuleOptModule +{ +public: + HttpParamRuleOptModule(const char* key_, const char* help, HttpEnums::HTTP_RULE_OPT rule_opt_index_, + snort::CursorActionType cat_, const snort::Parameter params[]) + : HttpRuleOptModule(key_, help, rule_opt_index_, cat_, params) {} + snort::ProfileStats* get_profile() const override { return &http_param_ps; } + static void mod_dtor(snort::Module* m) { delete m; } + bool begin(const char*, int, snort::SnortConfig*) override; + bool set(const char*, snort::Value&, snort::SnortConfig*) override; + bool end(const char*, int, snort::SnortConfig*) override; + +private: + friend class HttpParamIpsOption; + static THREAD_LOCAL snort::ProfileStats http_param_ps; + + std::string param; // provide buffer containing specific parameter + bool nocase; // case insensitive match +}; + +class HttpParamIpsOption : public HttpIpsOption +{ +public: + HttpParamIpsOption(const HttpParamRuleOptModule* cm) : + HttpIpsOption(cm, RULE_OPTION_TYPE_BUFFER_SET), + key(cm->key), http_param(cm->param, cm->nocase) {} + EvalStatus eval(Cursor&, snort::Packet*) override; + uint32_t hash() const override; + bool operator==(const snort::IpsOption& ips) const override; + + static IpsOption* opt_ctor(snort::Module* m, OptTreeNode*) + { return new HttpParamIpsOption((HttpParamRuleOptModule*)m); } + + static void opt_dtor(snort::IpsOption* p) { delete p; } + bool retry(Cursor& , const Cursor&) override; + +private: + const char* const key; + const HttpParam http_param; +}; + +#endif diff --git a/src/service_inspectors/http_inspect/ips_http_version.cc b/src/service_inspectors/http_inspect/ips_http_version.cc new file mode 100644 index 000000000..7a60c31e9 --- /dev/null +++ b/src/service_inspectors/http_inspect/ips_http_version.cc @@ -0,0 +1,188 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2022 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. +//-------------------------------------------------------------------------- +// ips_http_version.cc author Maya Dagon +// Refactored from ips_http.cc author Tom Peters + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ips_http_version.h" + +#include "framework/cursor.h" +#include "hash/hash_key_operations.h" +#include "log/messages.h" +#include "parser/parse_utils.h" +#include "protocols/packet.h" + +#include "http_common.h" +#include "http_enum.h" +#include "http_inspect.h" + +using namespace snort; +using namespace HttpCommon; +using namespace HttpEnums; + +THREAD_LOCAL ProfileStats HttpVersionRuleOptModule::http_version_ps; + +bool HttpVersionRuleOptModule::begin(const char*, int, SnortConfig*) +{ + HttpRuleOptModule::begin(nullptr, 0, nullptr); + inspect_section = IS_FLEX_HEADER; + version_flags = 0; + 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 HttpVersionRuleOptModule::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; + } + + version_flags[iter->second - VERS__MIN] = true; + } + return true; +} + +bool HttpVersionRuleOptModule::set(const char*, Value& v, SnortConfig*) +{ + if (v.is("~version_list")) + { + return parse_version_list(v); + } + return HttpRuleOptModule::set(nullptr, v, nullptr); +} + +uint32_t HttpVersionIpsOption::hash() const +{ + uint32_t a = HttpIpsOption::hash(); + uint32_t b = (uint32_t)version_flags.to_ulong(); + uint32_t c = 0; + mix(a,b,c); + finalize(a,b,c); + return c; +} + +bool HttpVersionIpsOption::operator==(const IpsOption& ips) const +{ + const HttpVersionIpsOption& hio = static_cast(ips); + return HttpIpsOption::operator==(static_cast(ips)) && + version_flags == hio.version_flags; +} + +IpsOption::EvalStatus HttpVersionIpsOption::eval(Cursor&, Packet* p) +{ + RuleProfile profile(HttpVersionRuleOptModule::http_version_ps); + + const HttpInspect* const hi = eval_helper(p); + if (hi == nullptr) + return NO_MATCH; + + const VersionId version = hi->http_get_version_id(p, buffer_info); + + if (version_flags[version - HttpEnums::VERS__MIN]) + return MATCH; + + return NO_MATCH; +} + +//------------------------------------------------------------------------- +// 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" }, + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "match against the version from the request message even when examining the response" }, + { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, + "this rule is limited to examining HTTP message headers" }, + { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message body" }, + { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, + "parts of this rule examine HTTP message trailers" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +static Module* version_match_mod_ctor() +{ + return new HttpVersionRuleOptModule(IPS_OPT, IPS_HELP, HTTP_VERSION_MATCH, CAT_SET_OTHER, + 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, + HttpVersionRuleOptModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + HttpVersionIpsOption::opt_ctor, + HttpVersionIpsOption::opt_dtor, + nullptr +}; + +//------------------------------------------------------------------------- +// plugins +//------------------------------------------------------------------------- +const BaseApi* ips_http_version_match = &version_match_api.base; diff --git a/src/service_inspectors/http_inspect/ips_http_version.h b/src/service_inspectors/http_inspect/ips_http_version.h new file mode 100644 index 000000000..2a89c6203 --- /dev/null +++ b/src/service_inspectors/http_inspect/ips_http_version.h @@ -0,0 +1,70 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2022 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. +//-------------------------------------------------------------------------- +// ips_http_version.h author Maya Dagon +// Refactored from ips_http.h author Tom Peters + +#ifndef IPS_HTTP_VERSION_H +#define IPS_HTTP_VERSION_H + +#include "profiler/profiler.h" +#include "framework/ips_option.h" +#include "framework/module.h" + +#include "http_enum.h" +#include "ips_http.h" + +class HttpVersionRuleOptModule : public HttpRuleOptModule +{ +public: + HttpVersionRuleOptModule(const char* key_, const char* help, HttpEnums::HTTP_RULE_OPT rule_opt_index_, + snort::CursorActionType cat_, const snort::Parameter params[]) + : HttpRuleOptModule(key_, help, rule_opt_index_, cat_, params){} + snort::ProfileStats* get_profile() const override { return &http_version_ps; } + static void mod_dtor(snort::Module* m) { delete m; } + bool begin(const char*, int, snort::SnortConfig*) override; + bool set(const char*, snort::Value&, snort::SnortConfig*) override; + +private: + friend class HttpVersionIpsOption; + static THREAD_LOCAL snort::ProfileStats http_version_ps; + static const int version_size = HttpEnums::VERS__MAX - HttpEnums::VERS__MIN + 1; + + std::bitset version_flags; + + bool parse_version_list(snort::Value& v); +}; + +class HttpVersionIpsOption : public HttpIpsOption +{ +public: + HttpVersionIpsOption(const HttpVersionRuleOptModule* cm) : + HttpIpsOption(cm, RULE_OPTION_TYPE_OTHER), version_flags(cm->version_flags) {} + EvalStatus eval(Cursor&, snort::Packet*) override; + uint32_t hash() const override; + bool operator==(const snort::IpsOption& ips) const override; + + static IpsOption* opt_ctor(snort::Module* m, OptTreeNode*) + { return new HttpVersionIpsOption((HttpVersionRuleOptModule*)m); } + + static void opt_dtor(snort::IpsOption* p) { delete p; } + +private: + const std::bitset version_flags; +}; + +#endif