percent_u = false
utf8_bare_byte = false
iis_unicode = false
- iis_double_decode = false
+ iis_double_decode = true
The HTTP inspector normalizes percent encodings found in URIs. For instance
it will convert "%48%69%64%64%65%6e" to "Hidden". All the options listed
3 are exempted. These may be percent-encoded without generating an alert.
simplify_path = true
- backslash_to_slash = false
+ backslash_to_slash = true
HTTP inspector simplifies directory paths in URIs by eliminating extra
traversals using ., .., and /.
\this\is\the\other\way\to\write\a\path
-backslash_to_slash is turned off by default. If you are protecting such a
-server then set backslash_to_slash = true and all the backslashes will be
-replaced with slashes during normalization.
+backslash_to_slash is turned on by default. It replaces all the backslashes
+with slashes during normalization.
==== Detection rules
continue;
}
- else
+ else if ( node->option_type != RULE_OPTION_TYPE_BUFFER_SET )
{
// Check for an unbounded relative search. If this
// failed before, it's going to fail again so don't
#include "config.h"
#endif
+#include <cassert>
+
#include "cursor.h"
#include "detection/detection_util.h"
using namespace snort;
+unsigned CursorData::cursor_data_id = 0;
+
Cursor::Cursor(Packet* p)
{
reset(p);
Cursor::Cursor(const Cursor& rhs)
{
- *this = rhs;
- delta = 0;
+ name = rhs.name;
+ buf = rhs.buf;
+ sz = rhs.sz;
+ pos = rhs.pos;
+
+ if (rhs.data)
+ {
+ data = new CursorDataVec;
+
+ for (CursorData*& cd : *rhs.data)
+ data->push_back(cd->clone());
+ }
+}
+
+CursorData* Cursor::get_data(unsigned id) const
+{
+ if (data)
+ {
+ for (CursorData*& cd : *data)
+ {
+ if (cd->get_id() == id)
+ return cd;
+ }
+ }
+
+ return nullptr;
+}
+
+void Cursor::set_data(CursorData* cd)
+{
+ assert(cd);
+
+ if (data)
+ {
+ unsigned id = cd->get_id();
+ for (CursorData*& old : *data)
+ {
+ if (old->get_id() == id)
+ {
+ delete old;
+ old = cd;
+ return;
+ }
+ }
+ }
+ else
+ {
+ data = new CursorDataVec;
+ }
+
+ data->push_back(cd);
}
void Cursor::reset(Packet* p)
#include <cstdint>
#include <cstring>
+#include <vector>
namespace snort
{
struct Packet;
}
+class CursorData
+{
+public:
+ CursorData(unsigned u) : id(u) {}
+ virtual ~CursorData() = default;
+ virtual CursorData* clone() = 0;
+
+ unsigned get_id()
+ { return id; }
+
+ static unsigned create_cursor_data_id()
+ { return ++cursor_data_id; }
+
+private:
+ static unsigned cursor_data_id;
+ unsigned id;
+};
+
class Cursor
{
public:
+ Cursor() = default;
Cursor(snort::Packet*);
Cursor(const Cursor&);
- Cursor& operator=(const Cursor&) = default;
+ Cursor& operator=(const Cursor&) = delete;
+
+ ~Cursor()
+ {
+ if (!data)
+ return;
+
+ for (CursorData*& cd : *data)
+ delete cd;
+
+ delete data;
+ }
const char* get_name() const
{ return name; }
void reset(snort::Packet*);
void set(const char* s, const uint8_t* b, unsigned n)
- { name = s; data = b; sz = n; pos = delta = 0; }
+ { name = s; buf = b; sz = n; pos = delta = 0; }
const uint8_t* buffer() const
- { return data; }
+ { return buf; }
unsigned size() const
{ return sz; }
// the NEXT octect after last in buffer
// (this pointer is out of bounds)
const uint8_t* endo() const
- { return data + sz; }
+ { return buf + sz; }
const uint8_t* start() const
- { return data + pos; }
+ { return buf + pos; }
unsigned length() const
{ return sz - pos; }
unsigned get_delta() const
{ return delta; }
+ CursorData* get_data(unsigned id) const;
+
bool add_pos(unsigned n)
{
if (pos + n > sz)
return true;
}
+ void set_data(CursorData* cd);
+
+ typedef std::vector<CursorData*> CursorDataVec;
+
private:
- const char* name; // rule option name ("pkt_data", "http_uri", etc.)
- const uint8_t* data; // start of buffer
- unsigned sz; // size of buffer
- unsigned pos; // current pos
- unsigned delta; // loop offset
+ const char* name = nullptr; // rule option name ("pkt_data", "http_uri", etc.)
+ const uint8_t* buf = nullptr; // start of buffer
+ unsigned sz = 0; // size of buffer
+ unsigned pos = 0; // current pos
+ unsigned delta = 0; // loop offset
+ CursorDataVec* data = nullptr; // data stored on the cursor
};
#endif
set (FILE_LIST
ips_http.cc
ips_http.h
+ http_buffer_info.h
+ http_buffer_info.cc
http_inspect.cc
http_inspect.h
http_msg_section.cc
http_msg_body_old.h
http_msg_trailer.cc
http_msg_trailer.h
+ http_param.h
+ http_query_parser.cc
+ http_query_parser.h
http_header_normalizer.cc
http_header_normalizer.h
http_uri.cc
http_flow_data.h
http_context_data.cc
http_context_data.h
+ http_cursor_data.h
http_transaction.cc
http_transaction.h
http_test_manager.cc
#include "http_api.h"
#include "http_context_data.h"
+#include "http_cursor_data.h"
#include "http_inspect.h"
using namespace snort;
const char* HttpApi::http_my_name = HTTP_NAME;
const char* HttpApi::http_help = "the new HTTP inspector!";
+unsigned HttpCursorData::id = 0;
+
Inspector* HttpApi::http_ctor(Module* mod)
{
HttpModule* const http_mod = (HttpModule*)mod;
{
HttpFlowData::init();
HttpContextData::init();
+ HttpCursorData::init();
}
const char* HttpApi::classic_buffer_names[] =
"http_cookie",
"http_header",
"http_method",
+ "http_param",
"http_raw_body",
"http_raw_cookie",
"http_raw_header",
extern const BaseApi* ips_http_cookie;
extern const BaseApi* ips_http_header;
extern const BaseApi* ips_http_method;
+extern const BaseApi* ips_http_param;
extern const BaseApi* ips_http_raw_body;
extern const BaseApi* ips_http_raw_cookie;
extern const BaseApi* ips_http_raw_header;
ips_http_cookie,
ips_http_header,
ips_http_method,
+ ips_http_param,
ips_http_raw_body,
ips_http_raw_cookie,
ips_http_raw_header,
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// http_buffer_info.cc author Brandon Stultz <brastult@cisco.com>
+
+#include "hash/hashfcn.h"
+#include "http_buffer_info.h"
+
+using namespace snort;
+
+uint32_t HttpBufferInfo::hash() const
+{
+ uint32_t a = type;
+ uint32_t b = sub_id >> 32;
+ 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;
+}
+
+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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// http_buffer_info.h author Brandon Stultz <brastult@cisco.com>
+
+#ifndef HTTP_BUFFER_INFO_H
+#define HTTP_BUFFER_INFO_H
+
+#include <string>
+
+#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;
+};
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// http_cursor_data.h author Brandon Stultz <brastult@cisco.com>
+
+#ifndef HTTP_CURSOR_DATA_H
+#define HTTP_CURSOR_DATA_H
+
+#include "framework/cursor.h"
+
+class HttpCursorData : public CursorData
+{
+public:
+ HttpCursorData() : CursorData(id) {}
+
+ static void init()
+ { id = CursorData::create_cursor_data_id(); }
+
+ HttpCursorData* clone() override
+ { return new HttpCursorData(*this); }
+
+ bool retry()
+ {
+ return query_index < num_query_params ||
+ body_index < num_body_params;
+ }
+
+public:
+ static unsigned id;
+ unsigned num_query_params = 0;
+ unsigned num_body_params = 0;
+ unsigned query_index = 0;
+ unsigned body_index = 0;
+};
+
+#endif
+
// Message buffers available to clients
// This enum must remain synchronized with HttpApi::classic_buffer_names[]
enum HTTP_BUFFER { HTTP_BUFFER_CLIENT_BODY = 1, HTTP_BUFFER_COOKIE, HTTP_BUFFER_HEADER,
- HTTP_BUFFER_METHOD, HTTP_BUFFER_RAW_BODY, HTTP_BUFFER_RAW_COOKIE, HTTP_BUFFER_RAW_HEADER,
- HTTP_BUFFER_RAW_REQUEST, HTTP_BUFFER_RAW_STATUS, HTTP_BUFFER_RAW_TRAILER, HTTP_BUFFER_RAW_URI,
- HTTP_BUFFER_STAT_CODE, HTTP_BUFFER_STAT_MSG, HTTP_BUFFER_TRAILER, HTTP_BUFFER_TRUE_IP,
+ HTTP_BUFFER_METHOD, HTTP_BUFFER_PARAM, HTTP_BUFFER_RAW_BODY, HTTP_BUFFER_RAW_COOKIE,
+ HTTP_BUFFER_RAW_HEADER, HTTP_BUFFER_RAW_REQUEST, HTTP_BUFFER_RAW_STATUS,
+ HTTP_BUFFER_RAW_TRAILER, HTTP_BUFFER_RAW_URI, HTTP_BUFFER_STAT_CODE,
+ HTTP_BUFFER_STAT_MSG, HTTP_BUFFER_TRAILER, HTTP_BUFFER_TRUE_IP,
HTTP_BUFFER_URI, HTTP_BUFFER_VERSION, HTTP_BUFFER_MAX };
// Peg counts
PEG_GET, PEG_HEAD, PEG_POST, PEG_PUT, PEG_DELETE, PEG_CONNECT, PEG_OPTIONS, PEG_TRACE,
PEG_OTHER_METHOD, PEG_REQUEST_BODY, PEG_CHUNKED, PEG_URI_NORM, PEG_URI_PATH, PEG_URI_CODING,
PEG_CONCURRENT_SESSIONS, PEG_MAX_CONCURRENT_SESSIONS, PEG_DETAINED, PEG_PARTIAL_INSPECT,
- PEG_COUNT_MAX };
+ PEG_EXCESS_PARAMS, PEG_PARAMS, PEG_COUNT_MAX };
// Result of scanning by splitter
enum ScanResult { SCAN_NOT_FOUND, SCAN_NOT_FOUND_DETAIN, SCAN_FOUND, SCAN_FOUND_PIECE,
EVENT_VERSION_NOT_UPPERCASE,
EVENT_BAD_HEADER_WHITESPACE,
EVENT_GZIP_EARLY_END, // 248
+ EVENT_EXCESS_REPEAT_PARAMS,
EVENT__MAX_VALUE
};
class HttpJsNorm;
class HttpMsgSection;
class HttpCutter;
+class HttpQueryParser;
class HttpFlowData : public snort::FlowData
{
friend class HttpMsgBodyChunk;
friend class HttpMsgBodyCl;
friend class HttpMsgBodyOld;
+ friend class HttpQueryParser;
friend class HttpStreamSplitter;
friend class HttpTransaction;
#if defined(REG_TEST) || defined(UNIT_TEST)
switch (ibt)
{
case InspectionBuffer::IBT_KEY:
- return http_get_buf(HTTP_BUFFER_URI, 0, 0, p, b);
+ return get_buf(HTTP_BUFFER_URI, p, b);
case InspectionBuffer::IBT_HEADER:
if (get_latest_is(p) == IS_TRAILER)
- return http_get_buf(HTTP_BUFFER_TRAILER, 0, 0, p, b);
+ return get_buf(HTTP_BUFFER_TRAILER, p, b);
else
- return http_get_buf(HTTP_BUFFER_HEADER, 0, 0, p, b);
+ return get_buf(HTTP_BUFFER_HEADER, p , b);
case InspectionBuffer::IBT_BODY:
- return http_get_buf(HTTP_BUFFER_CLIENT_BODY, 0, 0, p, b);
+ return get_buf(HTTP_BUFFER_CLIENT_BODY, p, b);
default:
return false;
}
bool HttpInspect::get_buf(unsigned id, Packet* p, InspectionBuffer& b)
{
- return http_get_buf(id, 0, 0, p, b);
+ Cursor c;
+ HttpBufferInfo buffer_info(id);
+
+ const Field& http_buffer = http_get_buf(c, p, buffer_info);
+
+ if (http_buffer.length() <= 0)
+ return false;
+
+ b.data = http_buffer.start();
+ b.len = http_buffer.length();
+ return true;
}
-bool HttpInspect::http_get_buf(unsigned id, uint64_t sub_id, uint64_t form, Packet* p,
- InspectionBuffer& b)
+const Field& HttpInspect::http_get_buf(Cursor& c, Packet* p,
+ HttpBufferInfo& buffer_info)
{
HttpMsgSection* current_section = HttpContextData::get_snapshot(p);
if (current_section == nullptr)
- return false;
-
- const Field& buffer = current_section->get_classic_buffer(id, sub_id, form);
+ return Field::FIELD_NULL;
- if (buffer.length() <= 0)
- return false;
-
- b.data = buffer.start();
- b.len = buffer.length();
- return true;
+ return current_section->get_classic_buffer(c, buffer_info);
}
bool HttpInspect::get_fp_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b)
// HttpInspect class
//-------------------------------------------------------------------------
+#include "framework/cursor.h"
+#include "log/messages.h"
+
+#include "http_buffer_info.h"
#include "http_common.h"
#include "http_enum.h"
#include "http_field.h"
#include "http_module.h"
#include "http_msg_section.h"
#include "http_stream_splitter.h"
-#include "log/messages.h"
class HttpApi;
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;
- bool http_get_buf(unsigned id, uint64_t sub_id, uint64_t form, snort::Packet* p,
- snort::InspectionBuffer& b);
- bool get_fp_buf(snort::InspectionBuffer::Type ibt, snort::Packet* p, snort::InspectionBuffer& b) override;
+ const Field& http_get_buf(Cursor& c, snort::Packet* p,
+ HttpBufferInfo& buffer_info);
+ bool get_fp_buf(snort::InspectionBuffer::Type ibt, snort::Packet* p,
+ snort::InspectionBuffer& b) override;
bool configure(snort::SnortConfig*) override;
void show(snort::SnortConfig*) override;
void eval(snort::Packet* p) override;
{ "iis_unicode_code_page", Parameter::PT_INT, "0:65535", "1252",
"code page to use from the IIS unicode map file" },
- { "iis_double_decode", Parameter::PT_BOOL, nullptr, "false",
+ { "iis_double_decode", Parameter::PT_BOOL, nullptr, "true",
"perform double decoding of percent encodings to normalize characters" },
{ "oversize_dir_length", Parameter::PT_INT, "1:65535", "300",
"maximum length for URL directory" },
- { "backslash_to_slash", Parameter::PT_BOOL, nullptr, "false",
+ { "backslash_to_slash", Parameter::PT_BOOL, nullptr, "true",
"replace \\ with / when normalizing URIs" },
{ "plus_to_space", Parameter::PT_BOOL, nullptr, "true",
std::string iis_unicode_map_file;
int iis_unicode_code_page = 1252;
uint8_t* unicode_map = nullptr;
- bool iis_double_decode = false;
- bool backslash_to_slash = false;
+ bool iis_double_decode = true;
+ bool backslash_to_slash = true;
bool plus_to_space = true;
bool simplify_path = true;
std::bitset<256> bad_characters;
const Field& HttpMsgBody::get_classic_client_body()
{
- return classic_normalize(detect_data, classic_client_body, params->uri_param);
+ return classic_normalize(detect_data, classic_client_body, false, params->uri_param);
}
#ifdef REG_TEST
const Field& get_classic_client_body();
const Field& get_detect_data() { return detect_data; }
static void fd_event_callback(void* context, int event);
+ bool is_first() { return first_body; }
protected:
HttpMsgBody(const uint8_t* buffer, const uint16_t buf_size, HttpFlowData* session_data_,
const Field& HttpMsgHeadShared::get_classic_norm_header()
{
- return classic_normalize(get_classic_raw_header(), classic_norm_header, params->uri_param);
+ return classic_normalize(get_classic_raw_header(), classic_norm_header,
+ false, params->uri_param);
}
const Field& HttpMsgHeadShared::get_classic_raw_cookie()
const Field& HttpMsgHeadShared::get_classic_norm_cookie()
{
- return classic_normalize(get_classic_raw_cookie(), classic_norm_cookie, params->uri_param);
+ return classic_normalize(get_classic_raw_cookie(), classic_norm_cookie,
+ false, params->uri_param);
}
const Field& HttpMsgHeadShared::get_header_value_raw(HeaderId header_id) const
get_related_sections();
}
+HttpMsgRequest::~HttpMsgRequest()
+{
+ delete uri;
+ delete query_params;
+ delete body_params;
+}
+
void HttpMsgRequest::parse_start_line()
{
// Version field
return Field::FIELD_NULL;
}
+ParameterMap& HttpMsgRequest::get_query_params()
+{
+ if (query_params == nullptr)
+ query_params = new ParameterMap;
+
+ return *query_params;
+}
+
+ParameterMap& HttpMsgRequest::get_body_params()
+{
+ if (body_params == nullptr)
+ body_params = new ParameterMap;
+
+ return *body_params;
+}
+
void HttpMsgRequest::gen_events()
{
if (*transaction->get_infractions(source_id) & INF_BAD_REQ_LINE)
#include "http_common.h"
#include "http_enum.h"
#include "http_msg_start.h"
+#include "http_query_parser.h"
#include "http_str_to_code.h"
#include "http_uri.h"
#include "http_uri_norm.h"
HttpMsgRequest(const uint8_t* buffer, const uint16_t buf_size, HttpFlowData* session_data_,
HttpCommon::SourceId source_id_, bool buf_owner, snort::Flow* flow_,
const HttpParaList* params_);
- ~HttpMsgRequest() override { delete uri; }
+ ~HttpMsgRequest() override;
void gen_events() override;
void update_flow() override;
const Field& get_method() { return method; }
const Field& get_uri();
const Field& get_uri_norm_classic();
HttpUri* get_http_uri() { return uri; }
+ ParameterMap& get_query_params();
+ ParameterMap& get_body_params();
static bool is_webdav(HttpEnums::MethodId method)
{
Field method;
HttpUri* uri = nullptr;
+
+ ParameterMap* query_params = nullptr;
+ ParameterMap* body_params = nullptr;
};
#endif
#include "config.h"
#endif
+#include <cstring>
+
#include "service_inspectors/http2_inspect/http2_flow_data.h"
#include "http_msg_section.h"
#include "http_context_data.h"
#include "http_common.h"
#include "http_enum.h"
+#include "http_module.h"
#include "http_msg_body.h"
#include "http_msg_head_shared.h"
#include "http_msg_header.h"
#include "http_msg_request.h"
#include "http_msg_status.h"
#include "http_msg_trailer.h"
+#include "http_param.h"
+#include "http_query_parser.h"
#include "http_test_manager.h"
#include "stream/flush_bucket.h"
}
const Field& HttpMsgSection::classic_normalize(const Field& raw, Field& norm,
- const HttpParaList::UriParam& uri_param)
+ bool do_path, const HttpParaList::UriParam& uri_param)
{
if (norm.length() != STAT_NOT_COMPUTE)
return norm;
- if ((raw.length() <= 0) || !UriNormalizer::classic_need_norm(raw, true, uri_param))
+ if ((raw.length() <= 0) || !UriNormalizer::classic_need_norm(raw, do_path, uri_param))
{
norm.set(raw);
return norm;
}
- UriNormalizer::classic_normalize(raw, norm, uri_param);
+ UriNormalizer::classic_normalize(raw, norm, do_path, uri_param);
return 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);
+}
+
+const Field& HttpMsgSection::get_classic_buffer(Cursor& c, HttpBufferInfo& buf)
{
// buffer_side replaces source_id for buffers that support the request option
- const SourceId buffer_side = (form & FORM_REQUEST) ? SRC_CLIENT : source_id;
+ const SourceId buffer_side = (buf.form & FORM_REQUEST) ? SRC_CLIENT : source_id;
- switch (id)
+ switch (buf.type)
{
case HTTP_BUFFER_CLIENT_BODY:
{
{
if (header[buffer_side] == nullptr)
return Field::FIELD_NULL;
- return (id == HTTP_BUFFER_COOKIE) ? header[buffer_side]->get_classic_norm_cookie() :
+ return (buf.type == HTTP_BUFFER_COOKIE) ? header[buffer_side]->get_classic_norm_cookie() :
header[buffer_side]->get_classic_raw_cookie();
}
case HTTP_BUFFER_HEADER:
case HTTP_BUFFER_TRAILER:
{
// FIXIT-L Someday want to be able to return field name or raw field value
- HttpMsgHeadShared* const head = (id == HTTP_BUFFER_HEADER) ?
+ HttpMsgHeadShared* const head = (buf.type == HTTP_BUFFER_HEADER) ?
(HttpMsgHeadShared*)header[buffer_side] : (HttpMsgHeadShared*)trailer[buffer_side];
if (head == nullptr)
return Field::FIELD_NULL;
- if (sub_id == 0)
+ if (buf.sub_id == 0)
return head->get_classic_norm_header();
- return head->get_header_value_norm((HeaderId)sub_id);
+ return head->get_header_value_norm((HeaderId)buf.sub_id);
}
case HTTP_BUFFER_METHOD:
{
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;
case HTTP_BUFFER_URI:
case HTTP_BUFFER_RAW_URI:
{
- const bool raw = (id == HTTP_BUFFER_RAW_URI);
+ const bool raw = (buf.type == HTTP_BUFFER_RAW_URI);
if (request == nullptr)
return Field::FIELD_NULL;
- if (sub_id == 0)
+ if (buf.sub_id == 0)
return raw ? request->get_uri() : request->get_uri_norm_classic();
HttpUri* const uri = request->get_http_uri();
if (uri == nullptr)
return Field::FIELD_NULL;
- switch ((UriComponent)sub_id)
+ switch ((UriComponent)buf.sub_id)
{
case UC_SCHEME:
return uri->get_scheme();
#define HTTP_MSG_SECTION_H
#include "detection/detection_util.h"
+#include "framework/cursor.h"
+#include "http_buffer_info.h"
#include "http_common.h"
+#include "http_cursor_data.h"
#include "http_enum.h"
#include "http_field.h"
-#include "http_module.h"
#include "http_flow_data.h"
+#include "http_module.h"
#include "http_transaction.h"
//-------------------------------------------------------------------------
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, HttpBufferInfo& buf);
HttpEnums::MethodId get_method_id() const { return method_id; }
void create_event(int sid);
void update_depth() const;
static const Field& classic_normalize(const Field& raw, Field& norm,
- const HttpParaList::UriParam& uri_param);
+ bool do_path, const HttpParaList::UriParam& uri_param);
#ifdef REG_TEST
void print_section_title(FILE* output, const char* title) const;
void print_section_wrapup(FILE* output) const;
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// http_param.h author Brandon Stultz <brastult@cisco.com>
+
+#ifndef HTTP_PARAM_H
+#define HTTP_PARAM_H
+
+#include <algorithm>
+#include <cassert>
+#include <string>
+
+#include "helpers/literal_search.h"
+
+class HttpParam
+{
+public:
+ HttpParam(const std::string& param_, bool nocase_)
+ : param(param_), param_upper(param_), nocase(nocase_)
+ {
+ assert(param.length() > 0);
+
+ std::transform(param_upper.begin(), param_upper.end(),
+ param_upper.begin(), ::toupper);
+
+ const uint8_t* pattern = (const uint8_t*)param_upper.c_str();
+ unsigned pattern_length = param_upper.length();
+
+ search_handle = snort::LiteralSearch::setup();
+
+ searcher = snort::LiteralSearch::instantiate(
+ search_handle, pattern, pattern_length, true
+ );
+ }
+
+ ~HttpParam()
+ {
+ delete searcher;
+ snort::LiteralSearch::cleanup(search_handle);
+ }
+
+ bool operator==(const HttpParam& rhs) const
+ { return param == rhs.param && nocase == rhs.nocase; }
+
+ const std::string& str() const
+ { return param; }
+
+ const std::string& str_upper() const
+ { return param_upper; }
+
+ const char* c_str() const
+ { return param.c_str(); }
+
+ unsigned length() const
+ { return param.length(); }
+
+ bool is_nocase() const
+ { return nocase; }
+
+ int search_nocase(const uint8_t* buffer, unsigned buffer_len) const
+ {
+ assert(searcher);
+ return searcher->search(search_handle, buffer, buffer_len);
+ }
+
+private:
+ std::string param;
+ std::string param_upper;
+ bool nocase = false;
+ snort::LiteralSearch* searcher = nullptr;
+ snort::LiteralSearch::Handle* search_handle = nullptr;
+};
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// http_query_parser.cc author Brandon Stultz <brastult@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cstring>
+
+#include "http_enum.h"
+#include "http_query_parser.h"
+#include "http_uri_norm.h"
+
+using namespace HttpEnums;
+
+void HttpQueryParser::create_event(int sid)
+{
+ session_data->events[source_id]->create_event(sid);
+}
+
+void HttpQueryParser::unescape(const Field& raw, Field& norm)
+{
+ if ( raw.length() > 0 )
+ {
+ if ( UriNormalizer::classic_need_norm(raw, false, uri_config) )
+ {
+ UriNormalizer::classic_normalize(raw, norm, false, uri_config);
+ return;
+ }
+ }
+
+ norm.set(raw);
+}
+
+void HttpQueryParser::parse(const HttpParam& param, ParameterData& data)
+{
+ // check if parameter is present in normalized buffer
+ if( param.search_nocase(norm_buffer, norm_buffer_len) < 0 )
+ return;
+
+ const std::string& key = param.str_upper();
+
+ unsigned kv_index = 0;
+ index = 0;
+
+ while ( index < buffer_len )
+ {
+ if ( kv_index >= MAX_REPEAT_PARAMS )
+ {
+ HttpModule::increment_peg_counts(PEG_EXCESS_PARAMS);
+ create_event(EVENT_EXCESS_REPEAT_PARAMS);
+ return;
+ }
+
+ Parameter p = {};
+
+ if ( !parse_parameter(p) )
+ return;
+
+ if ( p.key_len == 0 )
+ continue;
+
+ Field raw_key(p.key_len, p.key);
+ Field raw_value(p.value_len, p.value);
+
+ KeyValue* fields = new KeyValue;
+
+ Field& norm_key = fields->key;
+ Field& norm_value = fields->value;
+
+ // normalize the key
+ unescape(raw_key, norm_key);
+
+ if ( (unsigned)norm_key.length() != key.length() )
+ {
+ delete fields;
+ continue;
+ }
+
+ const char* norm_key_str = (const char*)norm_key.start();
+
+ if ( strncasecmp(norm_key_str, key.c_str(), key.length()) )
+ {
+ delete fields;
+ continue;
+ }
+
+ // normalize the value
+ unescape(raw_value, norm_value);
+
+ // cache the parameter
+ data.kv_vec.push_back(fields);
+ HttpModule::increment_peg_counts(PEG_PARAMS);
+ kv_index++;
+ }
+}
+
+bool HttpQueryParser::parse_parameter(Parameter& p)
+{
+ if ( !parse_key(p) )
+ return false;
+
+ if ( !parse_value(p) )
+ return false;
+
+ return true;
+}
+
+bool HttpQueryParser::parse_key(Parameter& p)
+{
+ const uint8_t* term;
+
+ if ( index >= buffer_len )
+ return false;
+
+ p.key = buffer + index;
+
+ unsigned remaining = buffer_len - index;
+
+ // locate delimiter
+ term = (const uint8_t*)memchr(p.key, '=', remaining);
+
+ if ( !term )
+ return false;
+
+ p.key_len = term - p.key;
+
+ index += p.key_len + 1;
+
+ return true;
+}
+
+bool HttpQueryParser::parse_value(Parameter& p)
+{
+ const uint8_t* amp;
+ const uint8_t* semi;
+ const uint8_t* term;
+
+ if ( index >= buffer_len )
+ return false;
+
+ p.value = buffer + index;
+
+ unsigned remaining = buffer_len - index;
+
+ // locate delimiter
+ amp = (const uint8_t*)memchr(p.value, '&', remaining);
+ semi = (const uint8_t*)memchr(p.value, ';', remaining);
+
+ if ( amp && !semi )
+ term = amp;
+ else if ( !amp && semi )
+ term = semi;
+ else
+ term = (amp < semi) ? amp : semi;
+
+ if ( !term )
+ {
+ // last parameter
+ p.value_len = remaining;
+ index += remaining;
+ return true;
+ }
+
+ p.value_len = term - p.value;
+
+ index += p.value_len + 1;
+
+ return true;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// http_query_parser.h author Brandon Stultz <brastult@cisco.com>
+
+#ifndef HTTP_QUERY_PARSER_H
+#define HTTP_QUERY_PARSER_H
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "http_common.h"
+#include "http_field.h"
+#include "http_flow_data.h"
+#include "http_module.h"
+#include "http_param.h"
+
+struct KeyValue
+{
+ Field key;
+ Field value;
+};
+
+typedef std::vector<KeyValue*> KeyValueVec;
+
+class ParameterData
+{
+public:
+ ParameterData() = default;
+
+ ~ParameterData()
+ {
+ for ( KeyValue* kv : kv_vec )
+ delete kv;
+ }
+
+public:
+ KeyValueVec kv_vec;
+ bool parsed = false;
+};
+
+typedef std::unordered_map<std::string, ParameterData> ParameterMap;
+
+class HttpQueryParser
+{
+public:
+ HttpQueryParser(const uint8_t* buffer_, unsigned buffer_len_,
+ const uint8_t* norm_buffer_, unsigned norm_buffer_len_,
+ const HttpParaList::UriParam& uri_config_,
+ HttpFlowData* session_data_,
+ HttpCommon::SourceId source_id_)
+ : buffer(buffer_), buffer_len(buffer_len_),
+ norm_buffer(norm_buffer_), norm_buffer_len(norm_buffer_len_),
+ uri_config(uri_config_), session_data(session_data_),
+ source_id(source_id_) {}
+
+ void parse(const HttpParam& param, ParameterData& data);
+
+ struct Parameter
+ {
+ const uint8_t* key;
+ const uint8_t* value;
+ unsigned key_len;
+ unsigned value_len;
+ };
+
+private:
+ void create_event(int sid);
+
+ void unescape(const Field& raw, Field& norm);
+
+ bool parse_parameter(Parameter& p);
+ bool parse_key(Parameter& p);
+ bool parse_value(Parameter& p);
+
+ const uint8_t* buffer;
+ unsigned buffer_len;
+
+ const uint8_t* norm_buffer;
+ unsigned norm_buffer_len;
+
+ unsigned index;
+
+ static const unsigned MAX_REPEAT_PARAMS = 100;
+
+ const HttpParaList::UriParam& uri_config;
+ HttpFlowData* const session_data;
+ const HttpCommon::SourceId source_id;
+};
+
+#endif
+
{ EVENT_BAD_HEADER_WHITESPACE, "white space embedded in critical header value" },
{ EVENT_GZIP_EARLY_END, "gzip compressed data followed by unexpected non-gzip "
"data" },
+ { EVENT_EXCESS_REPEAT_PARAMS, "excessive HTTP parameter key repeats" },
{ 0, nullptr }
};
{ CountType::MAX, "max_concurrent_sessions", "maximum concurrent http sessions" },
{ CountType::SUM, "detained_packets", "TCP packets delayed by detained inspection" },
{ CountType::SUM, "partial_inspections", "pre-inspections for detained inspection" },
+ { CountType::SUM, "excess_parameters", "repeat parameters exceeding max" },
+ { CountType::SUM, "parameters", "HTTP parameters inspected" },
{ CountType::END, nullptr, nullptr }
};
// Provide traditional URI-style normalization for buffers that usually are not URIs
void UriNormalizer::classic_normalize(const Field& input, Field& result,
- const HttpParaList::UriParam& uri_param)
+ bool do_path, const HttpParaList::UriParam& uri_param)
{
// The requirements for generating events related to these normalizations are unclear. It
// definitely doesn't seem right to generate standard URI events. For now we won't generate
// Normalize character escape sequences
int32_t data_length = norm_char_clean(input, buffer, uri_param, &unused, &dummy_ev);
- if (uri_param.simplify_path)
+ if (do_path && uri_param.simplify_path)
{
// Normalize path directory traversals
// Find the leading slash if there is one
HttpEventGen* events);
static bool classic_need_norm(const Field& uri_component, bool do_path,
const HttpParaList::UriParam& uri_param);
- static void classic_normalize(const Field& input, Field& result,
+ static void classic_normalize(const Field& input, Field& result, bool do_path,
const HttpParaList::UriParam& uri_param);
static void load_default_unicode_map(uint8_t map[65536]);
static void load_unicode_map(uint8_t map[65536], const char* filename, int code_page);
#include "framework/cursor.h"
#include "hash/hashfcn.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_flow_data.h"
#include "http_inspect.h"
#include "http_msg_head_shared.h"
+#include "http_param.h"
using namespace snort;
using namespace HttpCommon;
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:
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;
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 (buffer_index == HTTP_BUFFER_PARAM && para_list.param.length() == 0)
+ ParseError("Specify parameter name");
return true;
}
void HttpCursorModule::HttpRuleParaList::reset()
{
field.clear();
+ param.clear();
+ nocase = false;
request = false;
with_header = false;
with_body = false;
{
uint32_t a = IpsOption::hash();
uint32_t b = (uint32_t)inspect_section;
- uint32_t c = sub_id >> 32;
- uint32_t d = sub_id & 0xFFFFFFFF;
- uint32_t e = form >> 32;
- uint32_t f = form & 0xFFFFFFFF;
+ uint32_t c = buffer_info.hash();
mix(a,b,c);
- mix(d,e,f);
- mix(a,c,f);
- finalize(a,c,f);
- return f;
+ finalize(a,b,c);
+ return c;
}
bool HttpIpsOption::operator==(const IpsOption& ips) const
const HttpIpsOption& hio = static_cast<const HttpIpsOption&>(ips);
return IpsOption::operator==(ips) &&
inspect_section == hio.inspect_section &&
- sub_id == hio.sub_id &&
- form == hio.form;
+ buffer_info == hio.buffer_info;
+}
+
+bool HttpIpsOption::retry(Cursor& c)
+{
+ if (buffer_info.type == HTTP_BUFFER_PARAM)
+ {
+ HttpCursorData* cd = (HttpCursorData*)c.get_data(HttpCursorData::id);
+
+ if (cd)
+ return cd->retry();
+ }
+ return false;
}
IpsOption::EvalStatus HttpIpsOption::eval(Cursor& c, Packet* p)
return NO_MATCH;
const Http2FlowData* const h2i_flow_data =
- (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id);
+ (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id);
HttpInspect* const hi = (h2i_flow_data != nullptr) ?
(HttpInspect*)(p->flow->assistant_gadget) : (HttpInspect*)(p->flow->gadget);
- InspectionBuffer hb;
+ const Field& http_buffer = hi->http_get_buf(c, p, buffer_info);
- if (! (hi->http_get_buf((unsigned)buffer_index, sub_id, form, p, hb)))
+ if (http_buffer.length() <= 0)
return NO_MATCH;
- c.set(key, hb.data, hb.len);
+ c.set(key, http_buffer.start(), http_buffer.length());
return MATCH;
}
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 HttpCursorModule(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,
+ HttpCursorModule::mod_dtor
+ },
+ OPT_TYPE_DETECTION,
+ 0, PROTO_BIT__TCP,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ HttpIpsOption::opt_ctor,
+ HttpIpsOption::opt_dtor,
+ nullptr
+};
+
//-------------------------------------------------------------------------
// http_raw_body
//-------------------------------------------------------------------------
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_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;
#include "framework/ips_option.h"
#include "framework/module.h"
+#include "http_buffer_info.h"
#include "http_enum.h"
-enum PsIdx { PSI_CLIENT_BODY, PSI_COOKIE, PSI_HEADER, PSI_METHOD, 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_MAX };
+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_MAX };
class HttpCursorModule : public snort::Module
{
{
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;
{
public:
HttpIpsOption(const HttpCursorModule* cm) :
- snort::IpsOption(cm->key, RULE_OPTION_TYPE_BUFFER_SET), key(cm->key),
- buffer_index(cm->buffer_index), cat(cm->cat), psi(cm->psi),
- inspect_section(cm->inspect_section), sub_id(cm->sub_id), form(cm->form) {}
+ 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->buffer_index, cm->sub_id, cm->form,
+ cm->para_list.param, cm->para_list.nocase) {}
snort::CursorActionType get_cursor_type() const override { return cat; }
EvalStatus eval(Cursor&, snort::Packet*) override;
uint32_t hash() const override;
bool operator==(const snort::IpsOption& ips) const override;
+ bool retry(Cursor&) override;
static IpsOption* opt_ctor(snort::Module* m, OptTreeNode*)
{ return new HttpIpsOption((HttpCursorModule*)m); }
static void opt_dtor(snort::IpsOption* p) { delete p; }
private:
const char* const key;
- const HttpEnums::HTTP_BUFFER buffer_index;
const snort::CursorActionType cat;
const PsIdx psi;
const HttpEnums::InspectSection inspect_section;
- const uint64_t sub_id;
- const uint64_t form;
+ HttpBufferInfo buffer_info;
};
#endif