From: Tom Peters Date: Wed, 9 Jul 2014 22:09:06 +0000 (-0400) Subject: checkpoint after dynamic section allocation X-Git-Tag: 3.0.0-233~1447 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7537fe042d6ef18dbc3a203d4d074613655d2d47;p=thirdparty%2Fsnort3.git checkpoint after dynamic section allocation --- diff --git a/src/service_inspectors/nhttp_inspect/CMakeLists.txt b/src/service_inspectors/nhttp_inspect/CMakeLists.txt index 361a076a4..7d9904474 100644 --- a/src/service_inspectors/nhttp_inspect/CMakeLists.txt +++ b/src/service_inspectors/nhttp_inspect/CMakeLists.txt @@ -24,6 +24,8 @@ set (FILE_LIST nhttp_msg_trailer.h nhttp_head_norm.cc nhttp_head_norm.h + nhttp_uri.cc + nhttp_uri.h nhttp_uri_norm.cc nhttp_uri_norm.h nhttp_normalizers.cc diff --git a/src/service_inspectors/nhttp_inspect/Makefile.am b/src/service_inspectors/nhttp_inspect/Makefile.am index d10c5da97..c5370aab1 100644 --- a/src/service_inspectors/nhttp_inspect/Makefile.am +++ b/src/service_inspectors/nhttp_inspect/Makefile.am @@ -13,6 +13,7 @@ nhttp_msg_chunk_head.cc nhttp_msg_chunk_head.h \ nhttp_msg_chunk_body.cc nhttp_msg_chunk_body.h \ nhttp_msg_trailer.cc nhttp_msg_trailer.h \ nhttp_head_norm.cc nhttp_head_norm.h \ +nhttp_uri.cc nhttp_uri.h \ nhttp_uri_norm.cc nhttp_uri_norm.h \ nhttp_normalizers.cc nhttp_normalizers.h \ nhttp_str_to_code.cc nhttp_str_to_code.h \ diff --git a/src/service_inspectors/nhttp_inspect/nhttp_api.cc b/src/service_inspectors/nhttp_inspect/nhttp_api.cc index 68d6cd092..94bff6977 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_api.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_api.cc @@ -34,16 +34,11 @@ #include "snort.h" #include "target_based/sftarget_protocol_reference.h" #include "nhttp_enum.h" -#include "nhttp_module.h" #include "nhttp_inspect.h" #include "nhttp_api.h" int16_t NHttpApi::appProtocolId; -Module* NHttpApi::nhttp_mod_ctor() { return new NHttpModule; } - -void NHttpApi::nhttp_mod_dtor(Module* m) { delete m; } - const char* NHttpApi::nhttp_myName = "nhttp_inspect"; void NHttpApi::nhttp_init() @@ -52,47 +47,10 @@ void NHttpApi::nhttp_init() appProtocolId = AddProtocolReference("nhttp"); } -void NHttpApi::nhttp_term() -{ -} - Inspector* NHttpApi::nhttp_ctor(Module* mod) { const NHttpModule* nhttpMod = (NHttpModule*) mod; - return new NHttpInspect(nhttpMod->get_test_input(), nhttpMod->get_test_output()); -} - -void NHttpApi::nhttp_dtor(Inspector* p) -{ - delete p; -} - -void NHttpApi::nhttp_pinit() -{ - NHttpInspect::msgRequest = new NHttpMsgRequest; - NHttpInspect::msgStatus = new NHttpMsgStatus; - NHttpInspect::msgHead = new NHttpMsgHeader; - NHttpInspect::msgBody = new NHttpMsgBody; - NHttpInspect::msgChunkHead = new NHttpMsgChunkHead; - NHttpInspect::msgChunkBody = new NHttpMsgChunkBody; - NHttpInspect::msgTrailer = new NHttpMsgTrailer; -} - -void NHttpApi::nhttp_pterm() -{ - delete NHttpInspect::msgHead; -} - -void NHttpApi::nhttp_sum() -{ -} - -void NHttpApi::nhttp_stats() -{ -} - -void NHttpApi::nhttp_reset() -{ + return new NHttpInspect(nhttpMod->get_test_input(), nhttpMod->get_test_output(), nhttpMod->get_test_inspect()); } static const char* buffers[] = diff --git a/src/service_inspectors/nhttp_inspect/nhttp_api.h b/src/service_inspectors/nhttp_inspect/nhttp_api.h index 4335e7902..4e4b6ee85 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_api.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_api.h @@ -33,24 +33,26 @@ #include "framework/module.h" #include "framework/inspector.h" +#include "nhttp_module.h" + class NHttpApi { public: static const InspectApi nhttp_api; static int16_t appProtocolId; private: NHttpApi() = delete; - static Module* nhttp_mod_ctor(); - static void nhttp_mod_dtor(Module* m); + static Module* nhttp_mod_ctor() { return new NHttpModule; }; + static void nhttp_mod_dtor(Module* m) { delete m; }; static const char* nhttp_myName; static void nhttp_init(); - static void nhttp_term(); + static void nhttp_term() {}; static Inspector* nhttp_ctor(Module* mod); - static void nhttp_dtor(Inspector* p); - static void nhttp_pinit(); - static void nhttp_pterm(); - static void nhttp_sum(); - static void nhttp_stats(); - static void nhttp_reset(); + static void nhttp_dtor(Inspector* p) { delete p; }; + static void nhttp_pinit() {}; + static void nhttp_pterm() {}; + static void nhttp_sum() {}; + static void nhttp_stats() {}; + static void nhttp_reset() {}; }; #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_enum.h b/src/service_inspectors/nhttp_inspect/nhttp_enum.h index 8da00127f..fa94fb342 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_enum.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_enum.h @@ -38,7 +38,7 @@ namespace NHttpEnums { static const uint32_t MAXOCTETS = 63780; // Field status codes for when no valid value is present in length or integer value. Positive values are actual length or field value. -typedef enum { STAT_NOTCONFIGURED=-5, STAT_NOTCOMPUTE=-4, STAT_INSUFMEMORY=-3, STAT_PROBLEMATIC=-2, STAT_NOTPRESENT=-1, STAT_EMPTYSTRING=0, STAT_OTHER=1 } StatusCode; +typedef enum { STAT_NOSOURCE=-6, STAT_NOTCONFIGURED=-5, STAT_NOTCOMPUTE=-4, STAT_INSUFMEMORY=-3, STAT_PROBLEMATIC=-2, STAT_NOTPRESENT=-1, STAT_EMPTYSTRING=0, STAT_OTHER=1 } StatusCode; // Message originator--client or server typedef enum { SRC__NOTCOMPUTE=-4, SRC_CLIENT=0, SRC_SERVER=1 } SourceId; @@ -48,21 +48,21 @@ typedef enum { SEC__NOTCOMPUTE=-4, SEC__NOTPRESENT=-1, SEC_REQUEST = 2, SEC_STAT SEC_CLOSED, SEC_ABORT } SectionType; // 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" -typedef enum { VERS__NOTCOMPUTE=-4, VERS__PROBLEMATIC=-2, VERS__NOTPRESENT=-1, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 } VersionId; +typedef enum { VERS__NOSOURCE=-6, VERS__NOTCOMPUTE=-4, VERS__PROBLEMATIC=-2, VERS__NOTPRESENT=-1, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 } VersionId; // Every request method we have ever heard of -typedef enum { METH__NOTCOMPUTE=-4, METH__INSUFMEMORY=-3, METH__PROBLEMATIC=-2, METH__NOTPRESENT=-1, METH__OTHER=1, METH_OPTIONS, METH_GET, METH_HEAD, METH_POST, METH_PUT, - METH_DELETE, METH_TRACE, METH_CONNECT, METH_PROPFIND, +typedef enum { METH__NOSOURCE=-6, METH__NOTCOMPUTE=-4, METH__INSUFMEMORY=-3, METH__PROBLEMATIC=-2, METH__NOTPRESENT=-1, 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, METH_MERGE, METH_BASELINE_CONTROL, METH_MKACTIVITY, METH_ORDERPATCH, METH_ACL, METH_PATCH, METH_SEARCH, METH_BCOPY, METH_BDELETE, METH_BMOVE, METH_BPROPFIND, METH_BPROPPATCH, METH_NOTIFY, METH_POLL, METH_SUBSCRIBE, METH_UNSUBSCRIBE, METH_X_MS_ENUMATTS, METH_BIND, METH_LINK, METH_MKCALENDAR, METH_MKREDIRECTREF, METH_REBIND, METH_UNBIND, METH_UNLINK, METH_UPDATEREDIRECTREF } MethodId; // URI formats -typedef enum { URI__NOTCOMPUTE=-4, URI__PROBLEMATIC=-2, URI__NOTPRESENT=-1, URI_ASTERISK = 2, URI_AUTHORITY, URI_ABSPATH, URI_ABSOLUTE } UriType; +typedef enum { URI__NOSOURCE=-6, URI__NOTCOMPUTE=-4, URI__PROBLEMATIC=-2, URI_ASTERISK = 2, URI_AUTHORITY, URI_ABSPATH, URI_ABSOLUTE } UriType; // URI schemes -typedef enum { SCH__NOTCOMPUTE=-4, SCH__INSUFMEMORY=-3, SCH__NOTPRESENT=-1, SCH_OTHER = 1, SCH_HTTP, SCH_HTTPS, SCH_FTP, SCH_GOPHER, SCH_FILE } SchemeId; +typedef enum { SCH__NOSOURCE=-6, SCH__NOTCOMPUTE=-4, SCH__INSUFMEMORY=-3, SCH__NOTPRESENT=-1, SCH_OTHER = 1, SCH_HTTP, SCH_HTTPS, SCH_FTP, SCH_GOPHER, SCH_FILE } SchemeId; // Every header we have ever heard of typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2, HEAD__NOTPRESENT=-1, HEAD__OTHER=1, HEAD_CACHE_CONTROL, HEAD_CONNECTION, HEAD_DATE, @@ -71,7 +71,8 @@ typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2, HEAD_EXPECT, HEAD_FROM, HEAD_HOST, HEAD_IF_MATCH, HEAD_IF_MODIFIED_SINCE, HEAD_IF_NONE_MATCH, HEAD_IF_RANGE, HEAD_IF_UNMODIFIED_SINCE, HEAD_MAX_FORWARDS, HEAD_PROXY_AUTHORIZATION, HEAD_RANGE, HEAD_REFERER, HEAD_TE, HEAD_USER_AGENT, HEAD_ACCEPT_RANGES, HEAD_AGE, HEAD_ETAG, HEAD_LOCATION, HEAD_PROXY_AUTHENTICATE, HEAD_RETRY_AFTER, HEAD_SERVER, HEAD_VARY, HEAD_WWW_AUTHENTICATE, HEAD_ALLOW, HEAD_CONTENT_ENCODING, HEAD_CONTENT_LANGUAGE, HEAD_CONTENT_LENGTH, - HEAD_CONTENT_LOCATION, HEAD_CONTENT_MD5, HEAD_CONTENT_RANGE, HEAD_CONTENT_TYPE, HEAD_EXPIRES, HEAD_LAST_MODIFIED, HEAD__MAXVALUE } HeaderId; + HEAD_CONTENT_LOCATION, HEAD_CONTENT_MD5, HEAD_CONTENT_RANGE, HEAD_CONTENT_TYPE, HEAD_EXPIRES, HEAD_LAST_MODIFIED, HEAD_X_FORWARDED_FOR, HEAD_TRUE_CLIENT_IP, + HEAD__MAXVALUE } HeaderId; // All the infractions we might find while parsing and analyzing a message typedef enum { @@ -99,8 +100,8 @@ typedef enum { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSC // Never use the start pointer without verifying that length > 0. struct field { public: - const uint8_t *start; - int32_t length; + const uint8_t *start = nullptr; + int32_t length = NHttpEnums::STAT_NOTCOMPUTE; }; typedef enum diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc index 01b0f5a0e..818c22900 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc @@ -51,7 +51,6 @@ void NHttpFlowData::halfReset(SourceId sourceId) { versionId[sourceId] = VERS__NOTPRESENT; methodId[sourceId] = METH__NOTPRESENT; - schemeId[sourceId] = SCH__NOTPRESENT; statusCodeNum[sourceId] = STAT_NOTPRESENT; } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h index a11ad7713..c35ed00be 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h @@ -78,7 +78,6 @@ private: // Some items don't apply in both directions. Have two copies anyway just to simplify code and minimize hard-to-find bugs NHttpEnums::VersionId versionId[2] = { NHttpEnums::VERS__NOTPRESENT, NHttpEnums::VERS__NOTPRESENT }; NHttpEnums::MethodId methodId[2] = { NHttpEnums::METH__NOTPRESENT, NHttpEnums::METH__NOTPRESENT }; - NHttpEnums::SchemeId schemeId[2] = { NHttpEnums::SCH__NOTPRESENT, NHttpEnums::SCH__NOTPRESENT }; int32_t statusCodeNum[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT }; int64_t dataLength[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT }; // length of the data from Content-Length field or chunk header. diff --git a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc index bd30faa33..9b3d6adc8 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc @@ -59,12 +59,14 @@ int32_t HeaderNormalizer::deriveHeaderContent(const uint8_t *value, int32_t leng return outLength; } -void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions, HeaderId headId, const HeaderId headerNameId[], const field headerValue[], int32_t numHeaders, +// This method normalizes the header field value for headId. +int32_t HeaderNormalizer::normalize(HeaderId headId, ScratchPad &scratchPad, uint64_t &infractions, const HeaderId headerNameId[], const field headerValue[], int32_t numHeaders, field &resultField) const { - // This method normalizes the header field value for headId. + // If the raw header is not present length will be STAT_NOSOURCE and normalization is skipped + if (resultField.length != STAT_NOTCOMPUTE) return resultField.length; if (format == NORM_NULL) { resultField.length = STAT_NOTCONFIGURED; - return; + return resultField.length; } // Search Header IDs from all the headers in this message. A critical issue is whether the header can be present more than once in a message. concatenateRepeats means the @@ -85,8 +87,8 @@ void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions, } } if (numMatches == 0) { - resultField.length = STAT_NOTPRESENT; - return; + resultField.length = STAT_NOSOURCE; + return resultField.length; } if (infractRepeats && (numMatches >= 2)) infractions |= INF_BADHEADERREPS; @@ -102,7 +104,7 @@ void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions, uint8_t * const scratch = scratchPad.request(2*bufferLength); if (scratch == nullptr) { resultField.length = STAT_INSUFMEMORY; - return; + return resultField.length; } uint8_t * const frontHalf = scratch; @@ -127,12 +129,13 @@ void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions, else dataLength = normalizer[i](frontHalf, dataLength, backHalf, infractions, normArg[i]); if (dataLength <= 0) { resultField.length = dataLength; - return; + return resultField.length; } } resultField.start = scratch; resultField.length = dataLength; scratchPad.commit(dataLength); + return resultField.length; } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h index 6aab0e275..63b0d7e03 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h @@ -52,7 +52,7 @@ public: normalizer { f1, f2, f3 }, normArg { f1Arg, f2Arg, f3Arg }, numNormalizers((f1 != nullptr) + (f1 != nullptr)*(f2 != nullptr) + (f1 != nullptr)*(f2 != nullptr)*(f3 != nullptr)) {}; - void normalize(ScratchPad &scratchPad, uint64_t &infractions, NHttpEnums::HeaderId headId, const NHttpEnums::HeaderId headerNameId[], const field headerName[], int32_t numHeaders, + int32_t normalize(NHttpEnums::HeaderId headId, ScratchPad &scratchPad, uint64_t &infractions, const NHttpEnums::HeaderId headerNameId[], const field headerName[], int32_t numHeaders, field &resultField) const; NHttpEnums::NormFormat getFormat() const {return format;}; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc b/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc index 9b1bd16f4..4bc2aec15 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc @@ -42,15 +42,7 @@ using namespace NHttpEnums; -THREAD_LOCAL NHttpMsgRequest* NHttpInspect::msgRequest; -THREAD_LOCAL NHttpMsgStatus* NHttpInspect::msgStatus; -THREAD_LOCAL NHttpMsgHeader* NHttpInspect::msgHead; -THREAD_LOCAL NHttpMsgBody* NHttpInspect::msgBody; -THREAD_LOCAL NHttpMsgChunkHead* NHttpInspect::msgChunkHead; -THREAD_LOCAL NHttpMsgChunkBody* NHttpInspect::msgChunkBody; -THREAD_LOCAL NHttpMsgTrailer* NHttpInspect::msgTrailer; - -NHttpInspect::NHttpInspect(bool test_input, bool _test_output) : test_output(_test_output) +NHttpInspect::NHttpInspect(bool test_input, bool _test_output, bool _test_inspect) : test_output(_test_output), test_inspect(_test_inspect) { NHttpTestInput::test_input = test_input; if (NHttpTestInput::test_input) { @@ -60,6 +52,7 @@ NHttpInspect::NHttpInspect(bool test_input, bool _test_output) : test_output(_te NHttpInspect::~NHttpInspect () { + delete msgSection; if (NHttpTestInput::test_input) { delete NHttpTestInput::testInput; if (testOut) fclose(testOut); @@ -116,55 +109,57 @@ void NHttpInspect::eval(Packet* p) void NHttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const flow) { + delete msgSection; + msgSection = nullptr; + NHttpFlowData* sessionData = (NHttpFlowData*)flow->get_application_data(NHttpFlowData::nhttp_flow_id); assert(sessionData); - NHttpMsgSection *msgSect = nullptr; - if (!NHttpTestInput::test_input) { switch (sessionData->sectionType) { - case SEC_REQUEST: msgSect = msgRequest; break; - case SEC_STATUS: msgSect = msgStatus; break; - case SEC_HEADER: msgSect = msgHead; break; - case SEC_BODY: msgSect = msgBody; break; - case SEC_CHUNKHEAD: msgSect = msgChunkHead; break; - case SEC_CHUNKBODY: msgSect = msgChunkBody; break; - case SEC_TRAILER: msgSect = msgTrailer; break; + case SEC_REQUEST: msgSection = new NHttpMsgRequest; break; + case SEC_STATUS: msgSection = new NHttpMsgStatus; break; + case SEC_HEADER: msgSection = new NHttpMsgHeader; break; + case SEC_BODY: msgSection = new NHttpMsgBody; break; + case SEC_CHUNKHEAD: msgSection = new NHttpMsgChunkHead; break; + case SEC_CHUNKBODY: msgSection = new NHttpMsgChunkBody; break; + case SEC_TRAILER: msgSection = new NHttpMsgTrailer; break; case SEC_DISCARD: return; default: assert(0); return; } - msgSect->loadSection(data, dsize, sessionData); + msgSection->loadSection(data, dsize, sessionData); } else { uint8_t *testBuffer; uint16_t testLength; if ((testLength = NHttpTestInput::testInput->toEval(&testBuffer, testNumber)) > 0) { switch (sessionData->sectionType) { - case SEC_REQUEST: msgSect = msgRequest; break; - case SEC_STATUS: msgSect = msgStatus; break; - case SEC_HEADER: msgSect = msgHead; break; - case SEC_BODY: msgSect = msgBody; break; - case SEC_CHUNKHEAD: msgSect = msgChunkHead; break; - case SEC_CHUNKBODY: msgSect = msgChunkBody; break; - case SEC_TRAILER: msgSect = msgTrailer; break; + case SEC_REQUEST: msgSection = new NHttpMsgRequest; break; + case SEC_STATUS: msgSection = new NHttpMsgStatus; break; + case SEC_HEADER: msgSection = new NHttpMsgHeader; break; + case SEC_BODY: msgSection = new NHttpMsgBody; break; + case SEC_CHUNKHEAD: msgSection = new NHttpMsgChunkHead; break; + case SEC_CHUNKBODY: msgSection = new NHttpMsgChunkBody; break; + case SEC_TRAILER: msgSection = new NHttpMsgTrailer; break; case SEC_DISCARD: return; default: assert(0); return; } - msgSect->loadSection(testBuffer, testLength, sessionData); + msgSection->loadSection(testBuffer, testLength, sessionData); } else { printf("Zero length test data.\n"); return; } } - msgSect->initSection(); - msgSect->analyze(); - msgSect->updateFlow(); - msgSect->genEvents(); - msgSect->legacyClients(); + msgSection->initSection(); + msgSection->analyze(); + msgSection->updateFlow(); + msgSection->genEvents(); + msgSection->legacyClients(); + if (test_inspect) msgSection->analyzeAll(); if (test_output) { - if (!NHttpTestInput::test_input) msgSect->printSection(stdout); + if (!NHttpTestInput::test_input) msgSection->printSection(stdout); else { if (testNumber != fileTestNumber) { if (testOut) fclose (testOut); @@ -173,7 +168,7 @@ void NHttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* cons snprintf(fileName, sizeof(fileName), "%s%" PRIi64 ".txt", testOutputPrefix, testNumber); if ((testOut = fopen(fileName, "w+")) == nullptr) throw std::runtime_error("Cannot open test output file"); } - msgSect->printSection(testOut); + msgSection->printSection(testOut); } } } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_inspect.h b/src/service_inspectors/nhttp_inspect/nhttp_inspect.h index e49fdd92d..e1db545f7 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_inspect.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_inspect.h @@ -42,7 +42,7 @@ class NHttpApi; class NHttpInspect : public Inspector { public: - NHttpInspect(bool test_input, bool _test_output); + NHttpInspect(bool test_input, bool _test_output, bool _test_inspect); ~NHttpInspect(); bool get_buf(unsigned, Packet*, InspectionBuffer&); @@ -57,18 +57,13 @@ public: private: friend NHttpApi; - static THREAD_LOCAL NHttpMsgRequest *msgRequest; - static THREAD_LOCAL NHttpMsgStatus *msgStatus; - static THREAD_LOCAL NHttpMsgHeader *msgHead; - static THREAD_LOCAL NHttpMsgBody *msgBody; - static THREAD_LOCAL NHttpMsgChunkHead *msgChunkHead; - static THREAD_LOCAL NHttpMsgChunkBody *msgChunkBody; - static THREAD_LOCAL NHttpMsgTrailer *msgTrailer; + NHttpMsgSection *msgSection = nullptr; void process(const uint8_t* data, const uint16_t dsize, Flow* const flow); // Test mode bool test_output; + bool test_inspect; const char *testInputFile = "nhttp_test_msgs.txt"; const char *testOutputPrefix = "nhttpresults/testcase"; FILE *testOut = nullptr; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_module.cc b/src/service_inspectors/nhttp_inspect/nhttp_module.cc index 73b5670e1..691641f32 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_module.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_module.cc @@ -39,11 +39,13 @@ NHttpModule::NHttpModule() : Module("nhttp_inspect", nhttpParams, nhttpEvents) { const Parameter NHttpModule::nhttpParams[] = {{ "test_input", Parameter::PT_BOOL, nullptr, "false", "read HTTP messages from text file" }, { "test_output", Parameter::PT_BOOL, nullptr, "false", "print out HTTP section data" }, + { "test_inspect", Parameter::PT_BOOL, nullptr, "false", "force all possible inspections and normalizations" }, { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }}; bool NHttpModule::begin(const char*, int, SnortConfig*) { test_input = false; test_output = false; + test_inspect = false; return true; } @@ -54,6 +56,7 @@ bool NHttpModule::end(const char*, int, SnortConfig*) { bool NHttpModule::set(const char*, Value &val, SnortConfig*) { if (val.is("test_input")) test_input = val.get_bool(); else if (val.is("test_output")) test_output = val.get_bool(); + else if (val.is("test_inspect")) test_inspect = val.get_bool(); else return false; return true; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_module.h b/src/service_inspectors/nhttp_inspect/nhttp_module.h index e1a9f566d..6b2b9a993 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_module.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_module.h @@ -41,11 +41,13 @@ public: unsigned get_gid() const; bool get_test_input() const { return test_input; }; bool get_test_output() const { return test_output; }; + bool get_test_inspect() const { return test_inspect; }; private: static const Parameter nhttpParams[]; static const RuleMap nhttpEvents[]; bool test_input = false; bool test_output = false; + bool test_inspect = false; }; #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc index 2f0d9c76e..207947370 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc @@ -72,7 +72,7 @@ void NHttpMsgBody::printSection(FILE *output) const { NHttpMsgSection::printMessageWrapup(output); } -void NHttpMsgBody::updateFlow() const { +void NHttpMsgBody::updateFlow() { if (tcpClose) { sessionData->typeExpected[sourceId] = SEC_CLOSED; sessionData->halfReset(sourceId); @@ -91,7 +91,7 @@ void NHttpMsgBody::updateFlow() const { // Legacy support function. Puts message fields into the buffers used by old Snort. -void NHttpMsgBody::legacyClients() const { +void NHttpMsgBody::legacyClients() { ClearHttpBuffers(); if (data.length > 0) SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, data.start, (unsigned)data.length); } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h index 3b6f3cf2b..97a3c5800 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h @@ -43,8 +43,8 @@ public: void analyze(); void printSection(FILE *output) const; void genEvents(); - void updateFlow() const; - void legacyClients() const; + void updateFlow(); + void legacyClients(); protected: int64_t dataLength; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc index 32485707b..fddf73cc1 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc @@ -77,6 +77,7 @@ void NHttpMsgChunkBody::analyze() { if (tcpClose) infractions |= INF_TRUNCATED; } + void NHttpMsgChunkBody::genEvents() { if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event } @@ -89,7 +90,7 @@ void NHttpMsgChunkBody::printSection(FILE *output) const { NHttpMsgSection::printMessageWrapup(output); } -void NHttpMsgChunkBody::updateFlow() const { +void NHttpMsgChunkBody::updateFlow() { if (tcpClose) { sessionData->typeExpected[sourceId] = SEC_CLOSED; sessionData->halfReset(sourceId); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h index 34ffb1d4d..f18e6c2e4 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h @@ -43,7 +43,7 @@ public: void analyze(); void printSection(FILE *output) const; void genEvents(); - void updateFlow() const; + void updateFlow(); private: int64_t numChunks; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc index 248ec3ea4..c15382e85 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc @@ -44,7 +44,7 @@ using namespace NHttpEnums; // vastly bigger than any legitimate chunk. void NHttpMsgChunkHead::deriveChunkLength() { if (chunkSize.length <= 0) { - dataLength = STAT_PROBLEMATIC; + dataLength = STAT_NOSOURCE; infractions |= INF_BADCHUNKSIZE; return; } @@ -118,7 +118,7 @@ void NHttpMsgChunkHead::printSection(FILE *output) const { NHttpMsgSection::printMessageWrapup(output); } -void NHttpMsgChunkHead::updateFlow() const { +void NHttpMsgChunkHead::updateFlow() { if (tcpClose) { sessionData->typeExpected[sourceId] = SEC_CLOSED; sessionData->halfReset(sourceId); @@ -141,7 +141,7 @@ void NHttpMsgChunkHead::updateFlow() const { // Legacy support function. Puts message fields into the buffers used by old Snort. -void NHttpMsgChunkHead::legacyClients() const { +void NHttpMsgChunkHead::legacyClients() { ClearHttpBuffers(); if (startLine.length > 0) SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, startLine.start, (unsigned)startLine.length); } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h index 6c86b08c4..e99ca5e24 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h @@ -43,8 +43,8 @@ public: void analyze(); void printSection(FILE *output) const; void genEvents(); - void updateFlow() const; - void legacyClients() const; + void updateFlow(); + void legacyClients(); private: void deriveChunkLength(); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_head.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_head.cc index 984eda60a..997464a17 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_head.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_head.cc @@ -48,9 +48,12 @@ void NHttpMsgHeader::printSection(FILE *output) const { NHttpMsgSection::printMessageWrapup(output); } -void NHttpMsgHeader::updateFlow() const { +void NHttpMsgHeader::updateFlow() { const uint64_t disasterMask = 0; + ; + headerNorms[HEAD_CONTENT_LENGTH]->normalize(HEAD_CONTENT_LENGTH, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_CONTENT_LENGTH]); + // The following logic to determine body type is by no means the last word on this topic. if (tcpClose) { sessionData->typeExpected[sourceId] = SEC_CLOSED; @@ -66,15 +69,18 @@ void NHttpMsgHeader::updateFlow() const { sessionData->halfReset(sourceId); } // If there is a Transfer-Encoding header, see if the last of the encoded values is "chunked". - else if ( (headerValueNorm[HEAD_TRANSFER_ENCODING].length > 0) && - ((*(int64_t *)(headerValueNorm[HEAD_TRANSFER_ENCODING].start + (headerValueNorm[HEAD_TRANSFER_ENCODING].length - 8))) == TRANSCODE_CHUNKED) ) { + else if ( (headerNorms[HEAD_TRANSFER_ENCODING]->normalize(HEAD_TRANSFER_ENCODING, scratchPad, infractions, + headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_TRANSFER_ENCODING]) > 0) && + ((*(int64_t *)(headerValueNorm[HEAD_TRANSFER_ENCODING].start + (headerValueNorm[HEAD_TRANSFER_ENCODING].length - 8))) == TRANSCODE_CHUNKED) ) { // Chunked body sessionData->typeExpected[sourceId] = SEC_CHUNKHEAD; sessionData->bodySections[sourceId] = 0; sessionData->bodyOctets[sourceId] = 0; sessionData->numChunks[sourceId] = 0; } - else if ((headerValueNorm[HEAD_CONTENT_LENGTH].length > 0) && (*(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start > 0)) { + else if ((headerNorms[HEAD_CONTENT_LENGTH]->normalize(HEAD_CONTENT_LENGTH, scratchPad, infractions, + headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_CONTENT_LENGTH]) > 0) && + (*(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start > 0)) { // Regular body sessionData->typeExpected[sourceId] = SEC_BODY; sessionData->octetsExpected[sourceId] = *(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_head.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_head.h index dbe0eb39c..bb9898c3e 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_head.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_head.h @@ -40,7 +40,7 @@ public: NHttpMsgHeader() {}; void printSection(FILE *output) const; void genEvents(); - void updateFlow() const; + void updateFlow(); }; #endif 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 1453190e9..b22a1bdd3 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc @@ -49,7 +49,7 @@ void NHttpMsgHeadShared::initSection() { headerValue[k].length = STAT_NOTCOMPUTE; } for (int k = 1; k < HEAD__MAXVALUE; k++) { - headerValueNorm[k].length = STAT_NOTCOMPUTE; + headerValueNorm[k].length = STAT_NOSOURCE; } } @@ -61,9 +61,14 @@ void NHttpMsgHeadShared::analyze() { for (int j=0; j < MAXHEADERS; j++) { if (headerName[j].length <= 0) break; deriveHeaderNameId(j); + // Mark this header field as present and therefore eligible for normalization + if (headerNameId[j] > 0) headerValueNorm[headerNameId[j]].length = STAT_NOTCOMPUTE; } +} + +void NHttpMsgHeadShared::analyzeAll() { for (int k=1; k <= numNorms; k++) { - headerNorms[k]->normalize(scratchPad, infractions, (HeaderId)k, headerNameId, headerValue, MAXHEADERS, headerValueNorm[k]); + headerNorms[k]->normalize((HeaderId)k, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[k]); } } @@ -109,18 +114,22 @@ void NHttpMsgHeadShared::parseWhole() { // Divide up the block of header fields into individual header field lines. void NHttpMsgHeadShared::parseHeaderBlock() { - if (headers.length <= 0) return; - int32_t bytesused = 0; + if (headers.length < 0) { + numHeaders = STAT_NOSOURCE; + return; + } + + int32_t bytesUsed = 0; numHeaders = 0; - while (bytesused < headers.length) { - headerLine[numHeaders].start = headers.start + bytesused; - headerLine[numHeaders].length = findCrlf(headerLine[numHeaders].start, headers.length - bytesused, true); - bytesused += headerLine[numHeaders++].length + 2; + while (bytesUsed < headers.length) { + headerLine[numHeaders].start = headers.start + bytesUsed; + headerLine[numHeaders].length = findCrlf(headerLine[numHeaders].start, headers.length - bytesUsed, true); + bytesUsed += headerLine[numHeaders++].length + 2; if (numHeaders >= MAXHEADERS) { break; } } - if (bytesused < headers.length) { + if (bytesUsed < headers.length) { infractions |= INF_TOOMANYHEADERS; } } @@ -145,7 +154,6 @@ void NHttpMsgHeadShared::parseHeaderLines() { } void NHttpMsgHeadShared::deriveHeaderNameId(int index) { - if (headerName[index].length <= 0) return; // Normalize header field name to lower case for matching purposes uint8_t *lowerName; if ((lowerName = scratchPad.request(headerName[index].length)) == nullptr) { @@ -162,7 +170,7 @@ void NHttpMsgHeadShared::genEvents() { } // Legacy support function. Puts message fields into the buffers used by old Snort. -void NHttpMsgHeadShared::legacyClients() const { +void NHttpMsgHeadShared::legacyClients() { ClearHttpBuffers(); if (headers.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_HEADER, headers.start, (unsigned)headers.length); @@ -175,21 +183,27 @@ void NHttpMsgHeadShared::legacyClients() const { } } - if ((sourceId == SRC_CLIENT) && (headerValueNorm[HEAD_COOKIE].length > 0)) - SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_COOKIE].start, (unsigned)headerValueNorm[HEAD_COOKIE].length); - else if ((sourceId == SRC_SERVER) && (headerValueNorm[HEAD_SET_COOKIE].length > 0)) - SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_SET_COOKIE].start, (unsigned)headerValueNorm[HEAD_SET_COOKIE].length); + if (sourceId == SRC_CLIENT) { + if (headerNorms[HEAD_COOKIE]->normalize(HEAD_COOKIE, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_COOKIE]) > 0) { + SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_COOKIE].start, (unsigned)headerValueNorm[HEAD_COOKIE].length); + } + } + else { + if (headerNorms[HEAD_SET_COOKIE]->normalize(HEAD_SET_COOKIE, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_SET_COOKIE]) > 0) { + SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_SET_COOKIE].start, (unsigned)headerValueNorm[HEAD_SET_COOKIE].length); + } + } } void NHttpMsgHeadShared::printHeaders(FILE *output) const { char titleBuf[100]; - if (numHeaders != STAT_NOTCOMPUTE) fprintf(output, "Number of headers: %d\n", numHeaders); + if (numHeaders != STAT_NOSOURCE) fprintf(output, "Number of headers: %d\n", numHeaders); for (int j=0; j < numHeaders && j < 200; j++) { snprintf(titleBuf, sizeof(titleBuf), "Header ID %d", headerNameId[j]); printInterval(output, titleBuf, headerValue[j].start, headerValue[j].length); } for (int k=1; k <= numNorms; k++) { - if (headerValueNorm[k].length != STAT_NOTPRESENT) { + if (headerValueNorm[k].length != STAT_NOSOURCE) { snprintf(titleBuf, sizeof(titleBuf), "Normalized header %d", k); printInterval(output, titleBuf, headerValueNorm[k].start, headerValueNorm[k].length, true); } 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 67c4f5b8f..155015e82 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h @@ -41,8 +41,9 @@ class NHttpMsgHeadShared: public NHttpMsgSection { public: void initSection(); void analyze(); + void analyzeAll(); void genEvents(); - void legacyClients() const; + void legacyClients(); protected: // Header normalization. There should be one of these for every different way we can process a header field value. diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc index dcb8bc482..df05d1093 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc @@ -39,48 +39,12 @@ using namespace NHttpEnums; -const UriNormalizer NHttpMsgRequest::uriNoPath { false }; -const UriNormalizer NHttpMsgRequest::uriPath { true }; - // Reinitialize everything derived in preparation for analyzing a new message void NHttpMsgRequest::initSection() { NHttpMsgStart::initSection(); method.length = STAT_NOTCOMPUTE; - uri.length = STAT_NOTCOMPUTE; - uriLegacyNorm.length = STAT_NOTCOMPUTE; - uriType = URI__NOTCOMPUTE; - scheme.length = STAT_NOTCOMPUTE; - schemeId = SCH__NOTCOMPUTE; - host.length = STAT_NOTCOMPUTE; - hostInfractions = 0; - hostNorm.length = STAT_NOTCOMPUTE; - port.length = STAT_NOTCOMPUTE; - portValue = STAT_NOTCOMPUTE; - path.length = STAT_NOTCOMPUTE; - pathInfractions = 0; - pathNorm.length = STAT_NOTCOMPUTE; - query.length = STAT_NOTCOMPUTE; - queryInfractions = 0; - queryNorm.length = STAT_NOTCOMPUTE; - fragment.length = STAT_NOTCOMPUTE; - fragmentInfractions = 0; - fragmentNorm.length = STAT_NOTCOMPUTE; -} - -// All the processing that is done for every message (i.e. not just-in-time) is done here. -void NHttpMsgRequest::analyze() { - NHttpMsgStart::analyze(); - deriveMethodId(); - parseUri(); - deriveSchemeId(); - parseAuthority(); - derivePortValue(); - parseAbsPath(); - uriPath.normalize(path, pathNorm, scratchPad, pathInfractions); - uriNoPath.normalize(host, hostNorm, scratchPad, hostInfractions); - uriNoPath.normalize(query, queryNorm, scratchPad, queryInfractions); - uriNoPath.normalize(fragment, fragmentNorm, scratchPad, fragmentInfractions); - makeLegacyNormUri(); + delete uri; + uri = nullptr; } void NHttpMsgRequest::parseStartLine() { @@ -105,137 +69,19 @@ void NHttpMsgRequest::parseStartLine() { method.start = startLine.start; method.length = space; - uri.start = startLine.start + method.length + 1; - uri.length = startLine.length - method.length - 10; + deriveMethodId(); + uri = new NHttpUri(startLine.start + method.length + 1, startLine.length - method.length - 10, methodId); version.start = startLine.start + (startLine.length - 8); version.length = 8; - assert (startLine.length == method.length + uri.length + version.length + 2); + assert (startLine.length == method.length + uri->getUri().length + version.length + 2); } void NHttpMsgRequest::deriveMethodId() { - methodId = (MethodId) strToCode(method.start, method.length, methodList); -} - -void NHttpMsgRequest::parseUri() { - if (uriType != URI__NOTCOMPUTE) return; - if (uri.length <= 0) { - uriType = URI__NOTPRESENT; + if (method.length <= 0) { + methodId = METH__NOSOURCE; return; } - - // Four basic types of HTTP URI - // "*" means request does not apply to any specific resource - if ((uri.length == 1) && (uri.start[0] == '*')) { - uriType = URI_ASTERISK; - scheme.length = STAT_NOTPRESENT; - authority.length = STAT_NOTPRESENT; - absPath.length = STAT_NOTPRESENT; - } - // CONNECT method uses an authority - else if (methodId == METH_CONNECT) { - uriType = URI_AUTHORITY; - scheme.length = STAT_NOTPRESENT; - authority.length = uri.length; - authority.start = uri.start; - absPath.length = STAT_NOTPRESENT; - } - // Absolute path is a path but no scheme or authority - else if (uri.start[0] == '/') { - uriType = URI_ABSPATH; - scheme.length = STAT_NOTPRESENT; - authority.length = STAT_NOTPRESENT; - absPath.length = uri.length; - absPath.start = uri.start; - } - // Absolute URI includes scheme, authority, and path - else { - // Find the "://" and then the "/" - int j; - int k; - for (j = 0; (uri.start[j] != ':') && (j < uri.length); j++); - for (k = j+3; (uri.start[k] != '/') && (k < uri.length); k++); - if ((k < uri.length) && (uri.start[j+1] == '/') && (uri.start[j+2] == '/')) { - uriType = URI_ABSOLUTE; - scheme.length = j; - scheme.start = uri.start; - authority.length = k - j - 3; - authority.start = uri.start + j + 3; - absPath.length = uri.length - k; - absPath.start = uri.start + k; - } - else { - infractions |= INF_BADURI; - uriType = URI__PROBLEMATIC; - scheme.length = STAT_PROBLEMATIC; - authority.length = STAT_PROBLEMATIC; - absPath.length = STAT_PROBLEMATIC; - } - } -} - -void NHttpMsgRequest::deriveSchemeId() { - if (schemeId != SCH__NOTCOMPUTE) return; - if (scheme.length <= 0) return; - - // Normalize scheme name to lower case for matching purposes - uint8_t *lowerScheme; - if ((lowerScheme = scratchPad.request(scheme.length)) == nullptr) { - infractions |= INF_NOSCRATCH; - schemeId = SCH__INSUFMEMORY; - return; - } - norm2Lower(scheme.start, scheme.length, lowerScheme, infractions, nullptr); - schemeId = (SchemeId) strToCode(lowerScheme, scheme.length, schemeList); -} - -void NHttpMsgRequest::parseAuthority() { - if (host.length != STAT_NOTCOMPUTE) return; - if (authority.length <= 0) return; - host.start = authority.start; - for (host.length = 0; (authority.start[host.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; -} - -void NHttpMsgRequest::derivePortValue() { - if (portValue != SCH__NOTCOMPUTE) return; - if (port.length <= 0) return; - portValue = 0; - for (int k = 0; k < port.length; k++) { - portValue = portValue * 10 + (port.start[k] - '0'); - if ((port.start[k] < '0') || (port.start[k] > '9') || (portValue > 65535)) - { - infractions |= INF_BADPORT; - portValue = STAT_PROBLEMATIC; - break; - } - } -} - -void NHttpMsgRequest::parseAbsPath() { - if (path.length != STAT_NOTCOMPUTE) return; - if (absPath.length <= 0) return; - path.start = absPath.start; - for (path.length = 0; (absPath.start[path.length] != '?') && (absPath.start[path.length] != '#') && (path.length < absPath.length); path.length++); - if (path.length == absPath.length) { - query.length = STAT_NOTPRESENT; - fragment.length = STAT_NOTPRESENT; - return; - } - if (absPath.start[path.length] == '?') { - query.start = absPath.start + path.length + 1; - for (query.length = 0; (query.start[query.length] != '#') && (query.length < absPath.length - path.length - 1); query.length++); - fragment.start = query.start + query.length + 1; - fragment.length = absPath.length - path.length - 1 - query.length - 1; - } - else { - query.length = STAT_NOTPRESENT; - fragment.start = absPath.start + path.length + 1; - fragment.length = absPath.length - path.length - 1; - } + methodId = (MethodId) strToCode(method.start, method.length, methodList); } void NHttpMsgRequest::genEvents() { @@ -244,30 +90,31 @@ void NHttpMsgRequest::genEvents() { void NHttpMsgRequest::printSection(FILE *output) const { NHttpMsgSection::printMessageTitle(output, "request line"); - if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId); - if (methodId != METH__NOTCOMPUTE) fprintf(output, "Method Id: %d\n", methodId); - printInterval(output, "URI", uri.start, uri.length); - if (uriType != URI__NOTCOMPUTE) fprintf(output, "URI Type: %d\n", uriType); - printInterval(output, "Scheme", scheme.start, scheme.length); - if (schemeId != SCH__NOTCOMPUTE) fprintf(output, "Scheme Id: %d\n", schemeId); - printInterval(output, "Authority", authority.start, authority.length); - printInterval(output, "Host Name", host.start, host.length); - printInterval(output, "Normalized Host Name", hostNorm.start, hostNorm.length); - printInterval(output, "Port", port.start, port.length); - if (portValue != STAT_NOTCOMPUTE) fprintf(output, "Port Value: %d\n", portValue); - printInterval(output, "Absolute Path", absPath.start, absPath.length); - printInterval(output, "Path", path.start, path.length); - printInterval(output, "Normalized Path", pathNorm.start, pathNorm.length); - printInterval(output, "Query", query.start, query.length); - printInterval(output, "Normalized Query", queryNorm.start, queryNorm.length); - printInterval(output, "Fragment", fragment.start, fragment.length); - printInterval(output, "Normalized Fragment", fragmentNorm.start, fragmentNorm.length); - fprintf(output, "URI infractions: host %" PRIx64 ", path %" PRIx64 ", query %" PRIx64 ", fragment %" PRIx64 "\n", - hostInfractions, pathInfractions, queryInfractions, fragmentInfractions); + fprintf(output, "Version Id: %d\n", versionId); + fprintf(output, "Method Id: %d\n", methodId); + printInterval(output, "URI", uri->getUri().start, uri->getUri().length); + if ((uri->getUriType() != URI__NOTCOMPUTE) && (uri->getUriType() != URI__NOSOURCE)) fprintf(output, "URI Type: %d\n", uri->getUriType()); + printInterval(output, "Scheme", uri->getScheme().start, uri->getScheme().length); + if ((uri->getSchemeId() != SCH__NOTCOMPUTE) && (uri->getSchemeId() != SCH__NOSOURCE)) fprintf(output, "Scheme Id: %d\n", uri->getSchemeId()); + printInterval(output, "Authority", uri->getAuthority().start, uri->getAuthority().length); + printInterval(output, "Host Name", uri->getHost().start, uri->getHost().length); + printInterval(output, "Normalized Host Name", uri->getNormHost().start, uri->getNormHost().length); + printInterval(output, "Port", uri->getPort().start, uri->getPort().length); + if ((uri->getPortValue() != STAT_NOTCOMPUTE) && (uri->getPortValue() != STAT_NOSOURCE)) fprintf(output, "Port Value: %d\n", uri->getPortValue()); + printInterval(output, "Absolute Path", uri->getAbsPath().start, uri->getAbsPath().length); + printInterval(output, "Path", uri->getPath().start, uri->getPath().length); + printInterval(output, "Normalized Path", uri->getNormPath().start, uri->getNormPath().length); + printInterval(output, "Query", uri->getQuery().start, uri->getQuery().length); + printInterval(output, "Normalized Query", uri->getNormQuery().start, uri->getNormQuery().length); + printInterval(output, "Fragment", uri->getFragment().start, uri->getFragment().length); + printInterval(output, "Normalized Fragment", uri->getNormFragment().start, uri->getNormFragment().length); + fprintf(output, "URI infractions: overall %" PRIx64 ", host %" PRIx64 ", path %" PRIx64 ", query %" PRIx64 ", fragment %" PRIx64 "\n", + uri->getUriInfractions(), uri->getHostInfractions(), uri->getPathInfractions(), uri->getQueryInfractions(), + uri->getFragmentInfractions()); NHttpMsgSection::printMessageWrapup(output); -} + } -void NHttpMsgRequest::updateFlow() const { +void NHttpMsgRequest::updateFlow() { const uint64_t disasterMask = INF_BADREQLINE; // The following logic to determine body type is by no means the last word on this topic. @@ -283,77 +130,15 @@ void NHttpMsgRequest::updateFlow() const { sessionData->typeExpected[sourceId] = SEC_HEADER; sessionData->versionId[sourceId] = versionId; sessionData->methodId[sourceId] = methodId; - sessionData->schemeId[sourceId] = schemeId; - } -} - -// Glue normalized URI fields back together -void NHttpMsgRequest::makeLegacyNormUri() { - if (uriLegacyNorm.length != STAT_NOTCOMPUTE) return; - - // We can reuse the raw URI for the normalized URI unless at least one part of the URI has been normalized - if ((hostInfractions == 0) && (pathInfractions == 0) && (queryInfractions == 0) && (fragmentInfractions == 0)) { - uriLegacyNorm.start = uri.start; - uriLegacyNorm.length = uri.length; - return; - } - - // Glue normalized path pieces back together - const uint32_t totalLength = ((scheme.length >= 0) ? scheme.length + 3 : 0) + - ((hostNorm.length >= 0) ? hostNorm.length : 0) + - ((port.length >= 0) ? port.length + 1 : 0) + - ((pathNorm.length >= 0) ? pathNorm.length : 0) + - ((queryNorm.length >= 0) ? queryNorm.length + 1 : 0) + - ((fragmentNorm.length >= 0) ? fragmentNorm.length + 1 : 0); - uint8_t* const scratch = scratchPad.request(totalLength); - if (scratch != nullptr) { - uint8_t *current = scratch; - if (scheme.length >= 0) { - memcpy(current, scheme.start, scheme.length); - current += scheme.length; - memcpy(current, "://", 3); - current += 3; - } - if (hostNorm.length >= 0) { - memcpy(current, hostNorm.start, hostNorm.length); - current += hostNorm.length; - } - if (port.length >= 0) { - memcpy(current, ":", 1); - current += 1; - memcpy(current, port.start, port.length); - current += port.length; - } - if (pathNorm.length >= 0) { - memcpy(current, pathNorm.start, pathNorm.length); - current += pathNorm.length; - } - if (queryNorm.length >= 0) { - memcpy(current, "?", 1); - current += 1; - memcpy(current, queryNorm.start, queryNorm.length); - current += queryNorm.length; - } - if (fragmentNorm.length >= 0) { - memcpy(current, "#", 1); - current += 1; - memcpy(current, fragmentNorm.start, fragmentNorm.length); - current += fragmentNorm.length; - } - assert(totalLength == current - scratch); - scratchPad.commit(current - scratch); - uriLegacyNorm.start = scratch; - uriLegacyNorm.length = current - scratch; } - else uriLegacyNorm.length = STAT_INSUFMEMORY; } // Legacy support function. Puts message fields into the buffers used by old Snort. -void NHttpMsgRequest::legacyClients() const { +void NHttpMsgRequest::legacyClients() { ClearHttpBuffers(); if (method.length > 0) SetHttpBuffer(HTTP_BUFFER_METHOD, method.start, (unsigned)method.length); - if (uri.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_URI, uri.start, (unsigned)uri.length); - if (uriLegacyNorm.length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uriLegacyNorm.start, (unsigned)uriLegacyNorm.length); + if (uri->getUri().length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_URI, uri->getUri().start, (unsigned)uri->getUri().length); + if (uri->getNormLegacy().length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uri->getNormLegacy().start, (unsigned)uri->getNormLegacy().length); } diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h index 409ce2d19..db3968622 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h @@ -30,6 +30,7 @@ #define NHTTP_MSG_REQUEST_H #include "nhttp_str_to_code.h" +#include "nhttp_uri.h" #include "nhttp_uri_norm.h" #include "nhttp_msg_start.h" @@ -40,61 +41,27 @@ class NHttpMsgRequest: public NHttpMsgStart { public: NHttpMsgRequest() {}; + ~NHttpMsgRequest() { delete uri; }; void initSection(); - void analyze(); void printSection(FILE *output) const; void genEvents(); - void updateFlow() const; - void legacyClients() const; + void updateFlow(); + void legacyClients(); private: // Code conversion tables are for turning token strings into enums. static const StrCode methodList[]; - static const StrCode schemeList[]; - // URI normalization strategy objects - static const UriNormalizer uriNoPath; - static const UriNormalizer uriPath; - - // "Parse" methods cut things into pieces. "Derive" methods convert things into a new format such as an integer or enum token. "Normalize" methods convert - // things into a standard form without changing the underlying format. + // "Parse" methods cut things into pieces. "Extract" methods find the named item. "Derive" methods convert things into a new format + // such as an integer or enum token. "Normalize" methods convert things into a standard form without changing the underlying format. void parseStartLine(); void deriveMethodId(); - void parseUri(); - void deriveSchemeId(); - void parseAuthority(); - void derivePortValue(); - void parseAbsPath(); - void makeLegacyNormUri(); // This is where all the derived values, extracted message parts, and normalized values are. // Note that these are all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the // scratchPad (normalized pieces). field method; - - // URI stuff - field uri; - field scheme; - field authority; - field host; - field port; - field absPath; - field path; - field query; - field fragment; - - uint64_t hostInfractions; - uint64_t pathInfractions; - uint64_t queryInfractions; - uint64_t fragmentInfractions; - - NHttpEnums::UriType uriType; - field hostNorm; - int32_t portValue; - field pathNorm; - field queryNorm; - field fragmentNorm; - field uriLegacyNorm; + NHttpUri* uri = nullptr; }; #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc index 6945c0df6..7071aa690 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc @@ -60,14 +60,13 @@ void NHttpMsgSection::loadSection(const uint8_t *buffer, const uint16_t bufsize, tcpClose = sessionData->tcpClose; versionId = sessionData->versionId[sourceId]; methodId = sessionData->methodId[sourceId]; - schemeId = sessionData->schemeId[sourceId]; statusCodeNum = sessionData->statusCodeNum[sourceId]; scratchPad.reinit(); } void NHttpMsgSection::printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals) { - if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE)) return; + if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE) || (length == STAT_NOSOURCE)) return; int outCount = fprintf(output, "%s, length = %d, ", name, length); if (length <= 0) { fprintf(output, "\n"); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h index c36c50046..19c378255 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h @@ -42,11 +42,12 @@ public: virtual void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_); virtual ~NHttpMsgSection() = default; virtual void initSection() = 0; - virtual void analyze() = 0; + virtual void analyze() = 0; // Minimum necessary processing for every message + virtual void analyzeAll() {}; // Force all just-in-time processing (testing method) virtual void printSection(FILE *output) const = 0; virtual void genEvents() = 0; - virtual void updateFlow() const = 0; - virtual void legacyClients() const = 0; + virtual void updateFlow() = 0; + virtual void legacyClients() = 0; protected: // Convenience methods @@ -63,10 +64,8 @@ protected: // original message should not be changing it. Only loading a completely new message into rawBuf should do that. const uint8_t * const msgText = rawBuf; - // Working space and storage for all the derived fields. See scratchPad.h for usage instructions. - uint64_t derivedBuf[NHttpEnums::MAXOCTETS/4]; NHttpFlowData* sessionData; - ScratchPad scratchPad {derivedBuf, NHttpEnums::MAXOCTETS/4}; + ScratchPad scratchPad {NHttpEnums::MAXOCTETS*2}; // This is where all the derived values, extracted message parts, and normalized values are. // Note that these are all scalars, buffer pointers, and buffer sizes. The actual buffers are in message buffer (raw pieces) or the @@ -76,7 +75,6 @@ protected: NHttpEnums::SourceId sourceId; NHttpEnums::VersionId versionId; NHttpEnums::MethodId methodId; - NHttpEnums::SchemeId schemeId; int32_t statusCodeNum; }; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc index 2a9f7ca0f..cdcdf445a 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc @@ -44,7 +44,7 @@ void NHttpMsgStart::initSection() { version.length = STAT_NOTCOMPUTE; } -// All the processing that is done for every message (i.e. not just-in-time) is done here. +// Required message processing that is automatically done instead of being just-in-time void NHttpMsgStart::analyze() { startLine.start = msgText; startLine.length = findCrlf(startLine.start, length, false); @@ -55,10 +55,16 @@ void NHttpMsgStart::analyze() { } void NHttpMsgStart::deriveVersionId() { + if (version.length <= 0) { + versionId = VERS__NOSOURCE; + return; + } if (version.length != 8) { versionId = VERS__PROBLEMATIC; + infractions |= INF_BADVERSION; return; } + if (memcmp(version.start, "HTTP/", 5) || (version.start[6] != '.')) { versionId = VERS__PROBLEMATIC; 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 d3446458c..48f3368e0 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc @@ -75,10 +75,15 @@ void NHttpMsgStatus::parseStartLine() { } void NHttpMsgStatus::deriveStatusCodeNum() { + if (statusCode.length <= 0) { + statusCodeNum = STAT_NOSOURCE; + return; + } if (statusCode.length != 3) { statusCodeNum = STAT_PROBLEMATIC; return; } + if ((statusCode.start[0] < '0') || (statusCode.start[0] > '9') || (statusCode.start[1] < '0') || (statusCode.start[1] > '9') || (statusCode.start[2] < '0') || (statusCode.start[2] > '9')) { infractions |= INF_BADSTATCODE; @@ -97,13 +102,13 @@ void NHttpMsgStatus::genEvents() { void NHttpMsgStatus::printSection(FILE *output) const { NHttpMsgSection::printMessageTitle(output, "status line"); - if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId); - if (statusCodeNum != STAT_NOTCOMPUTE) fprintf(output, "Status Code Num: %d\n", statusCodeNum); + fprintf(output, "Version Id: %d\n", versionId); + fprintf(output, "Status Code Num: %d\n", statusCodeNum); printInterval(output, "Reason Phrase", reasonPhrase.start, reasonPhrase.length); NHttpMsgSection::printMessageWrapup(output); } -void NHttpMsgStatus::updateFlow() const { +void NHttpMsgStatus::updateFlow() { const uint64_t disasterMask = INF_BADSTATLINE; // The following logic to determine body type is by no means the last word on this topic. @@ -123,7 +128,7 @@ void NHttpMsgStatus::updateFlow() const { } // Legacy support function. Puts message fields into the buffers used by old Snort. -void NHttpMsgStatus::legacyClients() const { +void NHttpMsgStatus::legacyClients() { ClearHttpBuffers(); if (statusCode.length > 0) SetHttpBuffer(HTTP_BUFFER_STAT_CODE, statusCode.start, (unsigned)statusCode.length); if (reasonPhrase.length > 0) SetHttpBuffer(HTTP_BUFFER_STAT_MSG, reasonPhrase.start, (unsigned)reasonPhrase.length); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_status.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_status.h index f2a968f96..597332d72 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_status.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_status.h @@ -42,8 +42,8 @@ public: void analyze(); void printSection(FILE *output) const; void genEvents(); - void updateFlow() const; - void legacyClients() const; + void updateFlow(); + void legacyClients(); private: // "Parse" methods cut things into pieces. "Derive" methods convert things into a new format such as an integer or enum token. "Normalize" methods convert diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc index 4ac75f8df..049451a64 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc @@ -48,7 +48,7 @@ void NHttpMsgTrailer::printSection(FILE *output) const { } -void NHttpMsgTrailer::updateFlow() const { +void NHttpMsgTrailer::updateFlow() { if (tcpClose) { sessionData->typeExpected[sourceId] = SEC_CLOSED; sessionData->halfReset(sourceId); diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.h index 3d02059e6..6c5101dd3 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.h @@ -40,7 +40,7 @@ public: NHttpMsgTrailer() {}; void printSection(FILE *output) const; void genEvents(); - void updateFlow() const; + void updateFlow(); }; #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_scratch_pad.h b/src/service_inspectors/nhttp_inspect/nhttp_scratch_pad.h index 76e324321..4ad6cb074 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_scratch_pad.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_scratch_pad.h @@ -44,15 +44,16 @@ class ScratchPad { public: - ScratchPad(uint64_t *buff, uint32_t length) : buffer(buff), capacity(length*8), used(0) {}; // Careful: length must be number of uint64_ts provided, not octets. + ScratchPad(uint32_t _capacity) : capacity(_capacity), buffer(new uint64_t[_capacity/8+1]) {}; + ~ScratchPad() { delete[] buffer; }; void reinit() {used = 0;}; uint8_t *request(uint32_t needed) const {return (needed <= capacity-used) ? (uint8_t*)(buffer+used) : nullptr;}; void commit(uint32_t taken) { used += taken + (8-(taken%8))%8; }; // round up to multiple of 8 to preserve alignment private: - uint64_t *buffer; uint32_t capacity; - uint32_t used; + uint64_t *buffer; + uint32_t used = 0; }; #endif diff --git a/src/service_inspectors/nhttp_inspect/nhttp_str_to_code.cc b/src/service_inspectors/nhttp_inspect/nhttp_str_to_code.cc index c8c33373f..c654c655c 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_str_to_code.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_str_to_code.cc @@ -36,7 +36,6 @@ // Need to replace this simple algorithm for better performance int32_t strToCode(const uint8_t *text, int32_t textLen, const StrCode table[]) { - if (textLen <= 0) return NHttpEnums::STAT_PROBLEMATIC; for (int32_t k=0; table[k].name != nullptr; k++) { if ((textLen == (int) strlen(table[k].name)) && (memcmp(text, table[k].name, textLen) == 0)) { return table[k].code; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_tables.cc b/src/service_inspectors/nhttp_inspect/nhttp_tables.cc index 0af32ee4c..fdad4c2d5 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_tables.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_tables.cc @@ -97,7 +97,7 @@ const StrCode NHttpMsgRequest::methodList[] = { METH_UPDATEREDIRECTREF, "UPDATEREDIRECTREF"}, { 0, nullptr} }; -const StrCode NHttpMsgRequest::schemeList[] = +const StrCode NHttpUri::schemeList[] = {{ SCH_HTTP, "http"}, { SCH_HTTPS, "https"}, { SCH_FTP, "ftp"}, @@ -155,6 +155,8 @@ const StrCode NHttpMsgHeadShared::headerList[] = { HEAD_CONTENT_TYPE, "content-type"}, { HEAD_EXPIRES, "expires"}, { HEAD_LAST_MODIFIED, "last-modified"}, + { HEAD_X_FORWARDED_FOR, "x-forwarded-for"}, + { HEAD_TRUE_CLIENT_IP, "true-client-ip"}, { 0, nullptr} }; const StrCode NHttpMsgHeadShared::transCodeList[] = @@ -222,7 +224,9 @@ const HeaderNormalizer* const NHttpMsgHeadShared::headerNorms[HEAD__MAXVALUE] = [HEAD_CONTENT_RANGE] = &NORMALIZER_BASIC, [HEAD_CONTENT_TYPE] = &NORMALIZER_BASIC, [HEAD_EXPIRES] = &NORMALIZER_BASIC, - [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC + [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC, + [HEAD_X_FORWARDED_FOR] = &NORMALIZER_CAT, + [HEAD_TRUE_CLIENT_IP] = &NORMALIZER_BASIC }; const int32_t NHttpMsgHeadShared::numNorms = HEAD__MAXVALUE-1; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri.cc b/src/service_inspectors/nhttp_inspect/nhttp_uri.cc new file mode 100644 index 000000000..a6b260d96 --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri.cc @@ -0,0 +1,291 @@ +/**************************************************************************** + * +** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved. + * Copyright (C) 2003-2013 Sourcefire, Inc. + * + * 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. + * + ****************************************************************************/ + +// +// @author Tom Peters +// +// @brief NHttpUri class +// + + +#include +#include +#include +#include + +#include "snort.h" +#include "nhttp_enum.h" +#include "nhttp_normalizers.h" +#include "nhttp_uri.h" + +using namespace NHttpEnums; + +void NHttpUri::parseUri() { + if (uriType != URI__NOTCOMPUTE) return; + if (uri.length <= 0) { + uriType = URI__NOSOURCE; + scheme.length = STAT_NOSOURCE; + authority.length = STAT_NOSOURCE; + absPath.length = STAT_NOSOURCE; + return; + } + + // Four basic types of HTTP URI + // "*" means request does not apply to any specific resource + if ((uri.length == 1) && (uri.start[0] == '*')) { + uriType = URI_ASTERISK; + scheme.length = STAT_NOTPRESENT; + authority.length = STAT_NOTPRESENT; + absPath.length = STAT_NOTPRESENT; + } + // CONNECT method uses an authority + else if (methodId == METH_CONNECT) { + uriType = URI_AUTHORITY; + scheme.length = STAT_NOTPRESENT; + authority.length = uri.length; + authority.start = uri.start; + absPath.length = STAT_NOTPRESENT; + } + // Absolute path is a path but no scheme or authority + else if (uri.start[0] == '/') { + uriType = URI_ABSPATH; + scheme.length = STAT_NOTPRESENT; + authority.length = STAT_NOTPRESENT; + absPath.length = uri.length; + absPath.start = uri.start; + } + // Absolute URI includes scheme, authority, and path + else { + // Find the "://" and then the "/" + int j; + int k; + for (j = 0; (uri.start[j] != ':') && (j < uri.length); j++); + for (k = j+3; (uri.start[k] != '/') && (k < uri.length); k++); + if ((k < uri.length) && (uri.start[j+1] == '/') && (uri.start[j+2] == '/')) { + uriType = URI_ABSOLUTE; + scheme.length = j; + scheme.start = uri.start; + authority.length = k - j - 3; + authority.start = uri.start + j + 3; + absPath.length = uri.length - k; + absPath.start = uri.start + k; + } + else { + uriInfractions |= INF_BADURI; + uriType = URI__PROBLEMATIC; + scheme.length = STAT_PROBLEMATIC; + authority.length = STAT_PROBLEMATIC; + absPath.length = STAT_PROBLEMATIC; + } + } +} + +SchemeId NHttpUri::getSchemeId() { + if (schemeId != SCH__NOTCOMPUTE) return schemeId; + if (getScheme().length <= 0) { + schemeId = SCH__NOSOURCE; + return schemeId; + } + + // Normalize scheme name to lower case for matching purposes + uint8_t *lowerScheme; + if ((lowerScheme = scratchPad.request(scheme.length)) == nullptr) { + uriInfractions |= INF_NOSCRATCH; + schemeId = SCH__INSUFMEMORY; + return schemeId; + } + norm2Lower(scheme.start, scheme.length, lowerScheme, uriInfractions, nullptr); + schemeId = (SchemeId) strToCode(lowerScheme, scheme.length, schemeList); + return schemeId; +} + +field NHttpUri::getNormHost() { + if (hostNorm.length != STAT_NOTCOMPUTE) return hostNorm; + if (getHost().length < 0) { + hostNorm.length = STAT_NOSOURCE; + return hostNorm; + } + UriNormalizer::normalize(host, hostNorm, false, scratchPad, hostInfractions); + return hostNorm; +} + +field NHttpUri::getNormPath() { + if (pathNorm.length != STAT_NOTCOMPUTE) return pathNorm; + if (getPath().length < 0) { + pathNorm.length = STAT_NOSOURCE; + return pathNorm; + } + UriNormalizer::normalize(path, pathNorm, true, scratchPad, pathInfractions); + return pathNorm; +} + +field NHttpUri::getNormQuery() { + if (queryNorm.length != STAT_NOTCOMPUTE) return queryNorm; + if (getQuery().length < 0) { + queryNorm.length = STAT_NOSOURCE; + return queryNorm; + } + UriNormalizer::normalize(query, queryNorm, true, scratchPad, queryInfractions); + return queryNorm; +} + +field NHttpUri::getNormFragment() { + if (fragmentNorm.length != STAT_NOTCOMPUTE) return fragmentNorm; + if (getFragment().length < 0) { + fragmentNorm.length = STAT_NOSOURCE; + return fragmentNorm; + } + UriNormalizer::normalize(fragment, fragmentNorm, true, scratchPad, fragmentInfractions); + return fragmentNorm; +} + +int32_t NHttpUri::getPortValue() { + if (portValue != STAT_NOTCOMPUTE) return portValue; + if (getPort().length <= 0) { + portValue = STAT_NOSOURCE; + return portValue; + } + portValue = 0; + for (int k = 0; k < port.length; k++) { + portValue = portValue * 10 + (port.start[k] - '0'); + if ((port.start[k] < '0') || (port.start[k] > '9') || (portValue > 65535)) + { + uriInfractions |= INF_BADPORT; + portValue = STAT_PROBLEMATIC; + break; + } + } + return portValue; +} + +void NHttpUri::parseAuthority() { + if (host.length != STAT_NOTCOMPUTE) return; + if (getAuthority().length <= 0) { + host.length = STAT_NOSOURCE; + port.length = STAT_NOSOURCE; + return; + } + host.start = authority.start; + for (host.length = 0; (authority.start[host.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; +} + +void NHttpUri::parseAbsPath() { + if (path.length != STAT_NOTCOMPUTE) return; + if (getAbsPath().length <= 0) { + path.length = STAT_NOSOURCE; + query.length = STAT_NOSOURCE; + fragment.length = STAT_NOSOURCE; + return; + } + path.start = absPath.start; + for (path.length = 0; (absPath.start[path.length] != '?') && (absPath.start[path.length] != '#') && (path.length < absPath.length); path.length++); + if (path.length == absPath.length) { + query.length = STAT_NOTPRESENT; + fragment.length = STAT_NOTPRESENT; + return; + } + if (absPath.start[path.length] == '?') { + query.start = absPath.start + path.length + 1; + for (query.length = 0; (query.start[query.length] != '#') && (query.length < absPath.length - path.length - 1); query.length++); + fragment.start = query.start + query.length + 1; + fragment.length = absPath.length - path.length - 1 - query.length - 1; + } + else { + query.length = STAT_NOTPRESENT; + fragment.start = absPath.start + path.length + 1; + fragment.length = absPath.length - path.length - 1; + } +} + +// Glue normalized URI fields back together +field NHttpUri::getNormLegacy() { + if (legacyNorm.length != STAT_NOTCOMPUTE) return legacyNorm; + + if (getPath().length >= 0) UriNormalizer::normalize(path, pathNorm, true, scratchPad, pathInfractions); + if (getHost().length >= 0) UriNormalizer::normalize(host, hostNorm, false, scratchPad, hostInfractions); + if (getQuery().length >= 0) UriNormalizer::normalize(query, queryNorm, false, scratchPad, queryInfractions); + if (getFragment().length >= 0) UriNormalizer::normalize(fragment, fragmentNorm, false, scratchPad, fragmentInfractions); + + // We can reuse the raw URI for the normalized URI unless at least one part of the URI has been normalized + if ((hostInfractions == 0) && (pathInfractions == 0) && (queryInfractions == 0) && (fragmentInfractions == 0)) { + legacyNorm.start = uri.start; + legacyNorm.length = uri.length; + return legacyNorm; + } + + // Glue normalized URI pieces back together + const uint32_t totalLength = ((scheme.length >= 0) ? scheme.length + 3 : 0) + + ((hostNorm.length >= 0) ? hostNorm.length : 0) + + ((port.length >= 0) ? port.length + 1 : 0) + + ((pathNorm.length >= 0) ? pathNorm.length : 0) + + ((queryNorm.length >= 0) ? queryNorm.length + 1 : 0) + + ((fragmentNorm.length >= 0) ? fragmentNorm.length + 1 : 0); + uint8_t* const scratch = scratchPad.request(totalLength); + if (scratch != nullptr) { + uint8_t *current = scratch; + if (scheme.length >= 0) { + memcpy(current, scheme.start, scheme.length); + current += scheme.length; + memcpy(current, "://", 3); + current += 3; + } + if (hostNorm.length >= 0) { + memcpy(current, hostNorm.start, hostNorm.length); + current += hostNorm.length; + } + if (port.length >= 0) { + memcpy(current, ":", 1); + current += 1; + memcpy(current, port.start, port.length); + current += port.length; + } + if (pathNorm.length >= 0) { + memcpy(current, pathNorm.start, pathNorm.length); + current += pathNorm.length; + } + if (queryNorm.length >= 0) { + memcpy(current, "?", 1); + current += 1; + memcpy(current, queryNorm.start, queryNorm.length); + current += queryNorm.length; + } + if (fragmentNorm.length >= 0) { + memcpy(current, "#", 1); + current += 1; + memcpy(current, fragmentNorm.start, fragmentNorm.length); + current += fragmentNorm.length; + } + assert(totalLength == current - scratch); + scratchPad.commit(current - scratch); + legacyNorm.start = scratch; + legacyNorm.length = current - scratch; + } + else legacyNorm.length = STAT_INSUFMEMORY; + return legacyNorm; +} + + diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri.h b/src/service_inspectors/nhttp_inspect/nhttp_uri.h new file mode 100644 index 000000000..181ccd2f6 --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri.h @@ -0,0 +1,112 @@ +/**************************************************************************** + * +** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved. + * Copyright (C) 2003-2013 Sourcefire, Inc. + * + * 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. + * + ****************************************************************************/ + +// +// @author Tom Peters +// +// @brief NHttpUri class declaration +// + +#ifndef NHTTP_URI_H +#define NHTTP_URI_H + +#include "nhttp_scratch_pad.h" +#include "nhttp_str_to_code.h" +#include "nhttp_uri_norm.h" + +//------------------------------------------------------------------------- +// NHttpUri class +//------------------------------------------------------------------------- + +class NHttpUri { +public: + NHttpUri(const uint8_t* start, int32_t length, NHttpEnums::MethodId method) : methodId(method) { + uri.length = length; uri.start = start; }; + + field getUri() const { return uri; }; + NHttpEnums::UriType getUriType() { parseUri(); return uriType; }; + field getScheme() { parseUri(); return scheme; }; + field getAuthority() { parseUri(); return authority; }; + field getHost() { parseAuthority(); return host; }; + field getPort() { parseAuthority(); return port; }; + field getAbsPath() { parseUri(); return absPath; }; + field getPath() { parseAbsPath(); return path; }; + field getQuery() { parseAbsPath(); return query; }; + field getFragment() { parseAbsPath(); return fragment; }; + + uint64_t getUriInfractions() const { return uriInfractions; }; + uint64_t getHostInfractions() const { return hostInfractions; }; + uint64_t getPathInfractions() const { return pathInfractions; }; + uint64_t getQueryInfractions() const { return queryInfractions; }; + uint64_t getFragmentInfractions() const { return fragmentInfractions; }; + + NHttpEnums::SchemeId getSchemeId(); + field getNormHost(); + int32_t getPortValue(); + field getNormPath(); + field getNormQuery(); + field getNormFragment(); + field getNormLegacy(); + +private: + static const StrCode schemeList[]; + + field uri; + const NHttpEnums::MethodId methodId; + + field scheme; + field authority; + field host; + field port; + field absPath; + field path; + field query; + field fragment; + + uint64_t uriInfractions = 0; + uint64_t hostInfractions = 0; + uint64_t pathInfractions = 0; + uint64_t queryInfractions = 0; + uint64_t fragmentInfractions = 0; + + NHttpEnums::UriType uriType = NHttpEnums::URI__NOTCOMPUTE; + NHttpEnums::SchemeId schemeId = NHttpEnums::SCH__NOTCOMPUTE; + field hostNorm; + int32_t portValue = NHttpEnums::STAT_NOTCOMPUTE; + field pathNorm; + field queryNorm; + field fragmentNorm; + field legacyNorm; + + void parseUri(); + void parseAuthority(); + void parseAbsPath(); + + ScratchPad scratchPad {NHttpEnums::MAXOCTETS*2}; +}; + +#endif + + + + + diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc index 3e46622bc..f94cd08bc 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc @@ -23,11 +23,10 @@ // // @author Tom Peters // -// @brief URI normalization functions +// @brief URI normalization class // -// &&&#include #include #include @@ -36,12 +35,9 @@ using namespace NHttpEnums; -void UriNormalizer::normalize(const field &input, field &result, ScratchPad &scratchPad, uint64_t &infractions) const { +void UriNormalizer::normalize(const field &input, field &result, bool doPath, ScratchPad &scratchPad, uint64_t &infractions) { if (result.length != STAT_NOTCOMPUTE) return; - if (input.length < 0) { - result.length = STAT_NOTPRESENT; - 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 @@ -78,7 +74,7 @@ void UriNormalizer::normalize(const field &input, field &result, ScratchPad &scr result.length = dataLength; } -bool UriNormalizer::noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const { +bool UriNormalizer::noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) { for (int32_t k = 0; k < inLength; k++) { if ((uriChar[inBuf[k]] == CHAR_NORMAL) || (uriChar[inBuf[k]] == CHAR_PATH)) continue; infractions |= INF_URINEEDNORM; @@ -87,7 +83,7 @@ bool UriNormalizer::noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t return true; } -bool UriNormalizer::pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const { +bool UriNormalizer::pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) { for (int32_t k = 0; k < inLength; k++) { if (uriChar[inBuf[k]] == CHAR_NORMAL) continue; if ((inBuf[k] == '/') && ((k == 0) || (inBuf[k-1] != '/'))) continue; @@ -97,7 +93,7 @@ bool UriNormalizer::pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& return true; } -int32_t UriNormalizer::normCharClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const { +int32_t UriNormalizer::normCharClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) { int32_t length = 0; for (int32_t k = 0; k < inLength; k++) { switch (uriChar[inBuf[k]]) { @@ -154,7 +150,7 @@ int32_t UriNormalizer::normCharClean(const uint8_t* inBuf, int32_t inLength, uin } // Convert URI backslashes to slashes -int32_t UriNormalizer::normBackSlash(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const { +int32_t UriNormalizer::normBackSlash(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) { for (int32_t k = 0; k < inLength; k++) { if (inBuf[k] != '\\') outBuf[k] = inBuf[k]; else { @@ -166,7 +162,7 @@ int32_t UriNormalizer::normBackSlash(const uint8_t* inBuf, int32_t inLength, uin } // Caution: worst case output length is one greater than input length -int32_t UriNormalizer::normPathClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const { +int32_t UriNormalizer::normPathClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& 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 .. diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h index 860ce218d..595cd7ec5 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h @@ -33,22 +33,19 @@ class UriNormalizer { public: - UriNormalizer(bool doPath_) : doPath(doPath_) {}; - void normalize(const field &input, field &result, ScratchPad &scratchPad, uint64_t &infractions) const; + static void normalize(const field &input, field &result, bool doPath, ScratchPad &scratchPad, uint64_t &infractions); private: static const NHttpEnums::CharAction uriChar[256]; static const int8_t asHex[256]; static const bool goodPercent[256]; - bool noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const; - bool pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const; + static bool noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions); + static bool pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions); - int32_t normCharClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed) const; - int32_t normBackSlash(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed) const; - int32_t normPathClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed) const; - - bool doPath; + static int32_t normCharClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed); + static int32_t normBackSlash(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed); + static int32_t normPathClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed); }; #endif