From: Russ Combs (rucombs) Date: Thu, 7 Jan 2016 22:54:27 +0000 (-0500) Subject: Merge pull request #198 in SNORT/snort3 from nhttp31 to master X-Git-Tag: 3.0.0-233~669 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7892df5064e549b4cbef95da8e92b1d592a48618;p=thirdparty%2Fsnort3.git Merge pull request #198 in SNORT/snort3 from nhttp31 to master Squashed commit of the following: commit 96a369d55ffe5bfb086286d61b41a8e40ca289fa Author: Tom Peters Date: Thu Jan 7 17:41:43 2016 -0500 code review cleanup + URI-related cleanup commit 0c202be8b9128b377994b95309ba8ffd8e7f7cd7 Author: Tom Peters Date: Wed Nov 11 17:47:18 2015 -0500 NHI IPS rule options --- diff --git a/doc/bugs.txt b/doc/bugs.txt index ca09d5a9d..f06f712c7 100644 --- a/doc/bugs.txt +++ b/doc/bugs.txt @@ -35,6 +35,10 @@ * --lua can only be used in addition to, not in place of, a -c config. Ideally, --lua could be used in lieu of -c. +* Rule line numbers provided with syntax error messages are off by one. The first rule is + unnumbered, the second rule is one, etc. See nhttp_inspect/detection_buffers/bad_rules/expected + for an example. + ==== Rules diff --git a/src/detection/fp_detect.cc b/src/detection/fp_detect.cc index a6a3625c3..92d8a3292 100644 --- a/src/detection/fp_detect.cc +++ b/src/detection/fp_detect.cc @@ -975,7 +975,7 @@ static int rule_tree_queue( } #define SEARCH_BUFFER(ibt, pmt, cnt) \ - if ( gadget->get_buf(ibt, p, buf) ) \ + if ( gadget->get_fp_buf(ibt, p, buf) ) \ { \ if ( Mpse* so = port_group->mpse[pmt] ) \ SEARCH_DATA(buf.data, buf.len, cnt) \ diff --git a/src/framework/cursor.cc b/src/framework/cursor.cc index 9e631d3e7..642791186 100644 --- a/src/framework/cursor.cc +++ b/src/framework/cursor.cc @@ -38,6 +38,7 @@ void Cursor::reset(Packet* p) { InspectionBuffer buf; + // FIXIT-M should this be converted to get_fp_buf()? if ( p->flow and p->flow->gadget and p->flow->gadget->get_buf(buf.IBT_ALT, p, buf) ) { diff --git a/src/framework/inspector.h b/src/framework/inspector.h index 9c27d0693..afb9e9550 100644 --- a/src/framework/inspector.h +++ b/src/framework/inspector.h @@ -108,6 +108,9 @@ public: virtual bool get_buf(unsigned /*id*/, Packet*, InspectionBuffer&) { return false; } + virtual bool get_fp_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& bf) + { return get_buf(ibt, p, bf); } + // IT_SERVICE only virtual class StreamSplitter* get_splitter(bool to_server); diff --git a/src/piglet_plugins/pp_inspector_iface.cc b/src/piglet_plugins/pp_inspector_iface.cc index 280d340fe..ca9aee2e2 100644 --- a/src/piglet_plugins/pp_inspector_iface.cc +++ b/src/piglet_plugins/pp_inspector_iface.cc @@ -31,6 +31,7 @@ #include "pp_raw_buffer_iface.h" #include "pp_stream_splitter_iface.h" +// FIXIT-H needs to be updated for addition of get_fp_buf() template static inline bool get_buf( Inspector& i, T v, Packet& p, std::string& rb) diff --git a/src/service_inspectors/nhttp_inspect/ips_nhttp.cc b/src/service_inspectors/nhttp_inspect/ips_nhttp.cc index ba45e5329..ec2b00f2d 100644 --- a/src/service_inspectors/nhttp_inspect/ips_nhttp.cc +++ b/src/service_inspectors/nhttp_inspect/ips_nhttp.cc @@ -36,6 +36,7 @@ bool NHttpCursorModule::begin(const char*, int, SnortConfig*) { para_list.reset(); sub_id = 0; + form = 0; switch (buffer_index) { case NHTTP_BUFFER_URI: @@ -44,13 +45,13 @@ bool NHttpCursorModule::begin(const char*, int, SnortConfig*) case NHTTP_BUFFER_STAT_MSG: case NHTTP_BUFFER_VERSION: case NHTTP_BUFFER_METHOD: - inspect_section = IS_START; - break; case NHTTP_BUFFER_HEADER: case NHTTP_BUFFER_RAW_HEADER: case NHTTP_BUFFER_COOKIE: case NHTTP_BUFFER_RAW_COOKIE: - inspect_section = IS_HEADER; + case NHTTP_BUFFER_RAW_REQUEST: + case NHTTP_BUFFER_RAW_STATUS: + inspect_section = IS_DETECTION; break; case NHTTP_BUFFER_CLIENT_BODY: inspect_section = IS_BODY; @@ -84,10 +85,15 @@ bool NHttpCursorModule::set(const char*, Value& v, SnortConfig*) if (sub_id == STAT_OTHER) ParseError("Unrecognized header field name"); } + else if (v.is("request")) + { + para_list.request = true; + form |= FORM_REQUEST; + } else if (v.is("with_header")) { para_list.with_header = true; - inspect_section = IS_HEADER; + inspect_section = IS_DETECTION; } else if (v.is("with_body")) { @@ -141,6 +147,10 @@ bool NHttpCursorModule::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 (((buffer_index == NHTTP_BUFFER_TRAILER) || (buffer_index == NHTTP_BUFFER_RAW_TRAILER)) && + (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"); @@ -150,6 +160,7 @@ bool NHttpCursorModule::end(const char*, int, SnortConfig*) void NHttpCursorModule::NHttpRuleParaList::reset() { field.clear(); + request = false; with_header = false; with_body = false; with_trailer = false; @@ -169,11 +180,17 @@ int NHttpIpsOption::eval(Cursor& c, Packet* p) return DETECTION_OPTION_NO_MATCH; if (NHttpInspect::get_latest_is() != inspect_section) - return DETECTION_OPTION_NO_MATCH; + { + // It is OK to provide a body buffer during the detection section. If there actually is + // a body buffer available then the detection section must also be the first body section. + if (! ((inspect_section == IS_BODY) && (NHttpInspect::get_latest_is() == IS_DETECTION)) ) + return DETECTION_OPTION_NO_MATCH; + } InspectionBuffer hb; - if (! ((NHttpInspect*)(p->flow->gadget))->get_buf((unsigned)buffer_index, sub_id, nullptr, hb)) + if (! ((NHttpInspect*)(p->flow->gadget))-> + get_buf((unsigned)buffer_index, sub_id, form, nullptr, hb)) return DETECTION_OPTION_NO_MATCH; c.set(key, hb.data, hb.len); @@ -187,8 +204,6 @@ int NHttpIpsOption::eval(Cursor& c, Packet* p) static const Parameter http_uri_params[] = { - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "Parts of this rule examine 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, @@ -290,8 +305,6 @@ static const IpsApi client_body_api = static const Parameter http_method_params[] = { - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "Parts of this rule examine 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, @@ -341,6 +354,8 @@ static const IpsApi method_api = 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_body", Parameter::PT_IMPLIED, nullptr, nullptr, "Parts of this rule examine HTTP message body" }, { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, @@ -390,8 +405,6 @@ static const IpsApi cookie_api = static const Parameter http_stat_code_params[] = { - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "Parts of this rule examine 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, @@ -441,8 +454,6 @@ static const IpsApi stat_code_api = static const Parameter http_stat_msg_params[] = { - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "Parts of this rule examine 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, @@ -492,8 +503,6 @@ static const IpsApi stat_msg_api = static const Parameter http_raw_uri_params[] = { - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "Parts of this rule examine 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, @@ -555,6 +564,8 @@ static const IpsApi raw_uri_api = static const Parameter http_raw_header_params[] = { + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "Match against the headers from the request message even when examining the response" }, { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, "Parts of this rule examine HTTP message body" }, { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, @@ -604,6 +615,8 @@ static const IpsApi raw_header_api = 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_body", Parameter::PT_IMPLIED, nullptr, nullptr, "Parts of this rule examine HTTP message body" }, { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, @@ -653,8 +666,8 @@ static const IpsApi raw_cookie_api = static const Parameter http_version_params[] = { - { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr, - "Parts of this rule examine HTTP message headers" }, + { "request", Parameter::PT_IMPLIED, nullptr, nullptr, + "Match against the version from the request message even when examining the response" }, { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr, "Parts of this rule examine HTTP message body" }, { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr, @@ -708,12 +721,14 @@ static const IpsApi version_api = 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_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" }, - { "field", Parameter::PT_STRING, nullptr, nullptr, - "Restrict to given header. Header name is case insensitive." }, { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; @@ -760,6 +775,13 @@ static const IpsApi header_api = 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 } }; @@ -770,7 +792,7 @@ static const Parameter http_trailer_params[] = static Module* trailer_mod_ctor() { - return new NHttpCursorModule(IPS_OPT, IPS_HELP, NHTTP_BUFFER_TRAILER, CAT_SET_OTHER, + return new NHttpCursorModule(IPS_OPT, IPS_HELP, NHTTP_BUFFER_TRAILER, CAT_SET_HEADER, PSI_TRAILER, http_trailer_params); } @@ -803,6 +825,18 @@ static const IpsApi trailer_api = // http_raw_trailer //------------------------------------------------------------------------- +static const Parameter http_raw_trailer_params[] = +{ + { "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 @@ -811,7 +845,7 @@ static const IpsApi trailer_api = static Module* raw_trailer_mod_ctor() { return new NHttpCursorModule(IPS_OPT, IPS_HELP, NHTTP_BUFFER_RAW_TRAILER, CAT_SET_OTHER, - PSI_RAW_TRAILER); + PSI_RAW_TRAILER, http_raw_trailer_params); } static const IpsApi raw_trailer_api = @@ -839,6 +873,104 @@ static const IpsApi raw_trailer_api = nullptr }; +//------------------------------------------------------------------------- +// http_raw_request +//------------------------------------------------------------------------- + +static const Parameter http_raw_request_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_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 NHttpCursorModule(IPS_OPT, IPS_HELP, NHTTP_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, + NHttpCursorModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + NHttpIpsOption::opt_ctor, + NHttpIpsOption::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 NHttpCursorModule(IPS_OPT, IPS_HELP, NHTTP_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, + NHttpCursorModule::mod_dtor + }, + OPT_TYPE_DETECTION, + 0, PROTO_BIT__TCP, + nullptr, + nullptr, + nullptr, + nullptr, + NHttpIpsOption::opt_ctor, + NHttpIpsOption::opt_dtor, + nullptr +}; + //------------------------------------------------------------------------- // plugins //------------------------------------------------------------------------- @@ -858,6 +990,8 @@ SO_PUBLIC const BaseApi* snort_plugins[] = &header_api.base, &trailer_api.base, &raw_trailer_api.base, + &raw_request_api.base, + &raw_status_api.base, nullptr }; diff --git a/src/service_inspectors/nhttp_inspect/ips_nhttp.h b/src/service_inspectors/nhttp_inspect/ips_nhttp.h index 564cad283..13523a56e 100644 --- a/src/service_inspectors/nhttp_inspect/ips_nhttp.h +++ b/src/service_inspectors/nhttp_inspect/ips_nhttp.h @@ -36,7 +36,7 @@ enum PsIdx { PSI_URI, PSI_CLIENT_BODY, PSI_METHOD, PSI_COOKIE, PSI_STAT_CODE, PSI_STAT_MSG, PSI_RAW_URI, PSI_RAW_HEADER, PSI_RAW_COOKIE, PSI_HEADER, PSI_VERSION, PSI_TRAILER, - PSI_RAW_TRAILER, PSI_MAX }; + PSI_RAW_TRAILER, PSI_RAW_REQUEST, PSI_RAW_STATUS, PSI_MAX }; class NHttpCursorModule : public Module { @@ -61,6 +61,7 @@ private: { public: std::string field; // provide buffer containing specific header field + 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; @@ -81,7 +82,8 @@ private: NHttpRuleParaList para_list; NHttpEnums::InspectSection inspect_section; - unsigned sub_id; + uint64_t sub_id; + uint64_t form; }; class NHttpIpsOption : public IpsOption @@ -89,7 +91,7 @@ class NHttpIpsOption : public IpsOption public: NHttpIpsOption(const NHttpCursorModule* cm) : IpsOption(cm->key), key(cm->key), buffer_index(cm->buffer_index), cat(cm->cat), psi(cm->psi), - inspect_section(cm->inspect_section), sub_id(cm->sub_id) {} + inspect_section(cm->inspect_section), sub_id(cm->sub_id), form(cm->form) {} CursorActionType get_cursor_type() const override { return cat; } int eval(Cursor&, Packet*) override; static IpsOption* opt_ctor(Module* m, OptTreeNode*) @@ -101,7 +103,8 @@ private: const CursorActionType cat; const PsIdx psi; const NHttpEnums::InspectSection inspect_section; - const unsigned sub_id; + const uint64_t sub_id; + const uint64_t form; }; #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_api.cc b/src/service_inspectors/nhttp_inspect/nhttp_api.cc index d2b06830e..52bce483f 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_api.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_api.cc @@ -33,7 +33,7 @@ Inspector* NHttpApi::nhttp_ctor(Module* mod) return new NHttpInspect(nhttp_mod->get_params()); } -const char* NHttpApi::legacy_buffers[] = +const char* NHttpApi::classic_buffers[] = { "http_client_body", "http_cookie", @@ -48,6 +48,8 @@ const char* NHttpApi::legacy_buffers[] = "http_version", "http_trailer", "http_raw_trailer", + "http_raw_request", + "http_raw_status", nullptr }; @@ -67,7 +69,7 @@ const InspectApi NHttpApi::nhttp_api = }, IT_SERVICE, (uint16_t)PktType::PDU, - legacy_buffers, + classic_buffers, "http", NHttpApi::nhttp_init, NHttpApi::nhttp_term, diff --git a/src/service_inspectors/nhttp_inspect/nhttp_api.h b/src/service_inspectors/nhttp_inspect/nhttp_api.h index d1fc7f600..358138a6a 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_api.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_api.h @@ -31,7 +31,7 @@ class NHttpApi { public: static const InspectApi nhttp_api; - static const char* legacy_buffers[]; + static const char* classic_buffers[]; private: NHttpApi() = delete; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_enum.h b/src/service_inspectors/nhttp_inspect/nhttp_enum.h index 8fec1726c..0549544ce 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_enum.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_enum.h @@ -31,30 +31,34 @@ static const int GZIP_BLOCK_SIZE = 2048; static const int FINAL_GZIP_BLOCK_SIZE = 2304; // compromise value, too big causes gzip overruns // too small leaves too many little end sections static const uint32_t NHTTP_GID = 219; -static const int GZIP_WINDOWBITS = 31; -static const int DEFLATE_WINDOWBITS = 15; +static const int GZIP_WINDOW_BITS = 31; +static const int DEFLATE_WINDOW_BITS = 15; static const int MAX_FIELD_NAME_LENGTH = 100; +// This can grow into a bitmap for the get_buf() form parameter +static const uint64_t FORM_REQUEST = 0x1; + // Field status codes for when no valid value is present in length or integer value. Positive // values are actual length or field value. -enum StatusCode { STAT_NOSOURCE=-16, STAT_NOTCONFIGURED=-15, STAT_NOTCOMPUTE=-14, - STAT_INSUFMEMORY=-13, STAT_PROBLEMATIC=-12, STAT_NOTPRESENT=-11, STAT_EMPTYSTRING=0, +enum StatusCode { STAT_NO_SOURCE=-16, STAT_NOT_CONFIGURED=-15, STAT_NOT_COMPUTE=-14, + STAT_INSUF_MEMORY=-13, STAT_PROBLEMATIC=-12, STAT_NOT_PRESENT=-11, STAT_EMPTY_STRING=0, STAT_OTHER=1 }; // Message originator--client or server -enum SourceId { SRC__NOTCOMPUTE=-14, SRC_CLIENT=0, SRC_SERVER=1 }; +enum SourceId { SRC__NOT_COMPUTE=-14, SRC_CLIENT=0, SRC_SERVER=1 }; // Type of message section -enum SectionType { SEC_DISCARD = -19, SEC_ABORT = -18, SEC__NOTCOMPUTE=-14, SEC__NOTPRESENT=-11, +enum SectionType { SEC_DISCARD = -19, SEC_ABORT = -18, SEC__NOT_COMPUTE=-14, SEC__NOT_PRESENT=-11, SEC_REQUEST = 2, SEC_STATUS, SEC_HEADER, SEC_BODY_CL, SEC_BODY_CHUNK, SEC_TRAILER, SEC_BODY_OLD }; // Message buffers available to clients -// This enum must remain synchronized with legacy_buffers[] +// This enum must remain synchronized with classic_buffers[] enum NHTTP_BUFFER { NHTTP_BUFFER_CLIENT_BODY = 1, NHTTP_BUFFER_COOKIE, NHTTP_BUFFER_HEADER, NHTTP_BUFFER_METHOD, NHTTP_BUFFER_RAW_COOKIE, NHTTP_BUFFER_RAW_HEADER, NHTTP_BUFFER_RAW_URI, NHTTP_BUFFER_STAT_CODE, NHTTP_BUFFER_STAT_MSG, NHTTP_BUFFER_URI, NHTTP_BUFFER_VERSION, - NHTTP_BUFFER_TRAILER, NHTTP_BUFFER_RAW_TRAILER, NHTTP_BUFFER_MAX }; + NHTTP_BUFFER_TRAILER, NHTTP_BUFFER_RAW_TRAILER, NHTTP_BUFFER_RAW_REQUEST, + NHTTP_BUFFER_RAW_STATUS, NHTTP_BUFFER_MAX }; // Result of scanning by splitter enum ScanResult { SCAN_NOTFOUND, SCAN_FOUND, SCAN_FOUND_PIECE, SCAN_DISCARD, SCAN_DISCARD_PIECE, @@ -66,12 +70,12 @@ enum ChunkState { CHUNK_ZEROS, CHUNK_NUMBER, CHUNK_WHITESPACE, CHUNK_OPTIONS, CH // List of possible HTTP versions. Version 0.9 omitted because 0.9 predates creation of the // HTTP/X.Y token. There would never be a message with "HTTP/0.9" -enum VersionId { VERS__NOSOURCE=-16, VERS__NOTCOMPUTE=-14, VERS__PROBLEMATIC=-12, - VERS__NOTPRESENT=-11, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 }; +enum VersionId { VERS__NO_SOURCE=-16, VERS__NOT_COMPUTE=-14, VERS__PROBLEMATIC=-12, + VERS__NOT_PRESENT=-11, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 }; // Every request method we have ever heard of -enum MethodId { METH__NOSOURCE=-16, METH__NOTCOMPUTE=-14, METH__INSUFMEMORY=-13, - METH__PROBLEMATIC=-12, METH__NOTPRESENT=-11, METH__OTHER=1, METH_OPTIONS, METH_GET, METH_HEAD, +enum MethodId { METH__NO_SOURCE=-16, METH__NOT_COMPUTE=-14, METH__INSUF_MEMORY=-13, + METH__PROBLEMATIC=-12, METH__NOT_PRESENT=-11, METH__OTHER=1, METH_OPTIONS, METH_GET, METH_HEAD, METH_POST, METH_PUT, METH_DELETE, METH_TRACE, METH_CONNECT, METH_PROPFIND, METH_PROPPATCH, METH_MKCOL, METH_COPY, METH_MOVE, METH_LOCK, METH_UNLOCK, METH_VERSION_CONTROL, METH_REPORT, METH_CHECKOUT, METH_CHECKIN, METH_UNCHECKOUT, METH_MKWORKSPACE, METH_UPDATE, METH_LABEL, @@ -82,25 +86,21 @@ enum MethodId { METH__NOSOURCE=-16, METH__NOTCOMPUTE=-14, METH__INSUFMEMORY=-13, METH_UPDATEREDIRECTREF }; // URI formats -enum UriType { URI__NOTCOMPUTE=-14, URI__PROBLEMATIC=-12, URI_ASTERISK = 2, URI_AUTHORITY, +enum UriType { URI__NOT_COMPUTE=-14, URI__PROBLEMATIC=-12, URI_ASTERISK = 2, URI_AUTHORITY, URI_ABSPATH, URI_ABSOLUTE }; -// URI schemes -enum SchemeId { SCH__NOSOURCE=-16, SCH__NOTCOMPUTE=-14, SCH__INSUFMEMORY=-13, SCH__NOTPRESENT=-11, - SCH_OTHER = 1, SCH_HTTP, SCH_HTTPS, SCH_FTP, SCH_GOPHER, SCH_FILE }; - // Body compression tpyes enum CompressId { CMP_NONE=2, CMP_GZIP, CMP_DEFLATE }; // Message section in which an IPS option provides the buffer -enum InspectSection { IS_NONE, IS_START, IS_HEADER, IS_BODY, IS_TRAILER }; +enum InspectSection { IS_NONE, IS_DETECTION, IS_BODY, IS_TRAILER }; // Part of the URI to be provided enum UriComponent { UC_SCHEME = 1, UC_HOST, UC_PORT, UC_PATH, UC_QUERY, UC_FRAGMENT }; // Every header we have ever heard of -enum HeaderId { HEAD__NOTCOMPUTE=-14, HEAD__INSUFMEMORY=-13, HEAD__PROBLEMATIC=-12, - HEAD__NOTPRESENT=-11, HEAD__OTHER=1, HEAD_CACHE_CONTROL, HEAD_CONNECTION, HEAD_DATE, +enum HeaderId { HEAD__NOT_COMPUTE=-14, HEAD__INSUF_MEMORY=-13, HEAD__PROBLEMATIC=-12, + HEAD__NOT_PRESENT=-11, HEAD__OTHER=1, HEAD_CACHE_CONTROL, HEAD_CONNECTION, HEAD_DATE, HEAD_PRAGMA, HEAD_TRAILER, HEAD_COOKIE, HEAD_SET_COOKIE, HEAD_TRANSFER_ENCODING, HEAD_UPGRADE, HEAD_VIA, HEAD_WARNING, HEAD_ACCEPT, HEAD_ACCEPT_CHARSET, HEAD_ACCEPT_ENCODING, HEAD_ACCEPT_LANGUAGE, HEAD_AUTHORIZATION, HEAD_EXPECT, HEAD_FROM, HEAD_HOST, HEAD_IF_MATCH, @@ -131,7 +131,7 @@ enum Infraction INF_BAD_CHUNK_SIZE, INF_BAD_PHRASE, INF_BAD_URI, - INF_BAD_PORT, + INF_UNUSED, INF_URI_NEED_NORM, INF_URI_PERCENT_NORMAL, INF_URI_PERCENT_ASCII, @@ -251,7 +251,7 @@ enum EventSid EVENT_BAD_HEADER, EVENT_CHUNK_OPTIONS, EVENT_URI_BAD_FORMAT, - EVENT_URI_BAD_PORT, + EVENT_UNUSED, EVENT_BROKEN_CHUNK, EVENT_CHUNK_WHITESPACE, EVENT_HEAD_NAME_WHITESPACE, diff --git a/src/service_inspectors/nhttp_inspect/nhttp_field.cc b/src/service_inspectors/nhttp_inspect/nhttp_field.cc index cdc9bab50..420eba626 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_field.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_field.cc @@ -28,12 +28,12 @@ using namespace NHttpEnums; -const Field Field::FIELD_NULL { STAT_NOSOURCE }; +const Field Field::FIELD_NULL { STAT_NO_SOURCE }; #ifdef REG_TEST void Field::print(FILE* output, const char* name) const { - if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE) || (length == STAT_NOSOURCE)) + if ((length == STAT_NOT_PRESENT) || (length == STAT_NOT_COMPUTE) || (length == STAT_NO_SOURCE)) { return; } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_field.h b/src/service_inspectors/nhttp_inspect/nhttp_field.h index 86650c6ef..b50c75cbf 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_field.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_field.h @@ -32,7 +32,7 @@ class Field { public: - int32_t length = NHttpEnums::STAT_NOTCOMPUTE; + int32_t length = NHttpEnums::STAT_NOT_COMPUTE; const uint8_t* start = nullptr; static const Field FIELD_NULL; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc index a811f6e4c..6c5bbbd7d 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc @@ -78,13 +78,13 @@ void NHttpFlowData::half_reset(SourceId source_id) { assert((source_id == SRC_CLIENT) || (source_id == SRC_SERVER)); - version_id[source_id] = VERS__NOTPRESENT; - data_length[source_id] = STAT_NOTPRESENT; - body_octets[source_id] = STAT_NOTPRESENT; + version_id[source_id] = VERS__NOT_PRESENT; + data_length[source_id] = STAT_NOT_PRESENT; + body_octets[source_id] = STAT_NOT_PRESENT; section_size_target[source_id] = 0; section_size_max[source_id] = 0; - file_depth_remaining[source_id] = STAT_NOTPRESENT; - detect_depth_remaining[source_id] = STAT_NOTPRESENT; + file_depth_remaining[source_id] = STAT_NOT_PRESENT; + detect_depth_remaining[source_id] = STAT_NOT_PRESENT; compression[source_id] = CMP_NONE; if (compress_stream[source_id] != nullptr) { @@ -100,7 +100,7 @@ void NHttpFlowData::half_reset(SourceId source_id) if (source_id == SRC_CLIENT) { - method_id = METH__NOTPRESENT; + method_id = METH__NOT_PRESENT; if (mime_state != nullptr) { delete mime_state; @@ -109,7 +109,7 @@ void NHttpFlowData::half_reset(SourceId source_id) } else { - status_code_num = STAT_NOTPRESENT; + status_code_num = STAT_NOT_PRESENT; } } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h index bd18bacc4..56321108c 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h @@ -78,34 +78,35 @@ private: uint32_t num_good_chunks[2] = { 0, 0 }; // *** StreamSplitter => Inspector (facts about the most recent message section) - NHttpEnums::SectionType section_type[2] = { NHttpEnums::SEC__NOTCOMPUTE, - NHttpEnums::SEC__NOTCOMPUTE }; + NHttpEnums::SectionType section_type[2] = { NHttpEnums::SEC__NOT_COMPUTE, + NHttpEnums::SEC__NOT_COMPUTE }; bool tcp_close[2] = { false, false }; NHttpInfractions infractions[2]; NHttpEventGen events[2]; - int32_t num_head_lines[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT }; + int32_t num_head_lines[2] = { NHttpEnums::STAT_NOT_PRESENT, NHttpEnums::STAT_NOT_PRESENT }; // *** Inspector => StreamSplitter (facts about the message section that is coming next) NHttpEnums::SectionType type_expected[2] = { NHttpEnums::SEC_REQUEST, NHttpEnums::SEC_STATUS }; // length of the data from Content-Length field - int64_t data_length[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT }; + int64_t data_length[2] = { NHttpEnums::STAT_NOT_PRESENT, NHttpEnums::STAT_NOT_PRESENT }; uint32_t section_size_target[2] = { 0, 0 }; uint32_t section_size_max[2] = { 0, 0 }; NHttpEnums::CompressId compression[2] = { NHttpEnums::CMP_NONE, NHttpEnums::CMP_NONE }; z_stream* compress_stream[2] = { nullptr, nullptr }; // *** Inspector's internal data about the current message - NHttpEnums::VersionId version_id[2] = { NHttpEnums::VERS__NOTPRESENT, - NHttpEnums::VERS__NOTPRESENT }; - NHttpEnums::MethodId method_id = NHttpEnums::METH__NOTPRESENT; - int32_t status_code_num = NHttpEnums::STAT_NOTPRESENT; - int64_t file_depth_remaining[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT }; - int64_t detect_depth_remaining[2] = { NHttpEnums::STAT_NOTPRESENT, - NHttpEnums::STAT_NOTPRESENT }; + NHttpEnums::VersionId version_id[2] = { NHttpEnums::VERS__NOT_PRESENT, + NHttpEnums::VERS__NOT_PRESENT }; + NHttpEnums::MethodId method_id = NHttpEnums::METH__NOT_PRESENT; + int32_t status_code_num = NHttpEnums::STAT_NOT_PRESENT; + int64_t file_depth_remaining[2] = { NHttpEnums::STAT_NOT_PRESENT, + NHttpEnums::STAT_NOT_PRESENT }; + int64_t detect_depth_remaining[2] = { NHttpEnums::STAT_NOT_PRESENT, + NHttpEnums::STAT_NOT_PRESENT }; MimeSession* mime_state = nullptr; // SRC_CLIENT only // number of user data octets seen so far (regular body or chunks) - int64_t body_octets[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT }; + int64_t body_octets[2] = { NHttpEnums::STAT_NOT_PRESENT, NHttpEnums::STAT_NOT_PRESENT }; // Transaction management including pipelining // FIXIT-L pipeline deserves to be its own class diff --git a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc index 7c24d3821..c3d7ea35e 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc @@ -63,7 +63,7 @@ void HeaderNormalizer::normalize(const HeaderId head_id, const int count, Scratc NHttpInfractions& infractions, NHttpEventGen& events, const HeaderId header_name_id[], const Field header_value[], const int32_t num_headers, Field& result_field) const { - if (result_field.length != STAT_NOTCOMPUTE) + if (result_field.length != STAT_NOT_COMPUTE) { return; } @@ -109,7 +109,7 @@ void HeaderNormalizer::normalize(const HeaderId head_id, const int count, Scratc uint8_t* const scratch = scratch_pad.request(2*buffer_length); if (scratch == nullptr) { - result_field.length = STAT_INSUFMEMORY; + result_field.length = STAT_INSUF_MEMORY; return; } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc b/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc index 7bcbdd123..e53d3edd5 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc @@ -63,29 +63,26 @@ bool NHttpInspect::get_buf(InspectionBuffer::Type ibt, Packet*, InspectionBuffer switch (ibt) { case InspectionBuffer::IBT_KEY: - return get_buf(NHTTP_BUFFER_URI, 0, nullptr, b); + return get_buf(NHTTP_BUFFER_URI, 0, 0, nullptr, b); case InspectionBuffer::IBT_HEADER: - return get_buf(NHTTP_BUFFER_HEADER, 0, nullptr, b); + if (get_latest_is() == IS_TRAILER) + return get_buf(NHTTP_BUFFER_TRAILER, 0, 0, nullptr, b); + else + return get_buf(NHTTP_BUFFER_HEADER, 0, 0, nullptr, b); case InspectionBuffer::IBT_BODY: - return get_buf(NHTTP_BUFFER_CLIENT_BODY, 0, nullptr, b); + return get_buf(NHTTP_BUFFER_CLIENT_BODY, 0, 0, nullptr, b); default: return false; } } -bool NHttpInspect::get_buf(unsigned id, Packet*, InspectionBuffer& b) +SO_PUBLIC bool NHttpInspect::get_buf(unsigned id, uint64_t sub_id, uint64_t form, Packet*, + InspectionBuffer& b) { - return get_buf(id, 0, nullptr, b); -} - -SO_PUBLIC bool NHttpInspect::get_buf(unsigned id, unsigned sub_id, Packet*, InspectionBuffer& b) -{ - // FIXIT-L some day we should add support for accessing the request headers, trailers, and - // version from the response side of the transaction. if (latest_section == nullptr) return false; - const Field& buffer = latest_section->get_classic_buffer(id, sub_id); + const Field& buffer = latest_section->get_classic_buffer(id, sub_id, form); if (buffer.length <= 0) return false; @@ -95,6 +92,29 @@ SO_PUBLIC bool NHttpInspect::get_buf(unsigned id, unsigned sub_id, Packet*, Insp return true; } +bool NHttpInspect::get_fp_buf(InspectionBuffer::Type ibt, Packet*, InspectionBuffer& b) +{ + // Fast pattern buffers only supplied at specific times + switch (ibt) + { + case InspectionBuffer::IBT_KEY: + if ((get_latest_is() != IS_DETECTION) || (get_latest_src() != SRC_CLIENT)) + return false; + break; + case InspectionBuffer::IBT_HEADER: + if ((get_latest_is() != IS_DETECTION) && (get_latest_is() != IS_TRAILER)) + return false; + break; + case InspectionBuffer::IBT_BODY: + if ((get_latest_is() != IS_DETECTION) && (get_latest_is() != IS_BODY)) + return false; + break; + default: + return false; + } + return get_buf(ibt, nullptr, b); +} + const Field& NHttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const flow, SourceId source_id, bool buf_owner) const { diff --git a/src/service_inspectors/nhttp_inspect/nhttp_inspect.h b/src/service_inspectors/nhttp_inspect/nhttp_inspect.h index 93bd6a2eb..8d6e4f407 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_inspect.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_inspect.h @@ -42,13 +42,12 @@ public: NHttpInspect(NHttpParaList params_); bool get_buf(InspectionBuffer::Type ibt, Packet*, InspectionBuffer& b) override; - bool get_buf(unsigned id, Packet*, InspectionBuffer& b) override; - bool get_buf(unsigned id, unsigned sub_id, Packet*, InspectionBuffer& b); + bool get_buf(unsigned id, uint64_t sub_id, uint64_t form, Packet*, InspectionBuffer& b); + bool get_fp_buf(InspectionBuffer::Type ibt, Packet*, InspectionBuffer& b) override; bool configure(SnortConfig*) override { return true; } void show(SnortConfig*) override { LogMessage("NHttpInspect\n"); } void eval(Packet*) override { } void clear(Packet* p) override; - void clear(NHttpFlowData* session_data, NHttpEnums::SourceId source_id); void tinit() override { } void tterm() override { } NHttpStreamSplitter* get_splitter(bool is_client_to_server) override @@ -63,6 +62,9 @@ private: const Field& process(const uint8_t* data, const uint16_t dsize, Flow* const flow, NHttpEnums::SourceId source_id_, bool buf_owner) const; + void clear(NHttpFlowData* session_data, NHttpEnums::SourceId source_id); + static NHttpEnums::SourceId get_latest_src() { return (latest_section != nullptr) ? + latest_section->get_source_id() : NHttpEnums::SRC__NOT_COMPUTE; } static THREAD_LOCAL NHttpMsgSection* latest_section; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_module.h b/src/service_inspectors/nhttp_inspect/nhttp_module.h index 3c498e7a6..3978c1964 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_module.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_module.h @@ -33,10 +33,11 @@ public: long request_depth; long response_depth; bool unzip; - +#ifdef REG_TEST bool test_input; bool test_output; long print_amount; +#endif }; class NHttpModule : public Module diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc index e9dc3fbba..2aaf13036 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc @@ -37,7 +37,8 @@ NHttpMsgBody::NHttpMsgBody(const uint8_t* buffer, const uint16_t buf_size, NHttpFlowData* session_data_, SourceId source_id_, bool buf_owner, Flow* flow_, const NHttpParaList* params_) : NHttpMsgSection(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_), - body_octets(session_data->body_octets[source_id]) + body_octets(session_data->body_octets[source_id]), + detection_section((body_octets == 0) && (session_data->detect_depth_remaining[source_id] > 0)) { transaction->set_body(this); } @@ -100,7 +101,7 @@ void NHttpMsgBody::do_file_processing() NHttpMsgRequest* request = transaction->get_request(); if (request != nullptr) { - const Field& tranaction_uri = request->get_uri_norm_legacy(); + const Field& tranaction_uri = request->get_uri_norm_classic(); if (tranaction_uri.length > 0) { file_flows->set_file_name(tranaction_uri.start, tranaction_uri.length); @@ -133,8 +134,8 @@ void NHttpMsgBody::do_file_processing() void NHttpMsgBody::print_body_section(FILE* output) { detect_data.print(output, "Detect data"); - get_classic_buffer(NHTTP_BUFFER_CLIENT_BODY, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_CLIENT_BODY-1]); + get_classic_buffer(NHTTP_BUFFER_CLIENT_BODY, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_CLIENT_BODY-1]); if (g_file_data.len > 0) { Field(g_file_data.len, g_file_data.data).print(output, "file_data"); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h index 1b23c5f15..53b110eab 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h @@ -33,7 +33,7 @@ public: void analyze() override; const Field& get_detect_buf() const override { return detect_data; } NHttpEnums::InspectSection get_inspection_section() const override - { return NHttpEnums::IS_BODY; } + { return detection_section ? NHttpEnums::IS_DETECTION : NHttpEnums::IS_BODY; } protected: NHttpMsgBody(const uint8_t* buffer, const uint16_t buf_size, NHttpFlowData* session_data_, @@ -44,6 +44,7 @@ protected: int64_t body_octets; Field detect_data; Field file_data; + const bool detection_section; #ifdef REG_TEST void print_body_section(FILE* output); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body_chunk.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_body_chunk.cc index cb636524f..d9a014965 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_body_chunk.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_body_chunk.cc @@ -48,7 +48,7 @@ void NHttpMsgBodyChunk::update_flow() session_data->infractions[source_id] = infractions; session_data->events[source_id] = events; } - session_data->section_type[source_id] = SEC__NOTCOMPUTE; + session_data->section_type[source_id] = SEC__NOT_COMPUTE; } #ifdef REG_TEST diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body_cl.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_body_cl.cc index 63989a348..d35694499 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_body_cl.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_body_cl.cc @@ -49,7 +49,7 @@ void NHttpMsgBodyCl::update_flow() SEC_STATUS; session_data->half_reset(source_id); } - session_data->section_type[source_id] = SEC__NOTCOMPUTE; + session_data->section_type[source_id] = SEC__NOT_COMPUTE; } #ifdef REG_TEST diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body_old.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_body_old.cc index 4e989572b..caf9b3fca 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_body_old.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_body_old.cc @@ -39,7 +39,7 @@ void NHttpMsgBodyOld::update_flow() update_depth(); session_data->infractions[source_id] = infractions; session_data->events[source_id] = events; - session_data->section_type[source_id] = SEC__NOTCOMPUTE; + session_data->section_type[source_id] = SEC__NOT_COMPUTE; } #ifdef REG_TEST diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc index cf8c7463b..d4da16fb3 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc @@ -182,7 +182,7 @@ void NHttpMsgHeadShared::derive_header_name_id(int index) if ((lower_name = scratch_pad.request(length)) == nullptr) { infractions += INF_NO_SCRATCH; - header_name_id[index] = HEAD__INSUFMEMORY; + header_name_id[index] = HEAD__INSUF_MEMORY; return; } for (int32_t k=0; k < length; k++) @@ -231,7 +231,7 @@ const Field& NHttpMsgHeadShared::get_header_value_norm(HeaderId header_id) void NHttpMsgHeadShared::print_headers(FILE* output) { char title_buf[100]; - if (num_headers != STAT_NOSOURCE) + if (num_headers != STAT_NO_SOURCE) fprintf(output, "Number of headers: %d\n", num_headers); for (int j=0; j < num_headers; j++) { @@ -240,7 +240,7 @@ void NHttpMsgHeadShared::print_headers(FILE* output) } for (int k=1; k <= HEAD__MAX_VALUE-1; k++) { - if (get_header_value_norm((HeaderId)k).length != STAT_NOSOURCE) + if (get_header_value_norm((HeaderId)k).length != STAT_NO_SOURCE) { snprintf(title_buf, sizeof(title_buf), "Normalized header %d", k); get_header_value_norm((HeaderId)k).print(output, title_buf); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h index cb4b363eb..05d38873d 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h @@ -77,7 +77,7 @@ protected: // All of these are indexed by the relative position of the header field in the message static const int MAX_HEADERS = 200; // I'm an arbitrary number. FIXIT-L static const int MAX_HEADER_LENGTH = 4096; // Based on max cookie size of some browsers - int32_t num_headers = NHttpEnums::STAT_NOTCOMPUTE; + int32_t num_headers = NHttpEnums::STAT_NOT_COMPUTE; Field* header_line = nullptr; Field* header_name = nullptr; NHttpEnums::HeaderId* header_name_id = nullptr; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc index 5b87294a7..1c2e5b175 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc @@ -42,7 +42,7 @@ NHttpMsgHeader::NHttpMsgHeader(const uint8_t* buffer, const uint16_t buf_size, void NHttpMsgHeader::update_flow() { - session_data->section_type[source_id] = SEC__NOTCOMPUTE; + session_data->section_type[source_id] = SEC__NOT_COMPUTE; // FIXIT-L put this test here for now. May want to integrate into the following code and // do more careful checks for inappropriate Content-Length. @@ -52,6 +52,13 @@ void NHttpMsgHeader::update_flow() // The following logic to determine body type is by no means the last word on this topic. // FIXIT-H need to distinguish methods such as POST that should have a body from those that // should not. + if (tcp_close) + { + session_data->type_expected[source_id] = SEC_ABORT; + session_data->half_reset(source_id); + return; + } + if ((source_id == SRC_SERVER) && ((status_code_num <= 199) || (status_code_num == 204) || (status_code_num == 304))) { @@ -138,6 +145,12 @@ void NHttpMsgHeader::prepare_body() const int64_t& depth = (source_id == SRC_CLIENT) ? params->request_depth : params->response_depth; session_data->detect_depth_remaining[source_id] = (depth != -1) ? depth : INT64_MAX; + if (session_data->detect_depth_remaining[source_id] > 0) + { + // Depth must be positive because first body section must actually go to detection in order + // to be the detection section + detection_section = false; + } setup_file_processing(); setup_decompression(); update_depth(); @@ -202,7 +215,7 @@ void NHttpMsgHeader::setup_decompression() session_data->compress_stream[source_id]->zfree = Z_NULL; session_data->compress_stream[source_id]->next_in = Z_NULL; session_data->compress_stream[source_id]->avail_in = 0; - const int window_bits = (compression == CMP_GZIP) ? GZIP_WINDOWBITS : DEFLATE_WINDOWBITS; + const int window_bits = (compression == CMP_GZIP) ? GZIP_WINDOW_BITS : DEFLATE_WINDOW_BITS; if (inflateInit2(session_data->compress_stream[source_id], window_bits) != Z_OK) { session_data->compression[source_id] = CMP_NONE; @@ -216,14 +229,14 @@ void NHttpMsgHeader::print_section(FILE* output) { NHttpMsgSection::print_message_title(output, "header"); NHttpMsgHeadShared::print_headers(output); - get_classic_buffer(NHTTP_BUFFER_COOKIE, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_COOKIE-1]); - get_classic_buffer(NHTTP_BUFFER_HEADER, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_HEADER-1]); - get_classic_buffer(NHTTP_BUFFER_RAW_COOKIE, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_RAW_COOKIE-1]); - get_classic_buffer(NHTTP_BUFFER_RAW_HEADER, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_RAW_HEADER-1]); + get_classic_buffer(NHTTP_BUFFER_COOKIE, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_COOKIE-1]); + get_classic_buffer(NHTTP_BUFFER_HEADER, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_HEADER-1]); + get_classic_buffer(NHTTP_BUFFER_RAW_COOKIE, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_RAW_COOKIE-1]); + get_classic_buffer(NHTTP_BUFFER_RAW_HEADER, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_RAW_HEADER-1]); NHttpMsgSection::print_message_wrapup(output); } #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_header.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_header.h index 42741d911..d0338745c 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_header.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_header.h @@ -36,7 +36,7 @@ public: NHttpEnums::SourceId source_id_, bool buf_owner, Flow* flow_, const NHttpParaList* params_); NHttpEnums::InspectSection get_inspection_section() const override - { return NHttpEnums::IS_HEADER; } + { return detection_section ? NHttpEnums::IS_DETECTION : NHttpEnums::IS_NONE; } void update_flow() override; private: // Dummy configurations to support MIME processing @@ -47,6 +47,8 @@ private: void setup_file_processing(); void setup_decompression(); + bool detection_section = true; + #ifdef REG_TEST void print_section(FILE* output) override; #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc index 52f7669b0..7bae74940 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc @@ -94,11 +94,11 @@ const Field& NHttpMsgRequest::get_uri() return Field::FIELD_NULL; } -const Field& NHttpMsgRequest::get_uri_norm_legacy() +const Field& NHttpMsgRequest::get_uri_norm_classic() { if (uri != nullptr) { - return uri->get_norm_legacy(); + return uri->get_norm_classic(); } return Field::FIELD_NULL; } @@ -162,7 +162,7 @@ void NHttpMsgRequest::update_flow() session_data->infractions[source_id].reset(); session_data->events[source_id].reset(); } - session_data->section_type[source_id] = SEC__NOTCOMPUTE; + session_data->section_type[source_id] = SEC__NOT_COMPUTE; } #ifdef REG_TEST @@ -177,14 +177,10 @@ void NHttpMsgRequest::print_section(FILE* output) uri->get_uri().print(output, "URI"); fprintf(output, "URI Type: %d\n", uri->get_uri_type()); uri->get_scheme().print(output, "Scheme"); - if (uri->get_scheme_id() != SCH__NOSOURCE) - fprintf(output, "Scheme Id: %d\n", uri->get_scheme_id()); uri->get_authority().print(output, "Authority"); uri->get_host().print(output, "Host Name"); uri->get_norm_host().print(output, "Normalized Host Name"); uri->get_port().print(output, "Port"); - if (uri->get_port_value() != STAT_NOSOURCE) - fprintf(output, "Port Value: %d\n", uri->get_port_value()); uri->get_abs_path().print(output, "Absolute Path"); uri->get_path().print(output, "Path"); uri->get_norm_path().print(output, "Normalized Path"); @@ -193,14 +189,16 @@ void NHttpMsgRequest::print_section(FILE* output) uri->get_fragment().print(output, "Fragment"); uri->get_norm_fragment().print(output, "Normalized Fragment"); } - get_classic_buffer(NHTTP_BUFFER_METHOD, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_METHOD-1]); - get_classic_buffer(NHTTP_BUFFER_RAW_URI, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_RAW_URI-1]); - get_classic_buffer(NHTTP_BUFFER_URI, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_URI-1]); - get_classic_buffer(NHTTP_BUFFER_VERSION, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_VERSION-1]); + get_classic_buffer(NHTTP_BUFFER_METHOD, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_METHOD-1]); + get_classic_buffer(NHTTP_BUFFER_RAW_URI, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_RAW_URI-1]); + get_classic_buffer(NHTTP_BUFFER_URI, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_URI-1]); + get_classic_buffer(NHTTP_BUFFER_VERSION, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_VERSION-1]); + get_classic_buffer(NHTTP_BUFFER_RAW_REQUEST, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_RAW_REQUEST-1]); NHttpMsgSection::print_message_wrapup(output); } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h index d83c6831d..ab70f8322 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h @@ -41,7 +41,7 @@ public: void update_flow() override; const Field& get_method() { return method; } const Field& get_uri(); - const Field& get_uri_norm_legacy(); + const Field& get_uri_norm_classic(); NHttpUri* get_nhttp_uri() { return uri; } #ifdef REG_TEST diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc index 16332690c..cb96cc00f 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc @@ -47,8 +47,8 @@ NHttpMsgSection::NHttpMsgSection(const uint8_t* buffer, const uint16_t buf_size, infractions(session_data->infractions[source_id]), events(session_data->events[source_id]), version_id(session_data->version_id[source_id]), - method_id((source_id == SRC_CLIENT) ? session_data->method_id : METH__NOTPRESENT), - status_code_num((source_id == SRC_SERVER) ? session_data->status_code_num : STAT_NOTPRESENT), + method_id((source_id == SRC_CLIENT) ? session_data->method_id : METH__NOT_PRESENT), + status_code_num((source_id == SRC_SERVER) ? session_data->status_code_num : STAT_NOT_PRESENT), delete_msg_on_destruct(buf_owner) { } @@ -79,8 +79,11 @@ void NHttpMsgSection::update_depth() const } } -const Field& NHttpMsgSection::get_classic_buffer(unsigned id, unsigned sub_id) +const Field& NHttpMsgSection::get_classic_buffer(unsigned id, uint64_t sub_id, uint64_t form) { + // Only use with buffers that support the request option + const SourceId buffer_side = (form & FORM_REQUEST) ? SRC_CLIENT : source_id; + switch (id) { case NHTTP_BUFFER_CLIENT_BODY: @@ -96,10 +99,10 @@ const Field& NHttpMsgSection::get_classic_buffer(unsigned id, unsigned sub_id) // Currently "normalization" is aggregation of multiple cookies. That is correct for raw // cookies and all there is for normalized cookies. { - NHttpMsgHeader* header = transaction->get_header(source_id); + NHttpMsgHeader* header = transaction->get_header(buffer_side); if (header == nullptr) return Field::FIELD_NULL; - HeaderId cookie_head = (source_id == SRC_CLIENT) ? HEAD_COOKIE : HEAD_SET_COOKIE; + HeaderId cookie_head = (buffer_side == SRC_CLIENT) ? HEAD_COOKIE : HEAD_SET_COOKIE; return header->get_header_value_norm(cookie_head); } case NHTTP_BUFFER_HEADER: @@ -107,8 +110,8 @@ const Field& NHttpMsgSection::get_classic_buffer(unsigned id, unsigned sub_id) { // FIXIT-L Someday want to be able to return field name or raw field value NHttpMsgHeadShared* const header = (id == NHTTP_BUFFER_HEADER) ? - (NHttpMsgHeadShared*)transaction->get_header(source_id) : - (NHttpMsgHeadShared*)transaction->get_trailer(source_id); + (NHttpMsgHeadShared*)transaction->get_header(buffer_side) : + (NHttpMsgHeadShared*)transaction->get_trailer(buffer_side); if (header == nullptr) return Field::FIELD_NULL; if (sub_id == 0) @@ -122,7 +125,7 @@ const Field& NHttpMsgSection::get_classic_buffer(unsigned id, unsigned sub_id) } case NHTTP_BUFFER_RAW_HEADER: { - NHttpMsgHeader* header = transaction->get_header(source_id); + NHttpMsgHeader* header = transaction->get_header(buffer_side); return (header != nullptr) ? header->get_headers() : Field::FIELD_NULL; } case NHTTP_BUFFER_STAT_CODE: @@ -143,7 +146,7 @@ const Field& NHttpMsgSection::get_classic_buffer(unsigned id, unsigned sub_id) if (request == nullptr) return Field::FIELD_NULL; if (sub_id == 0) - return raw ? request->get_uri() : request->get_uri_norm_legacy(); + return raw ? request->get_uri() : request->get_uri_norm_classic(); NHttpUri* const uri = request->get_nhttp_uri(); if (uri == nullptr) return Field::FIELD_NULL; @@ -167,13 +170,23 @@ const Field& NHttpMsgSection::get_classic_buffer(unsigned id, unsigned sub_id) } case NHTTP_BUFFER_VERSION: { - NHttpMsgStart* start = (source_id == SRC_CLIENT) ? + NHttpMsgStart* start = (buffer_side == SRC_CLIENT) ? (NHttpMsgStart*)transaction->get_request() : (NHttpMsgStart*)transaction->get_status(); return (start != nullptr) ? start->get_version() : Field::FIELD_NULL; } + case NHTTP_BUFFER_RAW_REQUEST: + { + NHttpMsgRequest* request = transaction->get_request(); + return (request != nullptr) ? request->get_detect_buf() : Field::FIELD_NULL; + } + case NHTTP_BUFFER_RAW_STATUS: + { + NHttpMsgStatus* status = transaction->get_status(); + return (status != nullptr) ? status->get_detect_buf() : Field::FIELD_NULL; + } case NHTTP_BUFFER_RAW_TRAILER: { - NHttpMsgTrailer* trailer = transaction->get_trailer(source_id); + NHttpMsgTrailer* trailer = transaction->get_trailer(buffer_side); return (trailer != nullptr) ? trailer->get_headers() : Field::FIELD_NULL; } default: diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h index 4a8058dcc..f07e1cc77 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h @@ -38,7 +38,9 @@ class NHttpMsgSection { public: virtual ~NHttpMsgSection() { if (delete_msg_on_destruct) delete[] msg_text.start; } - virtual NHttpEnums::InspectSection get_inspection_section() const = 0; + virtual NHttpEnums::InspectSection get_inspection_section() const + { return NHttpEnums::IS_NONE; } + NHttpEnums::SourceId get_source_id() { return source_id; } // Minimum necessary processing for every message virtual void analyze() = 0; @@ -46,7 +48,7 @@ public: // Manages the splitter and communication between message sections virtual void update_flow() = 0; - const Field& get_classic_buffer(unsigned id, unsigned sub_id); + const Field& get_classic_buffer(unsigned id, uint64_t sub_id, uint64_t form); // Provide buffer to be sent to detection virtual const Field& get_detect_buf() const { return msg_text; } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_start.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_start.h index e36a9bdbe..35230ff9e 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_start.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_start.h @@ -32,8 +32,6 @@ class NHttpMsgStart : public NHttpMsgSection public: void analyze() override; const Field& get_version() const { return version; } - NHttpEnums::InspectSection get_inspection_section() const override - { return NHttpEnums::IS_START; } protected: NHttpMsgStart(const uint8_t* buffer, const uint16_t buf_size, NHttpFlowData* session_data_, @@ -41,7 +39,7 @@ protected: : NHttpMsgSection(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_) { } virtual void parse_start_line() = 0; - virtual void gen_events() {}; + virtual void gen_events() = 0; void derive_version_id(); Field start_line; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc index 357f72f54..85d1572ae 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc @@ -161,7 +161,7 @@ void NHttpMsgStatus::update_flow() session_data->infractions[source_id].reset(); session_data->events[source_id].reset(); } - session_data->section_type[source_id] = SEC__NOTCOMPUTE; + session_data->section_type[source_id] = SEC__NOT_COMPUTE; } #ifdef REG_TEST @@ -171,12 +171,14 @@ void NHttpMsgStatus::print_section(FILE* output) fprintf(output, "Version Id: %d\n", version_id); fprintf(output, "Status Code Num: %d\n", status_code_num); reason_phrase.print(output, "Reason Phrase"); - get_classic_buffer(NHTTP_BUFFER_STAT_CODE, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_STAT_CODE-1]); - get_classic_buffer(NHTTP_BUFFER_STAT_MSG, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_STAT_MSG-1]); - get_classic_buffer(NHTTP_BUFFER_VERSION, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_VERSION-1]); + get_classic_buffer(NHTTP_BUFFER_STAT_CODE, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_STAT_CODE-1]); + get_classic_buffer(NHTTP_BUFFER_STAT_MSG, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_STAT_MSG-1]); + get_classic_buffer(NHTTP_BUFFER_VERSION, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_VERSION-1]); + get_classic_buffer(NHTTP_BUFFER_RAW_STATUS, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_RAW_STATUS-1]); NHttpMsgSection::print_message_wrapup(output); } #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc index 7059ee247..e1b643a0d 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc @@ -42,7 +42,7 @@ void NHttpMsgTrailer::update_flow() session_data->type_expected[source_id] = (source_id == SRC_CLIENT) ? SEC_REQUEST : SEC_STATUS; session_data->half_reset(source_id); - session_data->section_type[source_id] = SEC__NOTCOMPUTE; + session_data->section_type[source_id] = SEC__NOT_COMPUTE; } #ifdef REG_TEST @@ -50,10 +50,10 @@ void NHttpMsgTrailer::print_section(FILE* output) { NHttpMsgSection::print_message_title(output, "trailer"); NHttpMsgHeadShared::print_headers(output); - get_classic_buffer(NHTTP_BUFFER_TRAILER, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_TRAILER-1]); - get_classic_buffer(NHTTP_BUFFER_RAW_TRAILER, 0).print(output, - NHttpApi::legacy_buffers[NHTTP_BUFFER_RAW_TRAILER-1]); + get_classic_buffer(NHTTP_BUFFER_TRAILER, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_TRAILER-1]); + get_classic_buffer(NHTTP_BUFFER_RAW_TRAILER, 0, 0).print(output, + NHttpApi::classic_buffers[NHTTP_BUFFER_RAW_TRAILER-1]); NHttpMsgSection::print_message_wrapup(output); } #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc index 6d1026363..98f6cb397 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc @@ -35,6 +35,7 @@ int32_t norm_to_lower(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf { for (int32_t k=0; k < in_length; k++) { + // FIXIT-P tolower() might perform better but must be sure cannot be pulled in out_buf[k] = ((in_buf[k] < 'A') || (in_buf[k] > 'Z')) ? in_buf[k] : in_buf[k] - ('A' - 'a'); } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc index 21e3109f0..cae7b71d7 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc @@ -363,12 +363,7 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total, copied = len; - // FIXIT-M (b042cf28c49) - // assert(total <= MAX_OCTETS) was broke after changes in stream - // to accommodate asymmetric TCP connections. - // See "FIXIT-M (b042cf28c49)" in "src/tcp/tcp_session.c". - if (total > MAX_OCTETS) - total = MAX_OCTETS; + assert(total <= MAX_OCTETS); NHttpFlowData* session_data = (NHttpFlowData*)flow->get_application_data( NHttpFlowData::nhttp_flow_id); @@ -409,7 +404,7 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total, } #endif - if (session_data->section_type[source_id] == SEC__NOTCOMPUTE) + if (session_data->section_type[source_id] == SEC__NOT_COMPUTE) { // FIXIT-M In theory this check should not be necessary return nullptr; } @@ -427,7 +422,7 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total, #endif if (flags & PKT_PDU_TAIL) { - session_data->section_type[source_id] = SEC__NOTCOMPUTE; + session_data->section_type[source_id] = SEC__NOT_COMPUTE; // When we are skipping through a message body beyond flow depth this is the end of // the line. Here we do the message section's normal job of updating the flow for the @@ -488,12 +483,26 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total, session_data->section_offset[source_id] = 0; // Buffers are reset to nullptr without delete[] because NHttpMsgSection holds the pointer - // and is responsible - if (send_to_detection.length > 0) + // and is responsible. + // The detection section of a message is the first body section, unless there is no body + // section in which case it is the headers. The detection section is always returned to the + // framework and forwarded to detection even if it is empty. Other body sections and the + // trailer section are only forwarded if nonempty. The start line section and header + // sections other than the detection section are never forwarded. + if (((send_to_detection.length > 0) && (NHttpInspect::get_latest_is() != IS_NONE)) || + ((send_to_detection.length == 0) && (NHttpInspect::get_latest_is() == IS_DETECTION))) { - nhttp_buf.data = send_to_detection.start; - nhttp_buf.length = send_to_detection.length; - assert((nhttp_buf.length <= MAX_OCTETS) && (nhttp_buf.length != 0)); + // FIXIT-M kludge until we work out issues with returning an empty buffer + if (send_to_detection.length > 0) + { + nhttp_buf.data = send_to_detection.start; + nhttp_buf.length = send_to_detection.length; + } + else + { + nhttp_buf.data = (const uint8_t*)""; + nhttp_buf.length = 1; + } buffer = nullptr; #ifdef REG_TEST if (NHttpTestManager::use_test_output()) @@ -537,7 +546,7 @@ bool NHttpStreamSplitter::finish(Flow* flow) // If there is leftover data for which we returned PAF_SEARCH and never flushed, we need to set // up to process because it is about to go to reassemble(). But we don't support partial start // lines. - if ((session_data->section_type[source_id] == SEC__NOTCOMPUTE) && + if ((session_data->section_type[source_id] == SEC__NOT_COMPUTE) && (session_data->cutter[source_id] != nullptr) && (session_data->cutter[source_id]->get_octets_seen() > 0)) { @@ -558,7 +567,7 @@ bool NHttpStreamSplitter::finish(Flow* flow) } // If there is no more data to process we need to wrap up file processing right now - if ((session_data->section_type[source_id] == SEC__NOTCOMPUTE) && + if ((session_data->section_type[source_id] == SEC__NOT_COMPUTE) && (session_data->file_depth_remaining[source_id] > 0) && (session_data->cutter[source_id] != nullptr) && (session_data->cutter[source_id]->get_octets_seen() == 0)) @@ -568,7 +577,7 @@ bool NHttpStreamSplitter::finish(Flow* flow) FileFlows* file_flows = FileFlows::get_file_flows(flow); file_flows->file_process(nullptr, 0, SNORT_FILE_END, false); } - else + else if (session_data->mime_state != nullptr) { session_data->mime_state->process_mime_data(flow, nullptr, 0, true, SNORT_FILE_END); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_tables.cc b/src/service_inspectors/nhttp_inspect/nhttp_tables.cc index 839a45a7b..0bbb4959c 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_tables.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_tables.cc @@ -91,16 +91,6 @@ const StrCode NHttpMsgRequest::method_list[] = { 0, nullptr } }; -const StrCode NHttpUri::scheme_list[] = -{ - { SCH_HTTP, "http" }, - { SCH_HTTPS, "https" }, - { SCH_FTP, "ftp" }, - { SCH_GOPHER, "gopher" }, - { SCH_FILE, "file" }, - { 0, nullptr } -}; - SO_PUBLIC const StrCode NHttpMsgHeadShared::header_list[] = { { HEAD_CACHE_CONTROL, "cache-control" }, @@ -330,7 +320,7 @@ const RuleMap NHttpModule::nhttp_events[] = { EVENT_BAD_HEADER, "Format error in HTTP header" }, { EVENT_CHUNK_OPTIONS, "Chunk header options present" }, { EVENT_URI_BAD_FORMAT, "URI badly formatted" }, - { EVENT_URI_BAD_PORT, "URI bad port number" }, + { EVENT_UNUSED, "Unused" }, { EVENT_BROKEN_CHUNK, "HTTP chunk misformatted" }, { EVENT_CHUNK_WHITESPACE, "White space following chunk length" }, { EVENT_GZIP_OVERRUN, "Excessive gzip compression" }, diff --git a/src/service_inspectors/nhttp_inspect/nhttp_test_input.cc b/src/service_inspectors/nhttp_inspect/nhttp_test_input.cc index 1164f74b1..2ad39d151 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_test_input.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_test_input.cc @@ -67,7 +67,7 @@ void NHttpTestInput::reset() // Read from the test data file and present to StreamSplitter. In the process we may need to skip // comments, execute simple commands, and handle escape sequences. The best way to understand this -// function is to read the comments at the top of the file of test cases. +// function is to read dev_notes.txt. void NHttpTestInput::scan(uint8_t*& data, uint32_t& length, SourceId source_id, uint64_t seq_num) { bool skip_to_break = false; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri.cc b/src/service_inspectors/nhttp_inspect/nhttp_uri.cc index 7f0011b9b..927b39af0 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_uri.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri.cc @@ -30,35 +30,30 @@ using namespace NHttpEnums; void NHttpUri::parse_uri() { - if (uri_type != URI__NOTCOMPUTE) - { - return; - } - // Four basic types of HTTP URI // "*" means request does not apply to any specific resource if ((uri.length == 1) && (uri.start[0] == '*')) { uri_type = URI_ASTERISK; - scheme.length = STAT_NOTPRESENT; - authority.length = STAT_NOTPRESENT; - abs_path.length = STAT_NOTPRESENT; + scheme.length = STAT_NOT_PRESENT; + authority.length = STAT_NOT_PRESENT; + abs_path.length = STAT_NOT_PRESENT; } // CONNECT method uses an authority else if (method_id == METH_CONNECT) { uri_type = URI_AUTHORITY; - scheme.length = STAT_NOTPRESENT; + scheme.length = STAT_NOT_PRESENT; authority.length = uri.length; authority.start = uri.start; - abs_path.length = STAT_NOTPRESENT; + abs_path.length = STAT_NOT_PRESENT; } // Absolute path is a path but no scheme or authority else if (uri.start[0] == '/') { uri_type = URI_ABSPATH; - scheme.length = STAT_NOTPRESENT; - authority.length = STAT_NOTPRESENT; + scheme.length = STAT_NOT_PRESENT; + authority.length = STAT_NOT_PRESENT; abs_path.length = uri.length; abs_path.start = uri.start; } @@ -92,200 +87,86 @@ void NHttpUri::parse_uri() } } -SchemeId NHttpUri::get_scheme_id() -{ - if (scheme_id != SCH__NOTCOMPUTE) - { - return scheme_id; - } - if (get_scheme().length <= 0) - { - scheme_id = SCH__NOSOURCE; - return scheme_id; - } - - // Normalize scheme name to lower case for matching purposes - uint8_t* lower_scheme; - if ((lower_scheme = scratch_pad.request(scheme.length)) == nullptr) - { - infractions += INF_NO_SCRATCH; - scheme_id = SCH__INSUFMEMORY; - return scheme_id; - } - norm_to_lower(scheme.start, scheme.length, lower_scheme, infractions, events); - scheme_id = (SchemeId)str_to_code(lower_scheme, scheme.length, scheme_list); - return scheme_id; -} - -const Field& NHttpUri::get_norm_host() -{ - if (host_norm.length != STAT_NOTCOMPUTE) - { - return host_norm; - } - if (get_host().length < 0) - { - host_norm.length = STAT_NOSOURCE; - return host_norm; - } - UriNormalizer::normalize(host, host_norm, false, scratch_pad, infractions, events); - return host_norm; -} - -const Field& NHttpUri::get_norm_path() -{ - if (path_norm.length != STAT_NOTCOMPUTE) - { - return path_norm; - } - if (get_path().length < 0) - { - path_norm.length = STAT_NOSOURCE; - return path_norm; - } - UriNormalizer::normalize(path, path_norm, true, scratch_pad, infractions, events); - return path_norm; -} - -const Field& NHttpUri::get_norm_query() -{ - if (query_norm.length != STAT_NOTCOMPUTE) - { - return query_norm; - } - if (get_query().length < 0) - { - query_norm.length = STAT_NOSOURCE; - return query_norm; - } - UriNormalizer::normalize(query, query_norm, false, scratch_pad, infractions, events); - return query_norm; -} - -const Field& NHttpUri::get_norm_fragment() -{ - if (fragment_norm.length != STAT_NOTCOMPUTE) - { - return fragment_norm; - } - if (get_fragment().length < 0) - { - fragment_norm.length = STAT_NOSOURCE; - return fragment_norm; - } - UriNormalizer::normalize(fragment, fragment_norm, false, scratch_pad, infractions, events); - return fragment_norm; -} - -int32_t NHttpUri::get_port_value() -{ - if (port_value != STAT_NOTCOMPUTE) - { - return port_value; - } - if (get_port().length <= 0) - { - port_value = STAT_NOSOURCE; - return port_value; - } - port_value = 0; - for (int k = 0; k < port.length; k++) - { - port_value = port_value * 10 + (port.start[k] - '0'); - if ((port.start[k] < '0') || (port.start[k] > '9') || (port_value > MAX_PORT_VALUE)) - { - infractions += INF_BAD_PORT; - events.create_event(EVENT_URI_BAD_PORT); - port_value = STAT_PROBLEMATIC; - break; - } - } - return port_value; -} - void NHttpUri::parse_authority() { - if (host.length != STAT_NOTCOMPUTE) + if (authority.length <= 0) { - return; - } - if (get_authority().length <= 0) - { - host.length = STAT_NOSOURCE; - port.length = STAT_NOSOURCE; + host.length = STAT_NO_SOURCE; + port.length = STAT_NO_SOURCE; return; } host.start = authority.start; for (host.length = 0; (authority.start[host.length] != ':') && (host.length < - authority.length); host.length++) - ; + authority.length); host.length++); if (host.length < authority.length) { port.length = authority.length - host.length - 1; port.start = authority.start + host.length + 1; } else - port.length = STAT_NOTPRESENT; + port.length = STAT_NOT_PRESENT; } void NHttpUri::parse_abs_path() { - if (path.length != STAT_NOTCOMPUTE) - return; - if (get_abs_path().length <= 0) + // path?query#fragment + // path is always present in absolute path, while query and fragment are optional + if (abs_path.length <= 0) { - path.length = STAT_NOSOURCE; - query.length = STAT_NOSOURCE; - fragment.length = STAT_NOSOURCE; + path.length = STAT_NO_SOURCE; + query.length = STAT_NO_SOURCE; + fragment.length = STAT_NO_SOURCE; return; } path.start = abs_path.start; for (path.length = 0; (abs_path.start[path.length] != '?') && (abs_path.start[path.length] != - '#') && (path.length < abs_path.length); path.length++) - ; + '#') && (path.length < abs_path.length); path.length++); if (path.length == abs_path.length) { - query.length = STAT_NOTPRESENT; - fragment.length = STAT_NOTPRESENT; + query.length = STAT_NOT_PRESENT; + fragment.length = STAT_NOT_PRESENT; return; } if (abs_path.start[path.length] == '?') { query.start = abs_path.start + path.length + 1; for (query.length = 0; (query.start[query.length] != '#') && (query.length < - abs_path.length - path.length - 1); query.length++) - ; + abs_path.length - path.length - 1); query.length++); fragment.start = query.start + query.length + 1; fragment.length = abs_path.length - path.length - 1 - query.length - 1; } else { - query.length = STAT_NOTPRESENT; + query.length = STAT_NOT_PRESENT; fragment.start = abs_path.start + path.length + 1; fragment.length = abs_path.length - path.length - 1; } } -// Glue normalized URI fields back together -const Field& NHttpUri::get_norm_legacy() +void NHttpUri::normalize() { - if (legacy_norm.length != STAT_NOTCOMPUTE) - { - return legacy_norm; - } - if (get_path().length >= 0) + // FIXIT-P generating the normalized URI components directly into the normalized classic buffer + // would save a lot of memory and some copying. + + // Divide the URI up into its six components: scheme, host, port, path, query, and fragment + parse_uri(); + parse_authority(); + parse_abs_path(); + + // Normalize the individual components. We don't do anything with scheme or port. + if (path.length >= 0) { UriNormalizer::normalize(path, path_norm, true, scratch_pad, infractions, events); } - if (get_host().length >= 0) + if (host.length >= 0) { UriNormalizer::normalize(host, host_norm, false, scratch_pad, infractions, events); } - if (get_query().length >= 0) + if (query.length >= 0) { UriNormalizer::normalize(query, query_norm, false, scratch_pad, infractions, events); } - if (get_fragment().length >= 0) + if (fragment.length >= 0) { UriNormalizer::normalize(fragment, fragment_norm, false, scratch_pad, infractions, events); @@ -294,9 +175,9 @@ const Field& NHttpUri::get_norm_legacy() // We can reuse the raw URI for the normalized URI if no normalization is required if (!(infractions & INF_URI_NEED_NORM)) { - legacy_norm.start = uri.start; - legacy_norm.length = uri.length; - return legacy_norm; + classic_norm.start = uri.start; + classic_norm.length = uri.length; + return; } // Glue normalized URI pieces back together @@ -350,11 +231,10 @@ const Field& NHttpUri::get_norm_legacy() } assert(total_length == current - scratch); scratch_pad.commit(current - scratch); - legacy_norm.start = scratch; - legacy_norm.length = current - scratch; + classic_norm.start = scratch; + classic_norm.length = current - scratch; } else - legacy_norm.length = STAT_INSUFMEMORY; - return legacy_norm; + classic_norm.length = STAT_INSUF_MEMORY; } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri.h b/src/service_inspectors/nhttp_inspect/nhttp_uri.h index 2ce68db0d..4333f3c58 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_uri.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri.h @@ -37,30 +37,25 @@ public: NHttpUri(const uint8_t* start, int32_t length, NHttpEnums::MethodId method, NHttpInfractions& infractions_, NHttpEventGen& events_) : uri(length, start), method_id(method), infractions(infractions_), events(events_), - scratch_pad(2*length+200) { } + scratch_pad(2*length+200) { normalize(); } const Field& get_uri() const { return uri; } - NHttpEnums::UriType get_uri_type() { parse_uri(); return uri_type; } - const Field& get_scheme() { parse_uri(); return scheme; } - const Field& get_authority() { parse_uri(); return authority; } - const Field& get_host() { parse_authority(); return host; } - const Field& get_port() { parse_authority(); return port; } - const Field& get_abs_path() { parse_uri(); return abs_path; } - const Field& get_path() { parse_abs_path(); return path; } - const Field& get_query() { parse_abs_path(); return query; } - const Field& get_fragment() { parse_abs_path(); return fragment; } + NHttpEnums::UriType get_uri_type() { return uri_type; } + const Field& get_scheme() { return scheme; } + const Field& get_authority() { return authority; } + const Field& get_host() { return host; } + const Field& get_port() { return port; } + const Field& get_abs_path() { return abs_path; } + const Field& get_path() { return path; } + const Field& get_query() { return query; } + const Field& get_fragment() { return fragment; } - NHttpEnums::SchemeId get_scheme_id(); - const Field& get_norm_host(); - int32_t get_port_value(); - const Field& get_norm_path(); - const Field& get_norm_query(); - const Field& get_norm_fragment(); - const Field& get_norm_legacy(); + const Field& get_norm_host() { return host_norm; } + const Field& get_norm_path() { return path_norm; } + const Field& get_norm_query() { return query_norm; } + const Field& get_norm_fragment() { return fragment_norm; } + const Field& get_norm_classic() { return classic_norm; } private: - static const StrCode scheme_list[]; - static const int MAX_PORT_VALUE = 65535; - const Field uri; const NHttpEnums::MethodId method_id; NHttpInfractions& infractions; @@ -75,19 +70,20 @@ private: Field query; Field fragment; - NHttpEnums::UriType uri_type = NHttpEnums::URI__NOTCOMPUTE; - NHttpEnums::SchemeId scheme_id = NHttpEnums::SCH__NOTCOMPUTE; + NHttpEnums::UriType uri_type = NHttpEnums::URI__NOT_COMPUTE; Field host_norm; - int32_t port_value = NHttpEnums::STAT_NOTCOMPUTE; Field path_norm; Field query_norm; Field fragment_norm; - Field legacy_norm; + Field classic_norm; + void normalize(); void parse_uri(); void parse_authority(); void parse_abs_path(); + // FIXIT-P there is an enormous memory waste that this is always allocated. It is only needed + // when the URI requires normalization. Most of the time the raw URI is already in normal form. ScratchPad scratch_pad; }; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc index 4b08fe3a9..6d7514b92 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc @@ -28,10 +28,6 @@ using namespace NHttpEnums; void UriNormalizer::normalize(const Field& input, Field& result, bool do_path, ScratchPad& scratch_pad, NHttpInfractions& infractions, NHttpEventGen& events) { - if (result.length != STAT_NOTCOMPUTE) - return; - assert (input.length >= 0); - // Almost all HTTP requests are honest and rarely need expensive normalization processing. We // do a quick scan for red flags and only perform normalization if something comes up. // Otherwise we set the normalized field to point at the raw value. @@ -50,7 +46,7 @@ void UriNormalizer::normalize(const Field& input, Field& result, bool do_path, uint8_t* const scratch = scratch_pad.request(2 * buffer_length); if (scratch == nullptr) { - result.length = STAT_INSUFMEMORY; + result.length = STAT_INSUF_MEMORY; return; } uint8_t* const front_half = scratch;