From: Tom Peters Date: Mon, 22 Dec 2014 21:58:17 +0000 (-0500) Subject: infractions onject X-Git-Tag: 3.0.0-233~1078^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=966de98cfacea42ef3bbd78db5c985f23691fe2f;p=thirdparty%2Fsnort3.git infractions onject --- diff --git a/src/service_inspectors/nhttp_inspect/CMakeLists.txt b/src/service_inspectors/nhttp_inspect/CMakeLists.txt index af789dbd8..88f8b2d89 100644 --- a/src/service_inspectors/nhttp_inspect/CMakeLists.txt +++ b/src/service_inspectors/nhttp_inspect/CMakeLists.txt @@ -51,6 +51,7 @@ set (FILE_LIST nhttp_stream_splitter.h nhttp_splitter.cc nhttp_splitter.h + nhttp_infractions.h ) if (STATIC_INSPECTORS) diff --git a/src/service_inspectors/nhttp_inspect/Makefile.am b/src/service_inspectors/nhttp_inspect/Makefile.am index dea586e56..f066f980f 100644 --- a/src/service_inspectors/nhttp_inspect/Makefile.am +++ b/src/service_inspectors/nhttp_inspect/Makefile.am @@ -28,7 +28,8 @@ nhttp_splitter.cc nhttp_splitter.h \ nhttp_scratch_pad.h \ nhttp_enum.h \ nhttp_test_manager.cc nhttp_test_manager.h \ -nhttp_field.cc nhttp_field.h +nhttp_field.cc nhttp_field.h \ +nhttp_infractions.h if STATIC_INSPECTORS noinst_LIBRARIES = libnhttp_inspect.a diff --git a/src/service_inspectors/nhttp_inspect/nhttp_enum.h b/src/service_inspectors/nhttp_inspect/nhttp_enum.h index 47b381b08..157fbb093 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_enum.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_enum.h @@ -83,13 +83,38 @@ typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2, // All the infractions we might find while parsing and analyzing a message typedef enum { - INF_TRUNCATED=0x1, INF_HEADTOOLONG=0x2, INF_BADREQLINE=0x4, INF_BADSTATLINE=0x8, INF_TOOMANYHEADERS=0x10, - INF_BADHEADER=0x20, INF_BADSTATCODE=0x40, INF_UNKNOWNVERSION=0x80, INF_BADVERSION=0x100, INF_NOSCRATCH=0x200, - INF_BADHEADERREPS=0x400, INF_BADHEADERDATA=0x800, INF_BADCHUNKSIZE=0x2000, - INF_BADPHRASE=0x4000, INF_BADURI=0x8000, INF_BADPORT=0x10000, INF_URINEEDNORM=0x20000, INF_URIPERCENTNORMAL=0x40000, - INF_URIPERCENTASCII=0x80000, INF_URIPERCENTUTF8=0x100000, INF_URIPERCENTUCODE=0x200000, INF_URIPERCENTOTHER=0x400000, - INF_URIBADCHAR=0x800000, INF_URI8BITCHAR=0x1000000, INF_URIMULTISLASH=0x2000000, INF_URIBACKSLASH=0x4000000, - INF_URISLASHDOT=0x8000000, INF_URISLASHDOTDOT=0x10000000, INF_URIROOTTRAV=0x20000000 } Infraction; + INF_TRUNCATED = 0, + INF_HEADTOOLONG, + INF_BADREQLINE, + INF_BADSTATLINE, + INF_TOOMANYHEADERS, + INF_BADHEADER, + INF_BADSTATCODE, + INF_UNKNOWNVERSION, + INF_BADVERSION, + INF_NOSCRATCH, + INF_BADHEADERREPS, + INF_BADHEADERDATA, + INF_FIXIT_L_UNUSED, + INF_BADCHUNKSIZE, + INF_BADPHRASE, + INF_BADURI, + INF_BADPORT, + INF_URINEEDNORM, + INF_URIPERCENTNORMAL, + INF_URIPERCENTASCII, + INF_URIPERCENTUTF8, + INF_URIPERCENTUCODE, + INF_URIPERCENTOTHER, + INF_URIBADCHAR, + INF_URI8BITCHAR, + INF_URIMULTISLASH, + INF_URIBACKSLASH, + INF_URISLASHDOT, + INF_URISLASHDOTDOT, + INF_URIROOTTRAV, + INF_TOOMUCHLEADINGWS, +} Infraction; // Formats for output from a header normalization function typedef enum { NORM_NULL, NORM_FIELD, NORM_INT64, NORM_ENUM64, NORM_ENUM64LIST } NormFormat; @@ -102,13 +127,13 @@ typedef enum { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSC TRANSCODE_DEFLATE } Transcoding; typedef enum -{ +{ // FIXIT-L limit 63 before code changes required EVENT_ASCII = 1, EVENT_DOUBLE_DECODE, EVENT_U_ENCODE, EVENT_BARE_BYTE, EVENT_OBSOLETE_1, - EVENT_UTF_8, + EVENT_UTF_8, EVENT_IIS_UNICODE, EVENT_MULTI_SLASH, EVENT_IIS_BACKSLASH, diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h index 8c53e054f..5b91f9ef7 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h @@ -25,6 +25,7 @@ #include "stream/stream_api.h" #include "nhttp_splitter.h" +#include "nhttp_infractions.h" class NHttpTransaction; @@ -63,14 +64,14 @@ private: int32_t chunk_buffer_length[2] = { 0, 0 }; bool chunk_buffer_owned[2] = { true, true }; // never actually false and not checked, just here to simplify code bool zero_chunk[2] = { false, false }; - uint64_t chunk_infractions[2] = { 0, 0 }; + NHttpInfractions chunk_infractions[2]; // StreamSplitter => Inspector (facts about the most recent message section) // 0 element refers to client request, 1 element refers to server response NHttpEnums::SectionType section_type[2] = { NHttpEnums::SEC__NOTCOMPUTE, NHttpEnums::SEC__NOTCOMPUTE }; uint32_t num_excess[2] = { 0, 0 }; bool tcp_close[2] = { false, false }; - uint64_t infractions[2] = { 0, 0 }; + NHttpInfractions infractions[2]; uint32_t unused_octets_visible[2] = { 0, 0 }; uint32_t header_octets_visible[2] = { 0, 0 }; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc index 4bf87d1a1..f8eaab15a 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc @@ -54,8 +54,9 @@ int32_t HeaderNormalizer::derive_header_content(const uint8_t *value, int32_t le } // This method normalizes the header field value for headId. -int32_t HeaderNormalizer::normalize(const HeaderId head_id, const int count, ScratchPad &scratch_pad, uint64_t &infractions, - const HeaderId header_name_id[], const Field header_value[], const int32_t num_headers, Field &result_field) const { +int32_t HeaderNormalizer::normalize(const HeaderId head_id, const int count, ScratchPad &scratch_pad, + NHttpInfractions &infractions, const HeaderId header_name_id[], const Field header_value[], + const int32_t num_headers, Field &result_field) const { if (result_field.length != STAT_NOTCOMPUTE) { return result_field.length; } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h index 6ccb1625b..708c1b080 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h @@ -23,6 +23,7 @@ #include "nhttp_scratch_pad.h" #include "nhttp_field.h" +#include "nhttp_infractions.h" //------------------------------------------------------------------------- // HeaderNormalizer class @@ -39,19 +40,19 @@ public: constexpr HeaderNormalizer( NHttpEnums::NormFormat _format, bool _concatenate_repeats, - int32_t (*f1)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*), + int32_t (*f1)(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*), const void *f1_arg, - int32_t (*f2)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*), + int32_t (*f2)(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*), const void *f2_arg, - int32_t (*f3)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*), + int32_t (*f3)(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*), const void *f3_arg) : format(_format), concatenate_repeats(_concatenate_repeats), normalizer { f1, f2, f3 }, norm_arg { f1_arg, f2_arg, f3_arg }, num_normalizers((f1 != nullptr) + (f1 != nullptr)*(f2 != nullptr) + (f1 != nullptr)*(f2 != nullptr)*(f3 != nullptr)) {}; - int32_t normalize(const NHttpEnums::HeaderId head_id, const int count, ScratchPad &scratch_pad, uint64_t &infractions, - const NHttpEnums::HeaderId header_name_id[], const Field header_value[], const int32_t num_headers, - Field &result_field) const; + int32_t normalize(const NHttpEnums::HeaderId head_id, const int count, ScratchPad &scratch_pad, + NHttpInfractions &infractions, const NHttpEnums::HeaderId header_name_id[], const Field header_value[], + const int32_t num_headers, Field &result_field) const; NHttpEnums::NormFormat get_format() const { return format; }; private: @@ -59,7 +60,7 @@ private: const NHttpEnums::NormFormat format; const bool concatenate_repeats; - int32_t (* const normalizer[3])(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*); + int32_t (* const normalizer[3])(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*); const void * norm_arg[3]; const int num_normalizers; }; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_infractions.h b/src/service_inspectors/nhttp_inspect/nhttp_infractions.h new file mode 100644 index 000000000..9722b2677 --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_infractions.h @@ -0,0 +1,76 @@ +/* +** Copyright (C) 2014 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. +*/ +// nhttp_scratch_pad.h author Tom Peters + +#ifndef NHTTP_INFRACTIONS_H +#define NHTTP_INFRACTIONS_H + +#include + +//------------------------------------------------------------------------- +// Infractions class +//------------------------------------------------------------------------- + +class NHttpInfractions { +public: + NHttpInfractions() {}; + NHttpInfractions(int inf) : infractions(1 << inf) { assert((inf >= 0) && (inf < 64)); }; + bool found_new(int inf) { + assert((inf >= 0) && (inf < 64)); + const bool ret_val = ((1 << inf) & infractions & ~previous_infractions) != 0; + previous_infractions |= (1 << inf) & infractions; + return ret_val; }; + bool none_found() const { return infractions == 0; }; + NHttpInfractions& operator+=(const NHttpInfractions& rhs) { infractions |= rhs.infractions; + previous_infractions |= rhs.previous_infractions; return *this; }; + friend NHttpInfractions operator+(NHttpInfractions lhs, const NHttpInfractions& rhs) { lhs += rhs; return lhs; }; + friend bool operator&&(const NHttpInfractions& lhs, const NHttpInfractions& rhs) { + return (lhs.infractions & rhs.infractions) != 0; }; + + // The following two methods are for convenience of debug and test output only! + // The 64-bit implementation will not be big enough forever and this interface cannot be all over the code. + uint64_t get_raw() const { return infractions; }; + uint64_t get_raw_prev() const { return previous_infractions; }; +private: + NHttpInfractions(uint64_t infractions_, uint64_t previous_infractions_) : infractions(infractions_), + previous_infractions(previous_infractions_) {}; + + uint64_t infractions = 0; + uint64_t previous_infractions = 0; +}; + +#endif + + + + + + + + + + + + + + + + + + diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc index 5f3612421..d17b18f08 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc @@ -43,7 +43,7 @@ void NHttpMsgBody::analyze() { data.start = msg_text.start; data.length = msg_text.length; - if (tcp_close && (body_octets < data_length)) infractions |= INF_TRUNCATED; + if (tcp_close && (body_octets < data_length)) infractions += INF_TRUNCATED; } void NHttpMsgBody::gen_events() { 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 fc91b9534..9b29c6a32 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc @@ -55,7 +55,7 @@ void NHttpMsgHeadShared::parse_header_block() { } } if (bytes_used < msg_text.length) { - infractions |= INF_TOOMANYHEADERS; + infractions += INF_TOOMANYHEADERS; } } @@ -99,7 +99,7 @@ void NHttpMsgHeadShared::parse_header_lines() { header_value[k].length = header_line[k].length - colon - 1; } else { - infractions |= INF_BADHEADER; + infractions += INF_BADHEADER; } } } @@ -108,7 +108,7 @@ void NHttpMsgHeadShared::derive_header_name_id(int index) { // Normalize header field name to lower case for matching purposes uint8_t *lower_name; if ((lower_name = scratch_pad.request(header_name[index].length)) == nullptr) { - infractions |= INF_NOSCRATCH; + infractions += INF_NOSCRATCH; header_name_id[index] = HEAD__INSUFMEMORY; return; } @@ -123,7 +123,7 @@ const Field& NHttpMsgHeadShared::get_header_value_norm(NHttpEnums::HeaderId head } void NHttpMsgHeadShared::gen_events() { - if (infractions & INF_TOOMANYHEADERS) create_event(EVENT_MAX_HEADERS); + if (infractions && INF_TOOMANYHEADERS) create_event(EVENT_MAX_HEADERS); } void NHttpMsgHeadShared::print_headers(FILE *output) { diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc index eb937e32f..786849d5f 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc @@ -60,7 +60,7 @@ void NHttpMsgHeader::update_flow() { session_data->type_expected[source_id] = SEC_CLOSED; session_data->half_reset(source_id); } - else if (infractions & disaster_mask) { + else if (infractions && disaster_mask) { session_data->type_expected[source_id] = SEC_ABORT; session_data->half_reset(source_id); } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc index 31bd71b8b..e4e8c707e 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc @@ -48,7 +48,7 @@ void NHttpMsgRequest::parse_start_line() { // HTTP/X.Y if (start_line.start[start_line.length-9] != ' ') { // space before "HTTP" missing or in wrong place - infractions |= INF_BADREQLINE; + infractions += INF_BADREQLINE; return; } @@ -58,7 +58,7 @@ void NHttpMsgRequest::parse_start_line() { } if (space >= start_line.length-9) { // leading space or no space - infractions |= INF_BADREQLINE; + infractions += INF_BADREQLINE; return; } @@ -97,18 +97,18 @@ void NHttpMsgRequest::gen_events() { if (method_id == METH__OTHER) create_event(EVENT_UNKNOWN_METHOD); // URI character encoding events - if (uri && (uri->get_uri_infractions() & INF_URIPERCENTASCII)) create_event(EVENT_ASCII); - if (uri && (uri->get_uri_infractions() & INF_URIPERCENTUCODE)) create_event(EVENT_U_ENCODE); - if (uri && (uri->get_uri_infractions() & INF_URI8BITCHAR)) create_event(EVENT_BARE_BYTE); - if (uri && (uri->get_uri_infractions() & INF_URIPERCENTUTF8)) create_event(EVENT_UTF_8); - if (uri && (uri->get_uri_infractions() & INF_URIBADCHAR)) create_event(EVENT_NON_RFC_CHAR); + if (uri && (uri->get_uri_infractions() && INF_URIPERCENTASCII)) create_event(EVENT_ASCII); + if (uri && (uri->get_uri_infractions() && INF_URIPERCENTUCODE)) create_event(EVENT_U_ENCODE); + if (uri && (uri->get_uri_infractions() && INF_URI8BITCHAR)) create_event(EVENT_BARE_BYTE); + if (uri && (uri->get_uri_infractions() && INF_URIPERCENTUTF8)) create_event(EVENT_UTF_8); + if (uri && (uri->get_uri_infractions() && INF_URIBADCHAR)) create_event(EVENT_NON_RFC_CHAR); // URI path events - if (uri && (uri->get_path_infractions() & INF_URIMULTISLASH)) create_event(EVENT_MULTI_SLASH); - if (uri && (uri->get_path_infractions() & INF_URIBACKSLASH)) create_event(EVENT_IIS_BACKSLASH); - if (uri && (uri->get_path_infractions() & INF_URISLASHDOT)) create_event(EVENT_SELF_DIR_TRAV); - if (uri && (uri->get_path_infractions() & INF_URISLASHDOTDOT)) create_event(EVENT_DIR_TRAV); - if (uri && (uri->get_path_infractions() & INF_URIROOTTRAV)) create_event(EVENT_WEBROOT_DIR); + if (uri && (uri->get_path_infractions() && INF_URIMULTISLASH)) create_event(EVENT_MULTI_SLASH); + if (uri && (uri->get_path_infractions() && INF_URIBACKSLASH)) create_event(EVENT_IIS_BACKSLASH); + if (uri && (uri->get_path_infractions() && INF_URISLASHDOT)) create_event(EVENT_SELF_DIR_TRAV); + if (uri && (uri->get_path_infractions() && INF_URISLASHDOTDOT)) create_event(EVENT_DIR_TRAV); + if (uri && (uri->get_path_infractions() && INF_URIROOTTRAV)) create_event(EVENT_WEBROOT_DIR); } @@ -135,8 +135,10 @@ void NHttpMsgRequest::print_section(FILE *output) { uri->get_norm_fragment().print(output, "Normalized Fragment"); fprintf(output, "URI infractions: overall %" PRIx64 ", format %" PRIx64 ", scheme %" PRIx64 ", host %" PRIx64 ", port %" PRIx64 ", path %" PRIx64 ", query %" PRIx64 ", fragment %" PRIx64 "\n", - uri->get_uri_infractions(), uri->get_format_infractions(), uri->get_scheme_infractions(), uri->get_host_infractions(), - uri->get_port_infractions(), uri->get_path_infractions(), uri->get_query_infractions(), uri->get_fragment_infractions()); + uri->get_uri_infractions().get_raw(), uri->get_format_infractions().get_raw(), + uri->get_scheme_infractions().get_raw(), uri->get_host_infractions().get_raw(), + uri->get_port_infractions().get_raw(), uri->get_path_infractions().get_raw(), + uri->get_query_infractions().get_raw(), uri->get_fragment_infractions().get_raw()); } NHttpMsgSection::print_message_wrapup(output); } @@ -149,7 +151,7 @@ void NHttpMsgRequest::update_flow() { session_data->type_expected[source_id] = SEC_CLOSED; session_data->half_reset(source_id); } - else if (infractions & disaster_mask) { + else if (infractions && disaster_mask) { session_data->type_expected[source_id] = SEC_ABORT; session_data->half_reset(source_id); } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc index 0596f3c4b..d61ace4de 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc @@ -55,8 +55,8 @@ void NHttpMsgSection::print_message_title(FILE *output, const char *title) const } void NHttpMsgSection::print_message_wrapup(FILE *output) const { - fprintf(output, "Infractions: %" PRIx64 ", Events: %" PRIx64 ", TCP Close: %s\n", infractions, events_generated, - tcp_close ? "True" : "False"); + fprintf(output, "Infractions: %" PRIx64 ", Events: %" PRIx64 ", TCP Close: %s\n", infractions.get_raw(), + events_generated, tcp_close ? "True" : "False"); fprintf(output, "Interface to old clients. http_mask = %x.\n", http_mask); for (int i=0; i < HTTP_BUFFER_MAX; i++) { if ((1 << i) & http_mask) Field(http_buffer[i].length, http_buffer[i].buf).print(output, http_buffer_name[i]); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h index 28f0d17a0..4b3579276 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h @@ -25,6 +25,7 @@ #include "nhttp_field.h" #include "nhttp_flow_data.h" #include "nhttp_transaction.h" +#include "nhttp_infractions.h" //------------------------------------------------------------------------- // NHttpMsgSection class @@ -66,7 +67,7 @@ protected: const bool tcp_close; ScratchPad scratch_pad; - uint64_t infractions; + NHttpInfractions infractions; uint64_t events_generated = 0; NHttpEnums::VersionId version_id; NHttpEnums::MethodId method_id; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc index 19862226f..70da47c0c 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc @@ -43,13 +43,13 @@ void NHttpMsgStart::derive_version_id() { } if (version.length != 8) { version_id = VERS__PROBLEMATIC; - infractions |= INF_BADVERSION; + infractions += INF_BADVERSION; return; } if (memcmp(version.start, "HTTP/", 5) || (version.start[6] != '.')) { version_id = VERS__PROBLEMATIC; - infractions |= INF_BADVERSION; + infractions += INF_BADVERSION; } else if ((version.start[5] == '1') && (version.start[7] == '1')) { version_id = VERS_1_1; @@ -63,11 +63,11 @@ void NHttpMsgStart::derive_version_id() { else if ((version.start[5] >= '0') && (version.start[5] <= '9') && (version.start[7] >= '0') && (version.start[7] <= '9')) { version_id = VERS__OTHER; - infractions |= INF_UNKNOWNVERSION; + infractions += INF_UNKNOWNVERSION; } else { version_id = VERS__PROBLEMATIC; - infractions |= INF_BADVERSION; + infractions += INF_BADVERSION; } } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc index add1f18ce..de4c2d372 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc @@ -51,7 +51,7 @@ void NHttpMsgStatus::parse_start_line() { // Eventually we may need to cater to certain format errors, but for now exact match or treat as error. // HTTP/X.Y### if ((start_line.length < 13) || (start_line.start[8] != ' ') || (start_line.start[12] != ' ')) { - infractions |= INF_BADSTATLINE; + infractions += INF_BADSTATLINE; return; } version.start = start_line.start; @@ -63,7 +63,7 @@ void NHttpMsgStatus::parse_start_line() { for (int32_t k = 0; k < reason_phrase.length; k++) { if ((reason_phrase.start[k] <= 31) || (reason_phrase.start[k] >= 127)) { // Illegal character in reason phrase - infractions |= INF_BADPHRASE; + infractions += INF_BADPHRASE; break; } } @@ -82,13 +82,13 @@ void NHttpMsgStatus::derive_status_code_num() { if ((status_code.start[0] < '0') || (status_code.start[0] > '9') || (status_code.start[1] < '0') || (status_code.start[1] > '9') || (status_code.start[2] < '0') || (status_code.start[2] > '9')) { - infractions |= INF_BADSTATCODE; + infractions += INF_BADSTATCODE; status_code_num = STAT_PROBLEMATIC; return; } status_code_num = (status_code.start[0] - '0') * 100 + (status_code.start[1] - '0') * 10 + (status_code.start[2] - '0'); if ((status_code_num < 100) || (status_code_num > 599)) { - infractions |= INF_BADSTATCODE; + infractions += INF_BADSTATCODE; } } @@ -110,7 +110,7 @@ void NHttpMsgStatus::update_flow() { session_data->type_expected[source_id] = SEC_CLOSED; session_data->half_reset(source_id); } - else if (infractions & disaster_mask) { + else if (infractions && disaster_mask) { session_data->type_expected[source_id] = SEC_ABORT; session_data->half_reset(source_id); } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc index b3524bd13..7149aff71 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc @@ -30,7 +30,7 @@ using namespace NHttpEnums; // Collection of stock normalization functions. This will probably grow throughout the life of the software. New functions must follow the standard signature. // The void* at the end is for any special configuration data the function requires. -int32_t norm_decimal_integer(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, uint64_t& infractions, const void *) { +int32_t norm_decimal_integer(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, NHttpInfractions& infractions, const void *) { // Limited to 18 decimal digits, not including leading zeros, to fit comfortably into int64_t int64_t total = 0; int non_leading_zeros = 0; @@ -38,11 +38,11 @@ int32_t norm_decimal_integer(const uint8_t* in_buf, int32_t in_length, uint8_t* int value = in_buf[k] - '0'; if (non_leading_zeros || (value != 0)) non_leading_zeros++; if (non_leading_zeros > 18) { - infractions |= INF_BADHEADERDATA; + infractions += INF_BADHEADERDATA; return STAT_PROBLEMATIC; } if ((value < 0) || (value > 9)) { - infractions |= INF_BADHEADERDATA; + infractions += INF_BADHEADERDATA; return STAT_PROBLEMATIC; } total = total*10 + value; @@ -52,7 +52,7 @@ int32_t norm_decimal_integer(const uint8_t* in_buf, int32_t in_length, uint8_t* } -int32_t norm_to_lower(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t&, const void *) { +int32_t norm_to_lower(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, NHttpInfractions&, const void *) { for (int32_t k=0; k < in_length; k++) { out_buf[k] = ((in_buf[k] < 'A') || (in_buf[k] > 'Z')) ? in_buf[k] : in_buf[k] - ('A' - 'a'); } @@ -60,12 +60,14 @@ int32_t norm_to_lower(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf } -int32_t norm_str_code(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t&, const void *table) { +int32_t norm_str_code(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, NHttpInfractions&, + const void* table) { ((int64_t*)out_buf)[0] = str_to_code(in_buf, in_length, (const StrCode*)table); return sizeof(int64_t); } -int32_t norm_seq_str_code(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t&, const void *table) { +int32_t norm_seq_str_code(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, NHttpInfractions&, + const void *table) { int32_t num_codes = 0; const uint8_t* start = in_buf; while (true) { @@ -80,7 +82,7 @@ int32_t norm_seq_str_code(const uint8_t* in_buf, int32_t in_length, uint8_t *out } // Remove all space and tab characters (known as LWS or linear white space in the RFC) -int32_t norm_remove_lws(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t&, const void *) { +int32_t norm_remove_lws(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, NHttpInfractions&, const void*) { int32_t length = 0; for (int32_t k = 0; k < in_length; k++) { if ((in_buf[k] != ' ') && (in_buf[k] != '\t')) out_buf[length++] = in_buf[k]; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_normalizers.h b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.h index dc7e97b7d..f8b2f7829 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_normalizers.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.h @@ -21,11 +21,13 @@ #ifndef NHTTP_NORMALIZERS_H #define NHTTP_NORMALIZERS_H -int32_t norm_decimal_integer(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used); -int32_t norm_to_lower(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used); -int32_t norm_str_code(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*); -int32_t norm_seq_str_code(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*); -int32_t norm_remove_lws(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used); +#include "nhttp_infractions.h" + +int32_t norm_decimal_integer(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used); +int32_t norm_to_lower(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used); +int32_t norm_str_code(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*); +int32_t norm_seq_str_code(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void*); +int32_t norm_remove_lws(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used); #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_splitter.cc b/src/service_inspectors/nhttp_inspect/nhttp_splitter.cc index 0612f1276..8098b8697 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_splitter.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_splitter.cc @@ -32,7 +32,8 @@ ScanResult NHttpStartSplitter::split(const uint8_t* buffer, uint32_t length) { num_crlf++; continue; } - else { // FIXIT-M there needs to be an event for this + else { + infractions += INF_TOOMUCHLEADINGWS; return SCAN_ABORT; } } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_splitter.h b/src/service_inspectors/nhttp_inspect/nhttp_splitter.h index 4e2a8b74b..4bc8ef9cb 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_splitter.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_splitter.h @@ -24,6 +24,7 @@ #include #include "nhttp_enum.h" +#include "nhttp_infractions.h" //------------------------------------------------------------------------- // NHttpSplitter class @@ -39,11 +40,12 @@ public: virtual uint32_t get_num_excess() const { return 0; }; virtual bool get_zero_chunk() const { return false; }; virtual bool partial_ok() const { return true; }; - + NHttpInfractions get_infractions() const { return infractions; } protected: uint32_t octets_seen = 0; // number of octets processed by previous split() calls that returned NOTFOUND uint32_t num_crlf = 0; uint32_t num_flush = 0; + NHttpInfractions infractions; }; class NHttpStartSplitter : public NHttpSplitter { diff --git a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc index b661bafa1..97009c28b 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc @@ -32,7 +32,7 @@ using namespace NHttpEnums; // Convenience function. All the housekeeping that must be done before we can return FLUSH to stream. void NHttpStreamSplitter::prepare_flush(NHttpFlowData* session_data, uint32_t* flush_offset, SectionType section_type, - bool tcp_close, uint64_t infractions, uint32_t num_octets, uint32_t length, uint32_t num_excess, + bool tcp_close, const NHttpInfractions& infractions, uint32_t num_octets, uint32_t length, uint32_t num_excess, bool zero_chunk) { session_data->section_type[source_id] = section_type; session_data->num_excess[source_id] = num_excess; @@ -156,14 +156,15 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat } if (tcp_close) { if (splitter->partial_ok()) { - prepare_flush(session_data, flush_offset, type, true, INF_TRUNCATED, length, length, - splitter->get_num_excess(), splitter->get_zero_chunk()); + prepare_flush(session_data, flush_offset, type, true, splitter->get_infractions() += INF_TRUNCATED, + length, length, splitter->get_num_excess(), splitter->get_zero_chunk()); delete splitter; splitter = nullptr; return StreamSplitter::FLUSH; } else { - prepare_flush(session_data, flush_offset, SEC_DISCARD, true, 0, length, length, 0, false); + prepare_flush(session_data, flush_offset, SEC_DISCARD, true, splitter->get_infractions(), length, + length, 0, false); delete splitter; splitter = nullptr; return StreamSplitter::FLUSH; @@ -185,8 +186,8 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat case SCAN_DISCARD: case SCAN_DISCARD_CONTINUE: { const uint32_t flush_octets = splitter->get_num_flush(); - prepare_flush(session_data, flush_offset, SEC_DISCARD, tcp_close && (flush_octets >= length), 0, - flush_octets, length, 0, false); + prepare_flush(session_data, flush_offset, SEC_DISCARD, tcp_close && (flush_octets >= length), + splitter->get_infractions(), flush_octets, length, 0, false); if (split_result == SCAN_DISCARD) { delete splitter; splitter = nullptr; @@ -195,8 +196,9 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat } case SCAN_FOUND: { const uint32_t flush_octets = splitter->get_num_flush(); - prepare_flush(session_data, flush_offset, type, tcp_close && (flush_octets == length), 0, - flush_octets, length, splitter->get_num_excess(), splitter->get_zero_chunk()); + prepare_flush(session_data, flush_offset, type, tcp_close && (flush_octets == length), + splitter->get_infractions(), flush_octets, length, splitter->get_num_excess(), + splitter->get_zero_chunk()); delete splitter; splitter = nullptr; if ((type == SEC_REQUEST) || (type == SEC_STATUS)) { @@ -212,7 +214,7 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat } case SEC_BODY: { prepare_flush(session_data, flush_offset, SEC_BODY, - tcp_close && (length <= session_data->data_length[source_id]), 0, + tcp_close && (length <= session_data->data_length[source_id]), NHttpInfractions(), session_data->data_length[source_id], length, 0, false); return StreamSplitter::FLUSH; } @@ -312,7 +314,7 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total, // zero_chunk == true) flushes accumulated chunks. // infractions for each aggregated small chunk are accumulated here and then restored before processing - session_data->chunk_infractions[source_id] |= session_data->infractions[source_id]; + session_data->chunk_infractions[source_id] += session_data->infractions[source_id]; // FIXIT-M this implementation of the zero-length chunk is temporary until stream can support a zero- // octet flush. @@ -333,7 +335,7 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total, } paf_max = DATABLOCKSIZE; session_data->infractions[source_id] = session_data->chunk_infractions[source_id]; - session_data->chunk_infractions[source_id] = 0; + session_data->chunk_infractions[source_id] = NHttpInfractions(); send_to_detection = my_inspector->process(chunk_buffer, total_chunk_len, flow, source_id, true); if (session_data->zero_chunk[source_id]) { // zero-length chunk is not visible to inspector. Transition to trailer must be handled here. diff --git a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.h b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.h index 6c6f63b28..aa757b6fc 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.h @@ -40,7 +40,7 @@ public: unsigned max() override { return NHttpTestManager::use_test_input() ? NHttpEnums::DATABLOCKSIZE : paf_max; }; private: void prepare_flush(NHttpFlowData* session_data, uint32_t* flush_offset, NHttpEnums::SectionType section_type, - bool tcp_close, uint64_t infractions, uint32_t num_octets, uint32_t length, uint32_t num_excess, + bool tcp_close, const NHttpInfractions& infractions, uint32_t num_octets, uint32_t length, uint32_t num_excess, bool zero_chunk); NHttpSplitter* get_splitter(NHttpEnums::SectionType type) const; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri.cc b/src/service_inspectors/nhttp_inspect/nhttp_uri.cc index 7c1d44867..ac265977f 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_uri.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri.cc @@ -84,7 +84,7 @@ void NHttpUri::parse_uri() { abs_path.start = uri.start + k; } else { - format_infractions |= INF_BADURI; + format_infractions += INF_BADURI; uri_type = URI__PROBLEMATIC; scheme.length = STAT_PROBLEMATIC; authority.length = STAT_PROBLEMATIC; @@ -105,7 +105,7 @@ SchemeId NHttpUri::get_scheme_id() { // Normalize scheme name to lower case for matching purposes uint8_t *lower_scheme; if ((lower_scheme = scratch_pad.request(scheme.length)) == nullptr) { - scheme_infractions |= INF_NOSCRATCH; + scheme_infractions += INF_NOSCRATCH; scheme_id = SCH__INSUFMEMORY; return scheme_id; } @@ -175,7 +175,7 @@ int32_t NHttpUri::get_port_value() { port_value = port_value * 10 + (port.start[k] - '0'); if ((port.start[k] < '0') || (port.start[k] > '9') || (port_value > 65535)) { - port_infractions |= INF_BADPORT; + port_infractions += INF_BADPORT; port_value = STAT_PROBLEMATIC; break; } @@ -248,7 +248,8 @@ const Field& NHttpUri::get_norm_legacy() { } // We can reuse the raw URI for the normalized URI unless at least one part of the URI has been normalized - if ((host_infractions == 0) && (path_infractions == 0) && (query_infractions == 0) && (fragment_infractions == 0)) { + if ((host_infractions.none_found()) && (path_infractions.none_found()) && (query_infractions.none_found()) && + (fragment_infractions.none_found())) { legacy_norm.start = uri.start; legacy_norm.length = uri.length; return legacy_norm; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri.h b/src/service_inspectors/nhttp_inspect/nhttp_uri.h index 7e0221ca8..7c7f660b6 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_uri.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri.h @@ -25,6 +25,7 @@ #include "nhttp_str_to_code.h" #include "nhttp_uri_norm.h" #include "nhttp_field.h" +#include "nhttp_infractions.h" //------------------------------------------------------------------------- // NHttpUri class @@ -45,15 +46,15 @@ public: const Field& get_query() { parse_abs_path(); return query; }; const Field& get_fragment() { parse_abs_path(); return fragment; }; - uint64_t get_format_infractions() { parse_uri(); return format_infractions; }; - uint64_t get_scheme_infractions() { get_scheme_id(); return scheme_infractions; }; - uint64_t get_host_infractions() { get_norm_host(); return host_infractions; }; - uint64_t get_port_infractions() { get_port_value(); return port_infractions; }; - uint64_t get_path_infractions() { get_norm_path(); return path_infractions; }; - uint64_t get_query_infractions() { get_norm_query(); return query_infractions; }; - uint64_t get_fragment_infractions() { get_norm_fragment(); return fragment_infractions; }; - uint64_t get_uri_infractions() { return get_format_infractions() | get_scheme_infractions() | get_host_infractions() | - get_port_infractions() | get_path_infractions() | get_query_infractions() | get_fragment_infractions(); }; + NHttpInfractions get_format_infractions() { parse_uri(); return format_infractions; }; + NHttpInfractions get_scheme_infractions() { get_scheme_id(); return scheme_infractions; }; + NHttpInfractions get_host_infractions() { get_norm_host(); return host_infractions; }; + NHttpInfractions get_port_infractions() { get_port_value(); return port_infractions; }; + NHttpInfractions get_path_infractions() { get_norm_path(); return path_infractions; }; + NHttpInfractions get_query_infractions() { get_norm_query(); return query_infractions; }; + NHttpInfractions get_fragment_infractions() { get_norm_fragment(); return fragment_infractions; }; + NHttpInfractions get_uri_infractions() { return get_format_infractions() + get_scheme_infractions() + get_host_infractions() + + get_port_infractions() + get_path_infractions() + get_query_infractions() + get_fragment_infractions(); }; NHttpEnums::SchemeId get_scheme_id(); const Field& get_norm_host(); @@ -78,13 +79,13 @@ private: Field query; Field fragment; - uint64_t format_infractions = 0; - uint64_t scheme_infractions = 0; - uint64_t host_infractions = 0; - uint64_t port_infractions = 0; - uint64_t path_infractions = 0; - uint64_t query_infractions = 0; - uint64_t fragment_infractions = 0; + NHttpInfractions format_infractions; + NHttpInfractions scheme_infractions; + NHttpInfractions host_infractions; + NHttpInfractions port_infractions; + NHttpInfractions path_infractions; + NHttpInfractions query_infractions; + NHttpInfractions fragment_infractions; NHttpEnums::UriType uri_type = NHttpEnums::URI__NOTCOMPUTE; NHttpEnums::SchemeId scheme_id = NHttpEnums::SCH__NOTCOMPUTE; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc index 1b1b998d0..554d7cddc 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc @@ -26,7 +26,8 @@ using namespace NHttpEnums; -void UriNormalizer::normalize(const Field &input, Field &result, bool do_path, ScratchPad &scratch_pad, uint64_t &infractions) { +void UriNormalizer::normalize(const Field &input, Field &result, bool do_path, ScratchPad &scratch_pad, + NHttpInfractions &infractions) { if (result.length != STAT_NOTCOMPUTE) return; assert (input.length >= 0); @@ -65,27 +66,28 @@ void UriNormalizer::normalize(const Field &input, Field &result, bool do_path, S result.length = data_length; } -bool UriNormalizer::no_path_check(const uint8_t* in_buf, int32_t in_length, uint64_t& infractions) { +bool UriNormalizer::no_path_check(const uint8_t* in_buf, int32_t in_length, NHttpInfractions& infractions) { for (int32_t k = 0; k < in_length; k++) { if ((uri_char[in_buf[k]] == CHAR_NORMAL) || (uri_char[in_buf[k]] == CHAR_PATH)) continue; - infractions |= INF_URINEEDNORM; + infractions += INF_URINEEDNORM; return false; } return true; } -bool UriNormalizer::path_check(const uint8_t* in_buf, int32_t in_length, uint64_t& infractions) { +bool UriNormalizer::path_check(const uint8_t* in_buf, int32_t in_length, NHttpInfractions& infractions) { for (int32_t k = 0; k < in_length; k++) { // FIXIT-P Periods are common and most don't need to be normalized. Need a better test. if (uri_char[in_buf[k]] == CHAR_NORMAL) continue; if ((in_buf[k] == '/') && ((k == 0) || (in_buf[k-1] != '/'))) continue; - infractions |= INF_URINEEDNORM; + infractions += INF_URINEEDNORM; return false; } return true; } -int32_t UriNormalizer::norm_char_clean(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t& infractions, const void *) { +int32_t UriNormalizer::norm_char_clean(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, + NHttpInfractions& infractions, const void *) { int32_t length = 0; for (int32_t k = 0; k < in_length; k++) { switch (uri_char[in_buf[k]]) { @@ -94,11 +96,11 @@ int32_t UriNormalizer::norm_char_clean(const uint8_t* in_buf, int32_t in_length, out_buf[length++] = in_buf[k]; break; case CHAR_INVALID: - infractions |= INF_URIBADCHAR; + infractions += INF_URIBADCHAR; out_buf[length++] = in_buf[k]; break; case CHAR_EIGHTBIT: - infractions |= INF_URI8BITCHAR; + infractions += INF_URI8BITCHAR; out_buf[length++] = in_buf[k]; break; case CHAR_PERCENT: @@ -107,32 +109,32 @@ int32_t UriNormalizer::norm_char_clean(const uint8_t* in_buf, int32_t in_length, uint8_t value = as_hex[in_buf[k+1]] * 16 + as_hex[in_buf[k+2]]; if (good_percent[value]) { // Normal % escape of an ASCII special character that is supposed to be escaped - infractions |= INF_URIPERCENTNORMAL; + infractions += INF_URIPERCENTNORMAL; out_buf[length++] = '%'; } else { // Suspicious % escape of an ASCII character that does not need to be escaped - infractions |= INF_URIPERCENTASCII; - if (uri_char[value] == CHAR_INVALID) infractions |= INF_URIBADCHAR; + infractions += INF_URIPERCENTASCII; + if (uri_char[value] == CHAR_INVALID) infractions += INF_URIBADCHAR; out_buf[length++] = value; k += 2; } } else { // UTF-8 decoding not implemented yet - infractions |= INF_URIPERCENTUTF8; + infractions += INF_URIPERCENTUTF8; out_buf[length++] = '%'; } } else if ((k+5 < in_length) && (in_buf[k+1] == 'u') && (as_hex[in_buf[k+2]] != -1) && (as_hex[in_buf[k+3]] != -1) && (as_hex[in_buf[k+4]] != -1) && (as_hex[in_buf[k+5]] != -1)) { // 'u' UTF-16 decoding not implemented yet - infractions |= INF_URIPERCENTUCODE; + infractions += INF_URIPERCENTUCODE; out_buf[length++] = '%'; } else { // Don't recognize it - infractions |= INF_URIPERCENTOTHER; + infractions += INF_URIPERCENTOTHER; out_buf[length++] = '%'; } break; @@ -142,19 +144,21 @@ int32_t UriNormalizer::norm_char_clean(const uint8_t* in_buf, int32_t in_length, } // Convert URI backslashes to slashes -int32_t UriNormalizer::norm_backslash(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t& infractions, const void *) { +int32_t UriNormalizer::norm_backslash(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, + NHttpInfractions& infractions, const void*) { for (int32_t k = 0; k < in_length; k++) { if (in_buf[k] != '\\') out_buf[k] = in_buf[k]; else { out_buf[k] = '/'; - infractions |= INF_URIBACKSLASH; + infractions += INF_URIBACKSLASH; } } return in_length; } // Caution: worst case output length is one greater than input length -int32_t UriNormalizer::norm_path_clean(const uint8_t* in_buf, int32_t in_length, uint8_t *out_buf, uint64_t& infractions, const void *) { +int32_t UriNormalizer::norm_path_clean(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf, + NHttpInfractions& infractions, const void*) { int32_t length = 0; // It simplifies the code that handles /./ and /../ to pretend there is an extra '/' after the buffer. // Avoids making a special case of URIs that end in . or .. @@ -166,17 +170,17 @@ int32_t UriNormalizer::norm_path_clean(const uint8_t* in_buf, int32_t in_length, } // Ignore this slash if it directly follows another slash else if ((k < in_length) && (length >= 1) && (out_buf[length-1] == '/')) { - infractions |= INF_URIMULTISLASH; + infractions += INF_URIMULTISLASH; } // This slash is the end of a /./ pattern, ignore this slash and remove the period from the output else if ((length >= 2) && (out_buf[length-1] == '.') && (out_buf[length-2] == '/')) { - infractions |= INF_URISLASHDOT; + infractions += INF_URISLASHDOT; length -= 1; } // This slash is the end of a /../ pattern, normalization depends on whether there is a previous directory that // we can remove else if ((length >= 3) && (out_buf[length-1] == '.') && (out_buf[length-2] == '.') && (out_buf[length-3] == '/')) { - infractions |= INF_URISLASHDOTDOT; + infractions += INF_URISLASHDOTDOT; // Traversing above the root of the absolute path. A path of the form /../../../foo/bar/whatever cannot be // further normalized. Instead of taking away a directory we leave the .. and write out the new slash. // This code can write out the pretend slash after the end of the buffer. That is intentional so that the @@ -184,7 +188,7 @@ int32_t UriNormalizer::norm_path_clean(const uint8_t* in_buf, int32_t in_length, if ( (length == 3) || ((length >= 6) && (out_buf[length-4] == '.') && (out_buf[length-5] == '.') && (out_buf[length-6] == '/'))) { - infractions |= INF_URIROOTTRAV; + infractions += INF_URIROOTTRAV; out_buf[length++] = '/'; } // Remove the previous directory from the output. "/foo/bar/../" becomes "/foo/" diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h index 462bd7499..aaaa4e3c8 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h @@ -23,21 +23,23 @@ #include "nhttp_scratch_pad.h" #include "nhttp_field.h" +#include "nhttp_infractions.h" class UriNormalizer { public: - static void normalize(const Field &input, Field &result, bool do_path, ScratchPad &scratch_pad, uint64_t &infractions); + static void normalize(const Field &input, Field &result, bool do_path, ScratchPad &scratch_pad, + NHttpInfractions &infractions); private: static const NHttpEnums::CharAction uri_char[256]; static const bool good_percent[256]; - static bool no_path_check(const uint8_t* in_buf, int32_t in_length, uint64_t& infractions); - static bool path_check(const uint8_t* in_buf, int32_t in_length, uint64_t& infractions); + static bool no_path_check(const uint8_t* in_buf, int32_t in_length, NHttpInfractions& infractions); + static bool path_check(const uint8_t* in_buf, int32_t in_length, NHttpInfractions& infractions); - static int32_t norm_char_clean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used); - static int32_t norm_backslash(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used); - static int32_t norm_path_clean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* not_used); + static int32_t norm_char_clean(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used); + static int32_t norm_backslash(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used); + static int32_t norm_path_clean(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const void* not_used); }; #endif