src/service_inspectors/back_orifice/Makefile \
src/service_inspectors/ftp_telnet/Makefile \
src/service_inspectors/http_inspect/Makefile \
+src/service_inspectors/nhttp_inspect/Makefile \
src/service_inspectors/rpc_decode/Makefile \
src/protocols/Makefile \
src/search_engines/Makefile \
network_inspectors/port_scan/libport_scan.a \
service_inspectors/back_orifice/libback_orifice.a \
service_inspectors/ftp_telnet/libftp_telnet.a \
-service_inspectors/rpc_decode/librpc_decode.a
+service_inspectors/rpc_decode/librpc_decode.a \
+service_inspectors/nhttp_inspect/libnhttp_inspect.a
endif
# order libs to avoid undefined symbols
back_orifice \
ftp_telnet \
http_inspect \
+nhttp_inspect \
rpc_decode
AM_CXXFLAGS = @AM_CXXFLAGS@
--- /dev/null
+AUTOMAKE_OPTIONS=foreign no-dependencies
+
+file_list = \
+nhttp_inspect.cc nhttp_inspect.h \
+nhttp_msgheader.cc nhttp_msgheader.h \
+nhttp_headnorm.cc nhttp_headnorm.h \
+nhttp_strtocode.cc nhttp_strtocode.h \
+nhttp_api.cc nhttp_api.h \
+nhttp_tables.cc \
+nhttp_module.cc nhttp_module.h \
+nhttp_testinput.cc nhttp_testinput.h \
+nhttp_flowdata.cc nhttp_flowdata.h \
+nhttp_scratchpad.h nhttp_enum.h
+
+
+#if STATIC_INSPECTORS
+noinst_LIBRARIES = libnhttp_inspect.a
+libnhttp_inspect_a_SOURCES = $(file_list)
+#else
+# need to fix undefineds first :(
+#shlibdir = $(pkglibdir)/inspectors
+#shlib_LTLIBRARIES = libnhttp_inspect.la
+#libnhttp_inspect_la_CXXFLAGS = $(AM_CXXFLAGS) -DBUILDING_SO
+#libnhttp_inspect_la_LDFLAGS = -export-dynamic -shared
+#libnhttp_inspect_la_SOURCES = $(file_list)
+#endif
+
+AM_CXXFLAGS = @AM_CXXFLAGS@
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief API for NHttpInspect
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "snort.h"
+#include "framework/parameter.h"
+#include "framework/module.h"
+#include "framework/inspector.h"
+#include "flow/flow.h"
+#include "nhttp_enum.h"
+#include "nhttp_flowdata.h"
+#include "nhttp_scratchpad.h"
+#include "nhttp_module.h"
+#include "nhttp_strtocode.h"
+#include "nhttp_headnorm.h"
+#include "nhttp_msgheader.h"
+#include "nhttp_testinput.h"
+#include "nhttp_api.h"
+#include "nhttp_inspect.h"
+
+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()
+{
+ printf("nhttp_init()\n");
+ NHttpFlowData::init();
+}
+
+void NHttpApi::nhttp_term()
+{
+ printf("nhttp_term()\n");
+}
+
+Inspector* NHttpApi::nhttp_ctor(Module* mod)
+{
+ const NHttpModule* nhttpMod = (NHttpModule*) mod;
+ printf("nhttp_ctor()\n");
+ return new NHttpInspect(nhttpMod->get_test_mode());
+}
+
+void NHttpApi::nhttp_dtor(Inspector* p)
+{
+ printf("nhttp_dtor()\n");
+ delete p;
+}
+
+void NHttpApi::nhttp_pinit()
+{
+ printf("nhttp_pinit()\n");
+ NHttpInspect::msgHead = new NHttpMsgHeader;
+}
+
+void NHttpApi::nhttp_pterm()
+{
+ printf("nhttp_pterm()\n");
+ delete NHttpInspect::msgHead;
+}
+
+void NHttpApi::nhttp_purge()
+{
+ printf("nhttp_purge()\n");
+}
+
+void NHttpApi::nhttp_sum()
+{
+ printf("nhttp_sum()\n");
+}
+
+void NHttpApi::nhttp_stats()
+{
+ printf("nhttp_stats()\n");
+}
+
+void NHttpApi::nhttp_reset()
+{
+ printf("nhttp_reset()\n");
+}
+
+const InspectApi NHttpApi::nhttp_api =
+{
+ {
+ PT_INSPECTOR,
+ NHttpApi::nhttp_myName,
+ INSAPI_PLUGIN_V0,
+ 0,
+ NHttpApi::nhttp_mod_ctor,
+ NHttpApi::nhttp_mod_dtor
+ },
+ PRIORITY_APPLICATION,
+ PROTO_BIT__TCP,
+ NHttpApi::nhttp_init,
+ NHttpApi::nhttp_term,
+ NHttpApi::nhttp_ctor,
+ NHttpApi::nhttp_dtor,
+ NHttpApi::nhttp_pinit,
+ NHttpApi::nhttp_pterm,
+ NHttpApi::nhttp_purge,
+ NHttpApi::nhttp_sum,
+ NHttpApi::nhttp_stats,
+ NHttpApi::nhttp_reset
+};
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+{
+ &NHttpApi::nhttp_api.base,
+ nullptr
+};
+#else
+const BaseApi* sin_nhttp = &NHttpApi::nhttp_api.base;
+#endif
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief Noninstantiated class to collect static framework API functions and facilitate friendships.
+//
+
+#ifndef NHTTP_API_H
+#define NHTTP_API_H
+
+class NHttpApi {
+public:
+ static const InspectApi nhttp_api;
+private:
+ NHttpApi() = delete;
+ static Module* nhttp_mod_ctor();
+ static void nhttp_mod_dtor(Module* m);
+ static const char* nhttp_myName;
+ static void nhttp_init();
+ 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_purge();
+ static void nhttp_sum();
+ static void nhttp_stats();
+ static void nhttp_reset();
+};
+
+#endif
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief NHttpMsgHeader enumerations
+//
+
+#ifndef NHTTP_ENUM_H
+#define NHTTP_ENUM_H
+
+#define GID_HTTP_CLIENT 119
+#define GID_HTTP_SERVER 120
+
+namespace NHttpEnums {
+
+// 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;
+
+// Message originator--client or server
+typedef enum { SRC__NOTCOMPUTE=-4, SRC__PROBLEMATIC=-2, SRC_CLIENT=1, SRC_SERVER } SourceId;
+
+// Type of message section
+typedef enum { SEC__PROBLEMATIC=-2, SEC_HEADER = 2, SEC_BODY, SEC_CHUNK } 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;
+
+// Every request method we have ever heard of
+typedef enum { METH__NOTCOMPUTE=-4, METH__INSUFMEMORY=-3, METH__PROBLEMATIC=-2, METH__OTHER=1, 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;
+
+// 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,
+ HEAD_PRAGMA, HEAD_TRAILER, HEAD_COOKIE, HEAD_SET_COOKIE,
+ HEAD_TRANSFER_ENCODING, HEAD_UPGRADE, HEAD_VIA, HEAD_WARNING, HEAD_ACCEPT, HEAD_ACCEPT_CHARSET, HEAD_ACCEPT_ENCODING, HEAD_ACCEPT_LANGUAGE, HEAD_AUTHORIZATION,
+ HEAD_EXPECT, HEAD_FROM, HEAD_HOST, HEAD_IF_MATCH, 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;
+
+// All the infractions we might find while parsing and analyzing a message
+typedef enum { INF_TRUNCATED=0x1, /*INF_CANTFINDVERS=0x2,*/ /*INF_STARTTOOSHORT=0x4,*/ INF_BADREQLINE=0x8, INF_BADSTATLINE=0x10, INF_TOOMANYHEADERS=0x20,
+ INF_BADHEADER=0x40, INF_BADSTATCODE=0x80, INF_UNKNOWNVERSION=0x100, INF_BADVERSION=0x200, INF_NOSCRATCH=0x400, INF_BADHEADERREPS=0x800, INF_BADHEADERDATA=0x1000 } Infraction;
+
+// Formats for output from a header normalization function
+typedef enum { NORM_NULL, NORM_FIELD, NORM_INTEGER, NORM_ENUM, NORM_ENUMLIST } NormFormat;
+
+// Transfer codings
+typedef enum { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSCODE_GZIP, TRANSCODE_COMPRESS, TRANSCODE_DEFLATE } Transcoding;
+
+} // end namespace NHttpEnums
+
+// Individual pieces of the message found during parsing
+// Set length to -1 when field has no value
+typedef struct {
+ const uint8_t *start = nullptr;
+ int32_t length = -1;
+} field;
+
+#endif
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief Flow Data object used to store session information with Streams
+//
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include "snort.h"
+#include "flow/flow.h"
+#include "nhttp_enum.h"
+#include "nhttp_flowdata.h"
+
+unsigned NHttpFlowData::nhttp_flow_id = 0;
+
+NHttpFlowData::NHttpFlowData() : FlowData(nhttp_flow_id)
+{
+}
+
+NHttpFlowData::~NHttpFlowData()
+{
+}
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief Converts protocol constant string to enum
+//
+
+#ifndef NHTTP_FLOWDATA_H
+#define NHTTP_FLOWDATA_H
+
+class NHttpFlowData : public FlowData
+{
+public:
+ NHttpFlowData();
+ ~NHttpFlowData();
+ static unsigned nhttp_flow_id;
+ static void init() { nhttp_flow_id = FlowData::get_flow_id(); };
+
+ // PAF data
+ NHttpEnums::SourceId sourceId = NHttpEnums::SRC_CLIENT;
+ bool tcpClose = false;
+ uint64_t infractions = 0;
+
+ // Client message data
+ NHttpEnums::SectionType clientTypeExpected = NHttpEnums::SEC_HEADER;
+ int32_t clientOctetsExpected = NHttpEnums::STAT_NOTPRESENT;
+
+ // Server message data
+ NHttpEnums::SectionType serverTypeExpected = NHttpEnums::SEC_HEADER;
+ int32_t serverOctetsExpected = NHttpEnums::STAT_NOTPRESENT;
+
+ // Session data
+};
+
+#endif
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief HeaderNormalizer class
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "snort.h"
+#include "snort_types.h"
+
+#include "nhttp_enum.h"
+#include "nhttp_scratchpad.h"
+#include "nhttp_strtocode.h"
+#include "nhttp_headnorm.h"
+
+using namespace NHttpEnums;
+
+// This derivation removes embedded CRLFs (wrapping), omits leading and trailing white space, and replaces internal strings of <SP> and <LF> with a single <SP>
+int32_t HeaderNormalizer::deriveHeaderContent(const uint8_t *value, int32_t length, uint8_t *buffer) const {
+ int32_t outLength = 0;
+ bool lastWhite = true;
+ for (int k=0; k < length; k++) {
+ if ((value[k] == '\r') && (k+1 < length) && (value[k+1] == '\n')) k++;
+ else if ((value[k] != ' ') && (value[k] != '\t')) {
+ lastWhite = false;
+ buffer[outLength++] = value[k];
+ }
+ else if (!lastWhite) {
+ lastWhite = true;
+ buffer[outLength++] = ' ';
+ }
+ }
+ if ((outLength > 0) && (buffer[outLength - 1] == ' ')) outLength--;
+ return outLength;
+}
+
+void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions, HeaderId headId, const HeaderId headerNameId[], const field headerValue[], int32_t numHeaders,
+ field &resultField) const {
+ // This method normalizes the header field value for headId.
+ if (format == NORM_NULL) {
+ resultField.length = STAT_NOTCONFIGURED;
+ return;
+ }
+
+ // 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
+ // header can be present more than once. The standard normalization is to concatenate all the repeated field values into a comma-separated list. Otherwise there should not
+ // be more than one instance of this header. infractRepeats causes us to inspect for improper repeated headers. Regardless of whether we look for these extra values only
+ // the first value will be normalized.
+
+ int numMatches = 0;
+ int32_t bufferLength = 0;
+ int firstMatch = -1;
+ for (int k=0; k < numHeaders; k++) {
+ if (headerNameId[k] == HEAD__NOTCOMPUTE) break;
+ if (headerNameId[k] == headId) {
+ numMatches++;
+ if (numMatches == 1) firstMatch = k;
+ if ((numMatches == 1) || concatenateRepeats) bufferLength += headerValue[k].length;
+ if (!concatenateRepeats && !infractRepeats) break;
+ }
+ }
+ if (numMatches == 0) {
+ resultField.length = STAT_NOTPRESENT;
+ return;
+ }
+ if (infractRepeats && (numMatches >= 2)) infractions |= INF_BADHEADERREPS;
+
+ // The scratchPad provides the space to store the normalized value. We are allocating twice as much memory as we need to store the normalized field value. The raw field
+ // value will be copied into one half of the buffer. Concatenation and white space normalization happen during this step. Next a series of normalization functions will
+ // transform the value into final form. Each normalization copies the value from one half of the buffer to the other. Based on whether the number of normalization functions
+ // is odd or even, the initial placement in the buffer is chosen so that the final normalization leaves the field value at the front of the buffer. The buffer space actually
+ // used is locked down in the scratchPad. The remainder of the first half and all of the second half are returned to the scratchPad for future use.
+ if (concatenateRepeats) bufferLength += numMatches - 1; // allow space for concatenation commas
+ bufferLength += (4-bufferLength%4)%4 + 200; // &&& 200 is a "way too big" fudge factor to allow for modest expansion of field size during normalization. Needs improvement.
+ uint8_t *scratch;
+ if ((scratch = scratchPad.request(2*bufferLength)) == nullptr) {
+ resultField.length = STAT_INSUFMEMORY;
+ return;
+ }
+
+ uint8_t * const frontHalf = scratch;
+ uint8_t * const backHalf = scratch + bufferLength;
+ uint8_t *working = (numNormalizers%2 == 0) ? frontHalf : backHalf;
+ int currMatch = firstMatch;
+ int32_t growth;
+ int32_t dataLength = 0;
+ for (int j=0; j < numMatches; j++) {
+ if (j >= 1) {
+ *working++ = ',';
+ dataLength++;
+ while (headerNameId[++currMatch] != headId);
+ }
+ growth = deriveHeaderContent(headerValue[currMatch].start, headerValue[currMatch].length, working);
+ working += growth;
+ dataLength += growth;
+ if (!concatenateRepeats) break;
+ }
+
+ for (int i=0; i < numNormalizers; i++) {
+ if (i%2 != numNormalizers%2) dataLength = normalizer[i](backHalf, dataLength, frontHalf, infractions, normArg[i]);
+ else dataLength = normalizer[i](frontHalf, dataLength, backHalf, infractions, normArg[i]);
+ if (dataLength <= 0) {
+ resultField.length = dataLength;
+ return;
+ }
+ }
+ resultField.start = scratch;
+ resultField.length = dataLength;
+ scratchPad.commit(dataLength);
+}
+
+// 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 normDecimalInteger(const uint8_t* inBuf, int32_t inLength, uint8_t* outBuf, uint64_t& infractions, const void *notUsed) {
+ uint32_t total = 0;
+ int value;
+ for (int32_t k=0; k < inLength; k++) {
+ value = inBuf[k] - '0';
+ if ((value < 0) || (value > 9)) {
+ infractions |= INF_BADHEADERDATA;
+ return STAT_PROBLEMATIC;
+ }
+ total = total*10 + value;
+ }
+ ((uint32_t*)outBuf)[0] = total;
+ return sizeof(uint32_t);
+}
+
+
+int32_t norm2Lower(const uint8_t* inBuf, int32_t inLength, uint8_t* outBuf, uint64_t& infractions, const void *notUsed) {
+ for (int32_t k=0; k < inLength; k++) {
+ outBuf[k] = ((inBuf[k] < 'A') || (inBuf[k] > 'Z')) ? inBuf[k] : inBuf[k] - ('A' - 'a');
+ }
+ return inLength;
+}
+
+
+int32_t normStrCode(const uint8_t* inBuf, int32_t inLength, uint8_t* outBuf, uint64_t& infractions, const void *table) {
+ ((uint32_t*)outBuf)[0] = strToCode(inBuf, inLength, (const StrCode*)table);
+ return sizeof(uint32_t);
+}
+
+int32_t normSeqStrCode(const uint8_t* inBuf, int32_t inLength, uint8_t* outBuf, uint64_t& infractions, const void *table) {
+ int32_t numCodes = 0;
+ const uint8_t* start = inBuf;
+ int32_t length = 0;
+ while (true) {
+ start += length;
+ for (length = 0; (start + length < inBuf + inLength) && (start[length] != ','); length++);
+ if (length == 0) ((uint32_t*)outBuf)[numCodes++] = STAT_EMPTYSTRING;
+ else ((uint32_t*)outBuf)[numCodes++] = strToCode(start, length, (const StrCode*)table);
+ if (start + length++ >= inBuf + inLength) break;
+ }
+ return numCodes * sizeof(uint32_t);
+}
+
+
+
+
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief HeaderNormalizer class declaration
+//
+
+#ifndef NHTTP_HEADNORM_H
+#define NHTTP_HEADNORM_H
+
+
+//-------------------------------------------------------------------------
+// HeaderNormalizer class
+// Strategies for normalizing HTTP header field values
+//-------------------------------------------------------------------------
+
+// Three normalization functions per HeaderNormalizer seems likely to be enough. Nothing subtle will break if you choose to expand it to four or more. Just a whole bunch of
+// signatures and initializers to update.
+// When defining a HeaderNormalizer don't leave holes in the normalizer list. E.g. if you have two normalizers they must be first and second. If you do first and third
+// instead it won't explode but the third one won't be used either.
+
+class HeaderNormalizer {
+public:
+ constexpr HeaderNormalizer(NHttpEnums::NormFormat _format, bool _concatenateRepeats, bool _infractRepeats, int32_t (*f1)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*),
+ const void *f1Arg, int32_t (*f2)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*), const void *f2Arg, int32_t (*f3)(const uint8_t*, int32_t, uint8_t*, uint64_t&,
+ const void*), const void *f3Arg) :
+ format(_format),
+ concatenateRepeats(_concatenateRepeats),
+ infractRepeats(_infractRepeats),
+ 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,
+ field &resultField) const;
+ NHttpEnums::NormFormat getFormat() const {return format;};
+
+private:
+ int32_t deriveHeaderContent(const uint8_t *value, int32_t length, uint8_t *buffer) const;
+
+ const NHttpEnums::NormFormat format;
+ const bool concatenateRepeats;
+ const bool infractRepeats;
+ int32_t (* const normalizer[3])(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
+ const void * normArg[3];
+ const int numNormalizers;
+};
+
+// Normalizer functions
+
+int32_t normDecimalInteger(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+int32_t norm2Lower(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+int32_t normStrCode(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
+int32_t normSeqStrCode(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
+
+#endif
+
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief NHttp Inspector class.
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdexcept>
+
+#include "snort.h"
+#include "framework/inspector.h"
+#include "flow/flow.h"
+#include "nhttp_enum.h"
+#include "nhttp_scratchpad.h"
+#include "nhttp_strtocode.h"
+#include "nhttp_headnorm.h"
+#include "nhttp_flowdata.h"
+#include "nhttp_msgheader.h"
+#include "nhttp_testinput.h"
+#include "nhttp_api.h"
+#include "nhttp_inspect.h"
+
+const char* NHttpInspect::testInputFile = "nhttptestmsgs.txt";
+const char* NHttpInspect::testOutputPrefix = "nhttpresults/testcase";
+THREAD_LOCAL NHttpMsgHeader* NHttpInspect::msgHead;
+
+NHttpInspect::NHttpInspect(bool _test_mode) : test_mode(_test_mode)
+{
+ printf("NHttpInspect constructor()\n");
+ if (test_mode) {
+ testInput = new NHttpTestInput(testInputFile);
+ }
+}
+
+NHttpInspect::~NHttpInspect ()
+{
+ printf("NHttpInspect destructor()\n");
+ if (test_mode) {
+ delete testInput;
+ if (testOut) fclose(testOut);
+ }
+}
+
+bool NHttpInspect::enabled ()
+{
+ printf("NHttpInspect enabled()\n");
+ return true;
+}
+
+void NHttpInspect::configure (SnortConfig *sc, const char*, char *args)
+{
+ printf("NHttpInspect configure()\n");
+}
+
+int NHttpInspect::verify(SnortConfig* sc)
+{
+ printf("NHttpInspect verify()\n");
+ return 0; // 0 = good, -1 = bad
+}
+
+void NHttpInspect::pinit()
+{
+ printf("NHttpInspect pinit()\n");
+}
+
+void NHttpInspect::pterm()
+{
+ printf("NHttpInspect pterm()\n");
+}
+
+void NHttpInspect::show(SnortConfig*)
+{
+ printf("NHttpInspect show()\n");
+ LogMessage("NHttpInspect\n");
+}
+
+void NHttpInspect::eval (Packet* p)
+{
+ printf("NHttpInspect eval()\n");
+
+ Flow *flow = p->flow;
+ NHttpFlowData* sessionData = (NHttpFlowData*)flow->get_application_data(NHttpFlowData::nhttp_flow_id);
+ if (sessionData == nullptr) flow->set_application_data(sessionData = new NHttpFlowData);
+
+ if (!test_mode) msgHead->loadMessage(p->data, p->dsize, sessionData);
+ else {
+ uint8_t *testBuffer;
+ int32_t testLength;
+ if ((testLength = testInput->ntiGet(&testBuffer, sessionData, testNumber)) > 0) {
+ msgHead->loadMessage(testBuffer, testLength, sessionData);
+ }
+ else {
+ printf("Out of test data.\n");
+ return;
+ }
+ }
+
+ msgHead->analyze();
+
+ msgHead->genEvents();
+
+ // Interface to the old Snort clients
+ msgHead->oldClients();
+
+ if (!test_mode) msgHead->printMessage(stdout);
+ else {
+ if (testNumber != fileTestNumber) {
+ if (testOut) fclose (testOut);
+ fileTestNumber = testNumber;
+ char fileName[100];
+ sprintf(fileName, "%s%d.txt", testOutputPrefix, testNumber);
+ if ((testOut = fopen(fileName, "w+")) == nullptr) throw std::runtime_error("Cannot open test output file");
+ }
+ msgHead->printMessage(testOut);
+ }
+}
+
+
+
+
+
+
--- /dev/null
+/****************************************************************************
+ *
+** 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.
+ *
+ ****************************************************************************/
+
+#ifndef NHTTP_INSPECT_H
+#define NHTTP_INSPECT_H
+
+//-------------------------------------------------------------------------
+// NHttpInspect class
+//-------------------------------------------------------------------------
+
+class NHttpInspect : public Inspector {
+public:
+ NHttpInspect(bool _test_mode);
+ ~NHttpInspect();
+
+ void configure(SnortConfig*, const char*, char *args);
+ int verify(SnortConfig*);
+ void show(SnortConfig*);
+ void eval(Packet*);
+ bool enabled();
+ void pinit();
+ void pterm();
+
+private:
+ friend NHttpApi;
+ static THREAD_LOCAL NHttpMsgHeader *msgHead;
+
+ // Test mode
+ bool test_mode;
+ static const char *testInputFile;
+ static const char *testOutputPrefix;
+ NHttpTestInput *testInput = nullptr;
+ FILE *testOut = nullptr;
+ int64_t testNumber = 0;
+ int64_t fileTestNumber = -1;
+};
+
+#endif
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief Module class for NHttpInspect
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include "snort.h"
+#include "framework/module.h"
+#include "nhttp_enum.h"
+#include "nhttp_module.h"
+
+const Parameter NHttpModule::nhttpParams[] =
+ {{ "test_mode", Parameter::PT_BOOL, nullptr, "false", "read HTTP messages from text file" },
+ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }};
+
+bool NHttpModule::begin(const char*, int, SnortConfig*) {
+ printf("NHttpModule begin()\n");
+ test_mode = false;
+ return true;
+}
+
+bool NHttpModule::end(const char*, int, SnortConfig*) {
+ printf("NHttpModule end()\n");
+ return true;
+}
+
+bool NHttpModule::set(const char*, Value &val, SnortConfig*) {
+ printf("NHttpModule set()\n");
+ if (val.is("test_mode")) {
+ test_mode = val.get_bool();
+ return true;
+ }
+ return false;
+}
+
+unsigned NHttpModule::get_gid() const {
+ printf("NHttpModule get_gid()\n");
+ return GID_HTTP_CLIENT;
+}
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief NHttpModule
+//
+
+#ifndef NHTTP_MODULE_H
+#define NHTTP_MODULE_H
+
+class NHttpModule : public Module
+{
+public:
+ NHttpModule() : Module("nhttp_inspect", nhttpParams) { printf("NHttpModule constructor()\n"); };
+ bool begin(const char*, int, SnortConfig*);
+ bool end(const char*, int, SnortConfig*);
+ bool set(const char*, Value&, SnortConfig*);
+ unsigned get_gid() const;
+ bool get_test_mode() const { return test_mode; };
+private:
+ static const Parameter nhttpParams[];
+ bool test_mode = false;
+};
+
+#endif
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief NHttpMsgHeader class analyzes individual HTTP messages.
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "flow/flow.h"
+#include "detection/detection_util.h"
+#include "nhttp_enum.h"
+#include "nhttp_scratchpad.h"
+#include "nhttp_strtocode.h"
+#include "nhttp_headnorm.h"
+#include "nhttp_flowdata.h"
+#include "nhttp_msgheader.h"
+
+using namespace NHttpEnums;
+
+// Return the number of octets before the first CRLF. Return length if CRLF not present.
+//
+// wrappable: CRLF does not count in a header field when immediately followed by <SP> or <LF>. These whitespace characters
+// at the beginning of the next line indicate that the previous header has wrapped and is continuing on the next line.
+uint32_t NHttpMsgHeader::findCrlf(const uint8_t* buffer, uint32_t length, bool wrappable) {
+ for (uint32_t k=0; k < length-1; k++) {
+ if ((buffer[k] == '\r') && (buffer[k+1] == '\n'))
+ if (!wrappable || (k+2 >= length) || ((buffer[k+2] != ' ') && (buffer[k+2] != '\t'))) return k;
+ }
+ return length;
+}
+
+// Reinitialize everything and load a new message
+void NHttpMsgHeader::loadMessage(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_) {
+ length = (bufsize <= MAXOCTETS) ? bufsize : MAXOCTETS;
+ memcpy(rawBuf, buffer, length);
+
+ sessionData = sessionData_;
+ infractions = sessionData->infractions;
+ sourceId = sessionData->sourceId;
+ tcpClose = sessionData->tcpClose;
+
+ scratchPad.reinit();
+
+ startLine.length = STAT_NOTCOMPUTE;
+ version.length = STAT_NOTCOMPUTE;
+ versionId = VERS__NOTCOMPUTE;
+ method.length = STAT_NOTCOMPUTE;
+ methodId = METH__NOTCOMPUTE;
+ uri.length = STAT_NOTCOMPUTE;
+ statusCode.length = STAT_NOTCOMPUTE;
+ statusCodeNum = STAT_NOTCOMPUTE;
+ reasonPhrase.length = STAT_NOTCOMPUTE;
+ headers.length = STAT_NOTCOMPUTE;
+ numHeaders = STAT_NOTCOMPUTE;
+ for(int k = 0; k < MAXHEADERS; k++) {
+ headerLine[k].length = STAT_NOTCOMPUTE;
+ headerName[k].length = STAT_NOTCOMPUTE;
+ headerNameId[k] = HEAD__NOTCOMPUTE;
+ headerValue[k].length = STAT_NOTCOMPUTE;
+ }
+ for (int k = 1; k < HEAD__MAXVALUE; k++) {
+ headerValueNorm[k].length = STAT_NOTCOMPUTE;
+ }
+}
+
+// All the header processing that is done for every message (i.e. not just-in-time) is done here.
+void NHttpMsgHeader::analyze() {
+ parseWhole();
+ if (sourceId == SRC_CLIENT) {
+ parseRequestLine();
+ deriveMethodId();
+ }
+ else if (sourceId == SRC_SERVER) {
+ parseStatusLine();
+ deriveStatusCodeNum();
+ }
+ deriveVersionId();
+ parseHeaderBlock();
+ parseHeaderLines();
+ for (int j=0; j < MAXHEADERS; j++) {
+ if (headerName[j].length <= 0) break;
+ deriveHeaderNameId(j);
+ }
+ for (int k=1; k <= numNorms; k++) {
+ headerNorms[k]->normalize(scratchPad, infractions, (HeaderId)k, headerNameId, headerValue, MAXHEADERS, headerValueNorm[k]);
+ }
+}
+
+// All we do here is separate the start line from the header fields.
+// It is so complicated because 1) there might not be any header fields and 2) the message may have been truncated by a TCP connection close.
+// The asserts are very useful in test mode because they pick up bad test case data that we are not designed to handle. Otherwise they should
+// never go off unless PAF is broken and feeding us bad stuff.
+void NHttpMsgHeader::parseWhole() {
+ startLine.start = msgText;
+ startLine.length = findCrlf(startLine.start, length, false);
+ // findCrtl() guarentees that either the start line is the whole message or there must be at least two more characters and the first two are <CR><LF>.
+ assert((length == startLine.length) || ((length >= startLine.length+2) && !memcmp(msgText + startLine.length, "\r\n", 2)));
+ // We trust PAF. !tcpClose guarentees that either there are exactly four more characters <CR><LF><CR><LF> or there are at least seven more characters
+ // with the first two being <CR><LF> and the last four being <CR><LF><CR><LF>.
+ assert(tcpClose ||
+ ((length == startLine.length+4) && !memcmp(msgText + startLine.length, "\r\n\r\n", 4)) ||
+ ((length >= startLine.length+7) && !memcmp(msgText + startLine.length, "\r\n", 2) && !memcmp(msgText + length - 4, "\r\n\r\n", 4)));
+
+ // The following if-else ladder puts the extremely common normal cases at the beginning and the rare pathological cases at the end
+ // Normal case with header fields
+ if (!tcpClose && (length >= startLine.length+7)) {
+ headers.start = msgText + startLine.length + 2;
+ headers.length = length - startLine.length - 6;
+ }
+ // Normal case no header fields (only a start line)
+ else if (!tcpClose) {
+ headers.length = STAT_NOTPRESENT;
+ }
+ // Normal case with header fields and TCP connection close
+ else if ((length >= startLine.length+7) && !memcmp(msgText+length-4, "\r\n\r\n", 4)) {
+ headers.start = msgText + startLine.length + 2;
+ headers.length = length - startLine.length - 6;
+ }
+ // Normal case no header fields and TCP connection close
+ else if ((length == startLine.length + 4) && !memcmp(msgText+length-2, "\r\n", 2)) {
+ headers.length = STAT_NOTPRESENT;
+ }
+ // Abnormal cases truncated by TCP connection close
+ else {
+ infractions |= INF_TRUNCATED;
+ // Either start line incomplete or start line complete but no leftover octets for anything else
+ if (length <= startLine.length+2) {
+ headers.length = STAT_NOTPRESENT;
+ }
+ // Start line complete followed by lone <CR>
+ else if ((length == startLine.length+3) && (msgText[length-1] == '\r')) {
+ headers.length = STAT_NOTPRESENT;
+ }
+ // Truncation occurred somewhere in the header fields
+ else {
+ headers.start = msgText + startLine.length + 2;
+ headers.length = length - startLine.length - 2;
+ // When present, remove partial <CR><LF><CR><LF> sequence from the very end
+ if ((length > startLine.length+6) && !memcmp(msgText+length-3, "\r\n\r", 3)) headers.length -= 3;
+ else if ((length > startLine.length+5) && !memcmp(msgText+length-2, "\r\n", 2)) headers.length -= 2;
+ else if ((length > startLine.length+4) && (msgText[length-1] == '\r')) headers.length -= 1;
+ }
+ }
+}
+
+void NHttpMsgHeader::parseRequestLine() {
+ // There should be exactly two spaces. One following the method and one before "HTTP/".
+ // Eventually we may need to cater to certain format errors, but for now exact match or treat as error.
+ // <method><SP><URI><SP>HTTP/X.Y
+ if (startLine.start[startLine.length-9] != ' ') {
+ // space before "HTTP" missing or in wrong place
+ infractions |= INF_BADREQLINE;
+ return;
+ }
+
+ int space = -1;
+ for (int32_t k=0; k < startLine.length-9; k++) {
+ if (startLine.start[k] == ' ') {
+ if (space == -1) space = k;
+ else {
+ // too many spaces
+ infractions |= INF_BADREQLINE;
+ return;
+ }
+ }
+ }
+ if ((space <= 0)) {
+ // no first space or a leading space
+ infractions |= INF_BADREQLINE;
+ return;
+ }
+
+ method.start = startLine.start;
+ method.length = space;
+ uri.start = startLine.start + method.length + 1;
+ uri.length = startLine.length - method.length - 10;
+ version.start = startLine.start + (startLine.length - 8);
+ version.length = 8;
+ assert (startLine.length == method.length + uri.length + version.length + 2);
+}
+
+void NHttpMsgHeader::parseStatusLine() {
+ // Eventually we may need to cater to certain format errors, but for now exact match or treat as error.
+ // HTTP/X.Y<SP>###<SP><text>
+ if ((startLine.length < 13) || (startLine.start[8] != ' ') || (startLine.start[12] != ' ')) {
+ infractions |= INF_BADSTATLINE;
+ return;
+ }
+ version.start = startLine.start;
+ version.length = 8;
+ statusCode.start = startLine.start + 9;
+ statusCode.length = 3;
+ reasonPhrase.start = startLine.start + 13;
+ reasonPhrase.length = startLine.length - 13;
+ assert (startLine.length == version.length + statusCode.length + reasonPhrase.length + 2);
+}
+
+// Divide up the block of header fields into individual header field lines.
+void NHttpMsgHeader::parseHeaderBlock() {
+ if (headers.length <= 0) 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;
+ if (numHeaders >= MAXHEADERS) {
+ break;
+ }
+ }
+ if (bytesused < headers.length) {
+ infractions |= INF_TOOMANYHEADERS;
+ }
+}
+
+// Divide header field lines into field name and field value
+void NHttpMsgHeader::parseHeaderLines() {
+ int colon;
+ for (int k=0; k < numHeaders; k++) {
+ for (colon=0; colon < headerLine[k].length; colon++) {
+ if (headerLine[k].start[colon] == ':') break;
+ }
+ if (colon < headerLine[k].length) {
+ headerName[k].start = headerLine[k].start;
+ headerName[k].length = colon;
+ headerValue[k].start = headerLine[k].start + colon + 1;
+ headerValue[k].length = headerLine[k].length - colon - 1;
+ }
+ else {
+ infractions |= INF_BADHEADER;
+ }
+ }
+}
+
+void NHttpMsgHeader::deriveStatusCodeNum() {
+ 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;
+ statusCodeNum = STAT_PROBLEMATIC;
+ return;
+ }
+ statusCodeNum = (statusCode.start[0] - '0') * 100 + (statusCode.start[1] - '0') * 10 + (statusCode.start[2] - '0');
+ if ((statusCodeNum < 100) || (statusCodeNum > 599)) {
+ infractions |= INF_BADSTATCODE;
+ }
+}
+
+void NHttpMsgHeader::deriveVersionId() {
+ if (version.length != 8) {
+ versionId = VERS__PROBLEMATIC;
+ return;
+ }
+ if (memcmp(version.start, "HTTP/", 5) || (version.start[6] != '.')) {
+ versionId = VERS__PROBLEMATIC;
+ infractions |= INF_BADVERSION;
+ }
+ else if ((version.start[5] == '1') && (version.start[7] == '1')) {
+ versionId = VERS_1_1;
+ }
+ else if ((version.start[5] == '1') && (version.start[7] == '0')) {
+ versionId = VERS_1_0;
+ }
+ else if ((version.start[5] == '2') && (version.start[7] == '0')) {
+ versionId = VERS_2_0;
+ }
+ else if ((version.start[5] >= '0') && (version.start[5] <= '9') && (version.start[7] >= '0') && (version.start[7] <= '9')) {
+ versionId = VERS__OTHER;
+ infractions |= INF_UNKNOWNVERSION;
+ }
+ else {
+ versionId = VERS__PROBLEMATIC;
+ infractions |= INF_BADVERSION;
+ }
+}
+
+void NHttpMsgHeader::deriveMethodId() {
+ methodId = (MethodId) strToCode(method.start, method.length, methodList);
+}
+
+void NHttpMsgHeader::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) {
+ infractions |= INF_NOSCRATCH;
+ headerNameId[index] = HEAD__INSUFMEMORY;
+ return;
+ }
+ int32_t lowerLength = norm2Lower(headerName[index].start, headerName[index].length, lowerName, infractions, nullptr);
+ headerNameId[index] = (HeaderId) strToCode(lowerName, lowerLength, headerList);
+}
+
+void NHttpMsgHeader::genEvents() {
+ if (infractions != 0) SnortEventqAdd(GID_HTTP_CLIENT, 1); // I'm just an example event (HI_CLIENT_ASCII)
+}
+
+void NHttpMsgHeader::printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals) {
+ if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE)) return;
+ fprintf(output, "%s, length = %d\n", name, length);
+ if (length <= 0) return;
+ if (text == nullptr) {
+ fprintf(output, "nullptr\n");
+ return;
+ }
+ for (int k=0; k < length; k++) {
+ if ((text[k] >= 0x20) && (text[k] <= 0x7E)) fprintf(output, "%c", (char)text[k]);
+ else if (text[k] == 0x0) fprintf(output, "~");
+ else if (text[k] == 0xD) fprintf(output, "`");
+ else if (text[k] == 0xA) fprintf(output, "'");
+ else fprintf(output, "*");
+ if (k%200 == 199) fprintf(output, "\n");
+ }
+
+ if (intVals && (length%4 == 0)) {
+ fprintf(output, "\nInteger values =");
+ for (int j=0; j < length; j+=4) {
+ fprintf(output, " %u", *((const uint32_t*)(text+j)));
+ }
+ }
+ fprintf(output, "\n");
+}
+
+void NHttpMsgHeader::printMessage(FILE *output) {
+ fprintf(output, "Printout of HTTP message structure.\n");
+ printInterval(output, "Raw message", msgText, length);
+ printInterval(output, "Start Line", startLine.start, startLine.length);
+ if (sourceId != SRC__NOTCOMPUTE) fprintf(output, "Source Id: %d\n", sourceId);
+ printInterval(output, "Version", version.start, version.length);
+ if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId);
+ printInterval(output, "Method", method.start, method.length);
+ if (methodId != METH__NOTCOMPUTE) fprintf(output, "Method Id: %d\n", methodId);
+ printInterval(output, "URI", uri.start, uri.length);
+ printInterval(output, "Status Code", statusCode.start, statusCode.length);
+ if (statusCodeNum != STAT_NOTCOMPUTE) fprintf(output, "Status Code Num: %d\n", statusCodeNum);
+ printInterval(output, "Reason Phrase", reasonPhrase.start, reasonPhrase.length);
+ printInterval(output, "Headers", headers.start, headers.length);
+ if (numHeaders != STAT_NOTCOMPUTE) fprintf(output, "Number of headers: %d\n", numHeaders);
+ for (int j=0; j < numHeaders && j < 200; j++) {
+ printInterval(output, "Header Line", headerLine[j].start, headerLine[j].length);
+ printInterval(output, "Header Name", headerName[j].start, headerName[j].length);
+ fprintf(output, "Header name Id: %d\n", headerNameId[j]);
+ printInterval(output, "Header Value", headerValue[j].start, headerValue[j].length);
+ }
+ for (int k=1; k <= numNorms; k++) {
+ if (headerValueNorm[k].length != STAT_NOTPRESENT) fprintf(output, "Header ID = %d\n", k);
+ printInterval(output, "Header Value Normalized", headerValueNorm[k].start, headerValueNorm[k].length, true);
+ }
+ fprintf(output, "Infractions: %lx\n", infractions);
+ fprintf(output, "TCP Close: %s\n", tcpClose ? "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) printInterval(output, http_buffer_name[i], http_buffer[i].buf, http_buffer[i].length);
+ }
+}
+
+// Legacy support function. Puts message fields into the buffers used by old Snort. This should go away.
+void NHttpMsgHeader::oldClients() {
+ 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 (uri.length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uri.start, (unsigned)uri.length);
+ if (headers.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_HEADER, headers.start, (unsigned)headers.length);
+ if (headers.length > 0) SetHttpBuffer(HTTP_BUFFER_HEADER, headers.start, (unsigned)headers.length);
+ 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);
+
+ for (int k=0; (headerNameId[k] != HEAD__NOTCOMPUTE) && (k < MAXHEADERS); k++) {
+ if (((headerNameId[k] == HEAD_COOKIE) && (sourceId == SRC_CLIENT)) || ((headerNameId[k] == HEAD_SET_COOKIE) && (sourceId == SRC_SERVER))) {
+ if (headerValue[k].length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_COOKIE, headerValue[k].start, (unsigned)headerValue[k].length);
+ break;
+ }
+ }
+
+ 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);
+}
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief NHttpMsgHeader class declaration
+//
+
+#ifndef NHTTP_MSGHEADER_H
+#define NHTTP_MSGHEADER_H
+
+
+//-------------------------------------------------------------------------
+// NHttpMsgHeader class
+//-------------------------------------------------------------------------
+
+class NHttpMsgHeader {
+public:
+ NHttpMsgHeader() {};
+ void loadMessage(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
+ void analyze();
+ void printMessage(FILE *output);
+ void genEvents();
+ void oldClients(); // I'm a legacy support method and should go away eventually
+ static const uint32_t MAXOCTETS = 65535;
+
+private:
+ // Header normalization. There should be one of these for every different way we can process a header field value.
+ static const HeaderNormalizer NORMALIZER_NIL;
+ static const HeaderNormalizer NORMALIZER_BASIC;
+ static const HeaderNormalizer NORMALIZER_CAT;
+ static const HeaderNormalizer NORMALIZER_NOREPEAT;
+ static const HeaderNormalizer NORMALIZER_DECIMAL;
+ static const HeaderNormalizer NORMALIZER_TRANSCODE;
+
+ // Master table of known header fields and their normalization strategies.
+ static const HeaderNormalizer* const headerNorms[];
+ static const int32_t numNorms;
+
+ // Code conversion tables are for turning token strings into enums.
+ static const StrCode methodList[];
+ static const StrCode headerList[];
+ static const StrCode transCodeList[];
+
+ // "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.
+ void parseWhole();
+ void deriveSourceId();
+ void parseRequestLine();
+ void parseStatusLine();
+ void parseHeaderBlock();
+ void parseHeaderLines();
+ void deriveHeaderNameId(int index);
+ void deriveStatusCodeNum();
+ void deriveVersionId();
+ void deriveMethodId();
+
+ // Convenience methods
+ uint32_t findCrlf(const uint8_t* buffer, uint32_t length, bool wrappable);
+ void printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals = false);
+
+ // The current strategy is to copy the entire raw message headers into this object. Here it is.
+ uint32_t length; // Length of the original message headers in octets
+ uint8_t rawBuf[MAXOCTETS]; // The original HTTP message header octets
+ // This pointer is the handle for working with the original message data. It makes it simple to later replace rawBuf with some other form of storage
+ // such as the buffer in the packet structure or something dynamic. Const x 2 because this pointer should never change and people working with the
+ // 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.
+ // Allocation size may be complete overkill. Need to revisit this.
+ uint32_t derivedBuf[MAXOCTETS/4];
+ NHttpFlowData* sessionData;
+ ScratchPad scratchPad {derivedBuf, MAXOCTETS/4};
+
+ // This is where all the derived values, extracted message parts, and normalized values are.
+ // Note that this is all scalars, buffer pointers, and buffer sizes. The actual buffers are in the original message buffer (raw pieces) or the
+ // scratchPad (normalized pieces).
+ uint64_t infractions;
+ bool tcpClose;
+ field startLine;
+ NHttpEnums::SourceId sourceId;
+ field version;
+ NHttpEnums::VersionId versionId;
+ field method;
+ NHttpEnums::MethodId methodId;
+ field uri;
+ field statusCode;
+ int32_t statusCodeNum;
+ field reasonPhrase;
+ field headers;
+ static const int MAXHEADERS = 200; // I'm an arbitrary number. Need to revisit.
+ int32_t numHeaders;
+ field headerLine[MAXHEADERS];
+ field headerName[MAXHEADERS];
+ NHttpEnums::HeaderId headerNameId[MAXHEADERS];
+ field headerValue[MAXHEADERS];
+ field headerValueNorm[NHttpEnums::HEAD__MAXVALUE];
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief ScratchPad class declaration
+//
+
+#ifndef NHTTP_SCRATCHPAD_H
+#define NHTTP_SCRATCHPAD_H
+
+
+//-------------------------------------------------------------------------
+// ScratchPad class
+// Memory management for NHttpMsgHeader class
+//-------------------------------------------------------------------------
+
+// Working space and storage for all the derived fields
+// Return value of request is 32-bit aligned and may be freely cast to uint32_t*
+// 1. request the maximum number of bytes you might need
+// 2. use what you need
+// 3. commit() what you actually used if you want to keep it
+// Anything you do not commit will be reused by the next request.
+
+class ScratchPad {
+public:
+ ScratchPad(uint32_t *buff, uint32_t length) : buffer(buff), capacity(length*4), used(0) {}; // Careful: length must be number of uint32_ts provided, not octets.
+ 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 + (4-(taken%4))%4; }; // round up to multiple of 4 to preserve alignment
+
+private:
+ uint32_t *buffer;
+ uint32_t capacity;
+ uint32_t used;
+};
+
+#endif
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief Converts token strings to enum codes
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_strtocode.h"
+
+// 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;
+ }
+ }
+ return NHttpEnums::STAT_OTHER;
+}
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief Converts protocol constant string to enum
+//
+
+#ifndef NHTTP_STRTOCODE_H
+#define NHTTP_STRTOCODE_H
+
+struct StrCode {
+ int32_t code;
+ const char *name;
+};
+
+int32_t strToCode(const uint8_t *text, int32_t textLen, const StrCode table[]);
+
+#endif
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief Static constant tables for converting protocol strings to enum codes. Members of HttpMsgHeader.
+//
+
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "snort.h"
+#include "flow/flow.h"
+#include "nhttp_enum.h"
+#include "nhttp_scratchpad.h"
+#include "nhttp_strtocode.h"
+#include "nhttp_headnorm.h"
+#include "nhttp_flowdata.h"
+#include "nhttp_msgheader.h"
+
+using namespace NHttpEnums;
+
+const StrCode NHttpMsgHeader::methodList[] =
+ {{ METH_GET, "GET"},
+ { METH_HEAD, "HEAD"},
+ { METH_POST, "POST"},
+ { METH_PUT, "PUT"},
+ { METH_DELETE, "DELETE"},
+ { METH_TRACE, "TRACE"},
+ { METH_CONNECT, "CONNECT"},
+ { METH_PROPFIND, "PROPFIND"},
+ { METH_PROPPATCH, "PROPPATCH"},
+ { METH_MKCOL, "MKCOL"},
+ { METH_COPY, "COPY"},
+ { METH_MOVE, "MOVE"},
+ { METH_LOCK, "LOCK"},
+ { METH_UNLOCK, "UNLOCK"},
+ { METH_VERSION_CONTROL, "VERSION-CONTROL"},
+ { METH_REPORT, "REPORT"},
+ { METH_CHECKOUT, "CHECKOUT"},
+ { METH_CHECKIN, "CHECKIN"},
+ { METH_UNCHECKOUT, "UNCHECKOUT"},
+ { METH_MKWORKSPACE, "MKWORKSPACE"},
+ { METH_UPDATE, "UPDATE"},
+ { METH_LABEL, "LABEL"},
+ { METH_MERGE, "MERGE"},
+ { METH_BASELINE_CONTROL, "BASELINE-CONTROL"},
+ { METH_MKACTIVITY, "MKACTIVITY"},
+ { METH_ORDERPATCH, "ORDERPATCH"},
+ { METH_ACL, "ACL"},
+ { METH_PATCH, "PATCH"},
+ { METH_SEARCH, "SEARCH"},
+ { METH_BCOPY, "BCOPY"},
+ { METH_BDELETE, "BDELETE"},
+ { METH_BMOVE, "BMOVE"},
+ { METH_BPROPFIND, "BPROPFIND"},
+ { METH_BPROPPATCH, "BPROPPATCH"},
+ { METH_NOTIFY, "NOTIFY"},
+ { METH_POLL, "POLL"},
+ { METH_SUBSCRIBE, "SUBSCRIBE"},
+ { METH_UNSUBSCRIBE, "UNSUBSCRIBE"},
+ { METH_X_MS_ENUMATTS, "X-MS-ENUMATTS"},
+ { METH_BIND, "BIND"},
+ { METH_LINK, "LINK"},
+ { METH_MKCALENDAR, "MKCALENDAR"},
+ { METH_MKREDIRECTREF, "MKREDIRECTREF"},
+ { METH_REBIND, "REBIND"},
+ { METH_UNBIND, "UNBIND"},
+ { METH_UNLINK, "UNLINK"},
+ { METH_UPDATEREDIRECTREF, "UPDATEREDIRECTREF"},
+ { 0, nullptr} };
+
+const StrCode NHttpMsgHeader::headerList[] =
+ {{ HEAD_CACHE_CONTROL, "cache-control"},
+ { HEAD_CONNECTION, "connection"},
+ { HEAD_DATE, "date"},
+ { HEAD_PRAGMA, "pragma"},
+ { HEAD_TRAILER, "trailer"},
+ { HEAD_COOKIE, "cookie"},
+ { HEAD_SET_COOKIE, "set-cookie"},
+ { HEAD_TRANSFER_ENCODING, "transfer-encoding"},
+ { HEAD_UPGRADE, "upgrade"},
+ { HEAD_VIA, "via"},
+ { HEAD_WARNING, "warning"},
+ { HEAD_ACCEPT, "accept"},
+ { HEAD_ACCEPT_CHARSET, "accept-charset"},
+ { HEAD_ACCEPT_ENCODING, "accept-encoding"},
+ { HEAD_ACCEPT_LANGUAGE, "accept-language"},
+ { HEAD_AUTHORIZATION, "authorization"},
+ { HEAD_EXPECT, "expect"},
+ { HEAD_FROM, "from"},
+ { HEAD_HOST, "host"},
+ { HEAD_IF_MATCH, "if-match"},
+ { HEAD_IF_MODIFIED_SINCE, "if-modified-since"},
+ { HEAD_IF_NONE_MATCH, "if-none-match"},
+ { HEAD_IF_RANGE, "if-range"},
+ { HEAD_IF_UNMODIFIED_SINCE, "if-unmodified-since"},
+ { HEAD_MAX_FORWARDS, "max-forwards"},
+ { HEAD_PROXY_AUTHORIZATION, "proxy-authorization"},
+ { HEAD_RANGE, "range"},
+ { HEAD_REFERER, "referer"},
+ { HEAD_TE, "te"},
+ { HEAD_USER_AGENT, "user-agent"},
+ { HEAD_ACCEPT_RANGES, "accept-ranges"},
+ { HEAD_AGE, "age"},
+ { HEAD_ETAG, "etag"},
+ { HEAD_LOCATION, "location"},
+ { HEAD_PROXY_AUTHENTICATE, "proxy-authenticate"},
+ { HEAD_RETRY_AFTER, "retry-after"},
+ { HEAD_SERVER, "server"},
+ { HEAD_VARY, "vary"},
+ { HEAD_WWW_AUTHENTICATE, "www-authenticate"},
+ { HEAD_ALLOW, "allow"},
+ { HEAD_CONTENT_ENCODING, "content-encoding"},
+ { HEAD_CONTENT_LANGUAGE, "content-language"},
+ { HEAD_CONTENT_LENGTH, "content-length"},
+ { HEAD_CONTENT_LOCATION, "content-location"},
+ { HEAD_CONTENT_MD5, "content-md5"},
+ { HEAD_CONTENT_RANGE, "content-range"},
+ { HEAD_CONTENT_TYPE, "content-type"},
+ { HEAD_EXPIRES, "expires"},
+ { HEAD_LAST_MODIFIED, "last-modified"},
+ { 0, nullptr} };
+
+const StrCode NHttpMsgHeader::transCodeList[] =
+ {{ TRANSCODE_CHUNKED, "chunked"},
+ { TRANSCODE_IDENTITY, "identity"},
+ { TRANSCODE_GZIP, "gzip"},
+ { TRANSCODE_COMPRESS, "compress"},
+ { TRANSCODE_DEFLATE, "deflate"},
+ { 0, nullptr} };
+
+const HeaderNormalizer NHttpMsgHeader::NORMALIZER_NIL {NORM_NULL, false, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgHeader::NORMALIZER_BASIC {NORM_FIELD, false, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgHeader::NORMALIZER_CAT {NORM_FIELD, true, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgHeader::NORMALIZER_NOREPEAT {NORM_FIELD, false, true, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgHeader::NORMALIZER_DECIMAL {NORM_INTEGER, false, true, normDecimalInteger, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgHeader::NORMALIZER_TRANSCODE {NORM_INTEGER, true, false, normSeqStrCode, NHttpMsgHeader::transCodeList, nullptr, nullptr, nullptr, nullptr};
+
+const HeaderNormalizer* const NHttpMsgHeader::headerNorms[HEAD__MAXVALUE] = { [0] = &NORMALIZER_NIL,
+ [HEAD__OTHER] = &NORMALIZER_BASIC,
+ [HEAD_CACHE_CONTROL] = &NORMALIZER_BASIC,
+ [HEAD_CONNECTION] = &NORMALIZER_BASIC,
+ [HEAD_DATE] = &NORMALIZER_BASIC,
+ [HEAD_PRAGMA] = &NORMALIZER_BASIC,
+ [HEAD_TRAILER] = &NORMALIZER_BASIC,
+ [HEAD_COOKIE] = &NORMALIZER_BASIC,
+ [HEAD_SET_COOKIE] = &NORMALIZER_BASIC,
+ [HEAD_TRANSFER_ENCODING] = &NORMALIZER_TRANSCODE,
+ [HEAD_UPGRADE] = &NORMALIZER_BASIC,
+ [HEAD_VIA] = &NORMALIZER_BASIC,
+ [HEAD_WARNING] = &NORMALIZER_BASIC,
+ [HEAD_ACCEPT] = &NORMALIZER_BASIC,
+ [HEAD_ACCEPT_CHARSET] = &NORMALIZER_BASIC,
+ [HEAD_ACCEPT_ENCODING] = &NORMALIZER_BASIC,
+ [HEAD_ACCEPT_LANGUAGE] = &NORMALIZER_BASIC,
+ [HEAD_AUTHORIZATION] = &NORMALIZER_BASIC,
+ [HEAD_EXPECT] = &NORMALIZER_BASIC,
+ [HEAD_FROM] = &NORMALIZER_BASIC,
+ [HEAD_HOST] = &NORMALIZER_BASIC,
+ [HEAD_IF_MATCH] = &NORMALIZER_BASIC,
+ [HEAD_IF_MODIFIED_SINCE] = &NORMALIZER_BASIC,
+ [HEAD_IF_NONE_MATCH] = &NORMALIZER_BASIC,
+ [HEAD_IF_RANGE] = &NORMALIZER_BASIC,
+ [HEAD_IF_UNMODIFIED_SINCE] = &NORMALIZER_BASIC,
+ [HEAD_MAX_FORWARDS] = &NORMALIZER_BASIC,
+ [HEAD_PROXY_AUTHORIZATION] = &NORMALIZER_BASIC,
+ [HEAD_RANGE] = &NORMALIZER_BASIC,
+ [HEAD_REFERER] = &NORMALIZER_BASIC,
+ [HEAD_TE] = &NORMALIZER_BASIC,
+ [HEAD_USER_AGENT] = &NORMALIZER_BASIC,
+ [HEAD_ACCEPT_RANGES] = &NORMALIZER_BASIC,
+ [HEAD_AGE] = &NORMALIZER_BASIC,
+ [HEAD_ETAG] = &NORMALIZER_BASIC,
+ [HEAD_LOCATION] = &NORMALIZER_BASIC,
+ [HEAD_PROXY_AUTHENTICATE] = &NORMALIZER_BASIC,
+ [HEAD_RETRY_AFTER] = &NORMALIZER_BASIC,
+ [HEAD_SERVER] = &NORMALIZER_BASIC,
+ [HEAD_VARY] = &NORMALIZER_BASIC,
+ [HEAD_WWW_AUTHENTICATE] = &NORMALIZER_BASIC,
+ [HEAD_ALLOW] = &NORMALIZER_BASIC,
+ [HEAD_CONTENT_ENCODING] = &NORMALIZER_BASIC,
+ [HEAD_CONTENT_LANGUAGE] = &NORMALIZER_BASIC,
+ [HEAD_CONTENT_LENGTH] = &NORMALIZER_DECIMAL,
+ [HEAD_CONTENT_LOCATION] = &NORMALIZER_BASIC,
+ [HEAD_CONTENT_MD5] = &NORMALIZER_BASIC,
+ [HEAD_CONTENT_RANGE] = &NORMALIZER_BASIC,
+ [HEAD_CONTENT_TYPE] = &NORMALIZER_BASIC,
+ [HEAD_EXPIRES] = &NORMALIZER_BASIC,
+ [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC
+};
+
+ const int32_t NHttpMsgHeader::numNorms = HEAD__MAXVALUE-1;
+
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief Interface to file test messages
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdexcept>
+#include "snort.h"
+#include "flow/flow.h"
+#include "nhttp_enum.h"
+#include "nhttp_scratchpad.h"
+#include "nhttp_strtocode.h"
+#include "nhttp_headnorm.h"
+#include "nhttp_flowdata.h"
+#include "nhttp_msgheader.h"
+#include "nhttp_testinput.h"
+
+NHttpTestInput::NHttpTestInput(const char *fileName) {
+ if ((msgFile = fopen(fileName, "r")) == nullptr) throw std::runtime_error("Cannot open test input file");
+}
+
+NHttpTestInput::~NHttpTestInput() {
+ fclose(msgFile);
+}
+
+// Read the next message section from the test data file.
+// In the process we may need to skip comments, execute simple commands, and handle simple escape sequences.
+// The best way to understand this function is to read the comments at the top of the file of test cases.
+int32_t NHttpTestInput::ntiGet(uint8_t **buffer, NHttpFlowData* sessionData, int64_t &testNumber) {
+ int32_t length = 0;
+ *buffer = msgBuf;
+ int newChar;
+ typedef enum { WAITING, COMMENT, COMMAND, SECTION, ESCAPE0, ESCAPE1, ESCAPE2 } State;
+ State state = WAITING;
+ bool ending;
+ char escapeNum[] = { 0, 0, '\0' };
+ int commandLength = 0;
+ const int MaxCommand = 100;
+ char commandValue[MaxCommand];
+
+ sessionData->tcpClose = false;
+ sessionData->infractions = 0;
+
+ while ((newChar = getc(msgFile)) != EOF) {
+ switch (state) {
+ case WAITING:
+ if (newChar == '#') state = COMMENT;
+ else if (newChar == '@') {
+ state = COMMAND;
+ commandLength = 0;
+ }
+ else if (newChar == '\\') {
+ state = ESCAPE0;
+ ending = false;
+ }
+ else if (newChar != '\n') {
+ state = SECTION;
+ ending = false;
+ msgBuf[length++] = (uint8_t) newChar;
+ }
+ break;
+ case COMMENT:
+ if (newChar == '\n') state = WAITING;
+ break;
+ case COMMAND:
+ if (newChar == '\n') {
+ state = WAITING;
+ if ((commandLength == strlen("request")) && !memcmp(commandValue, "request", strlen("request"))) sessionData->sourceId = NHttpEnums::SRC_CLIENT;
+ else if ((commandLength == strlen("response")) && !memcmp(commandValue, "response", strlen("response"))) sessionData->sourceId = NHttpEnums::SRC_SERVER;
+ else if ((commandLength == strlen("tcpclose")) && !memcmp(commandValue, "tcpclose", strlen("tcpclose"))) sessionData->tcpClose = true;
+ else if ((commandLength == strlen("break")) && !memcmp(commandValue, "break", strlen("break"))) {
+ // &&& signifies start of new session so wipe "session data" when we define some. See flowdata.h.
+ }
+ else if (commandLength > 0) {
+ // Look for a test number
+ bool isNumber = true;
+ for (int k=0; isNumber && (k < commandLength); k++) {
+ isNumber = (commandValue[k] >= '0') && (commandValue[k] <= '9');
+ }
+ if (isNumber) {
+ testNumber = 0;
+ for (int j=0; j < commandLength; j++) {
+ testNumber = testNumber * 10 + (commandValue[j] - '0');
+ }
+ }
+ }
+ }
+ else {
+ if (commandLength < MaxCommand) commandValue[commandLength++] = newChar;
+ }
+ break;
+ case SECTION:
+ if (newChar == '\\') {
+ state = ESCAPE0;
+ }
+ else if (newChar == '\n') {
+ if (ending) return length; // Found the blank line that ends the section.
+ ending = true;
+ }
+ else {
+ ending = false;
+ msgBuf[length++] = (uint8_t) newChar;
+ }
+ break;
+ case ESCAPE0:
+ if (newChar == 'n') { state = SECTION; ending = false; msgBuf[length++] = '\n'; }
+ else if (newChar == 'r') { state = SECTION; ending = false; msgBuf[length++] = '\r'; }
+ else if (newChar == 't') { state = SECTION; ending = false; msgBuf[length++] = '\t'; }
+ else if (newChar == '#') { state = SECTION; ending = false; msgBuf[length++] = '#'; }
+ else if (newChar == '@') { state = SECTION; ending = false; msgBuf[length++] = '@'; }
+ else if (newChar == '\\') { state = SECTION; ending = false; msgBuf[length++] = '\\'; }
+ else if ((newChar == 'x') || (newChar == 'X')) state = ESCAPE1;
+ else { state = SECTION; ending = false; }
+ break;
+ case ESCAPE1:
+ state = ESCAPE2;
+ escapeNum[0] = newChar;
+ break;
+ case ESCAPE2:
+ state = SECTION;
+ ending = false;
+ escapeNum[1] = newChar;
+ if (((escapeNum[0] < '0') || (escapeNum[0] > '9')) && ((escapeNum[0] < 'A') || (escapeNum[0] > 'Z')) && ((escapeNum[0] < 'a') || (escapeNum[0] > 'z'))) break;
+ if (((escapeNum[1] < '0') || (escapeNum[1] > '9')) && ((escapeNum[1] < 'A') || (escapeNum[1] > 'Z')) && ((escapeNum[1] < 'a') || (escapeNum[1] > 'z'))) break;
+ msgBuf[length++] = strtoul(escapeNum, nullptr, 16);
+ break;
+ }
+ // Return because the buffer is full. Not a feature just a safety precaution against bad input.
+ if (length >= sizeof(msgBuf)) return length;
+ }
+ // End-of-file. Return everything we have so far.
+ return length;
+}
+
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 <thopeter@cisco.com>
+//
+// @brief Converts protocol constant string to enum
+//
+
+#ifndef NHTTP_TESTINPUT_H
+#define NHTTP_TESTINPUT_H
+
+// ntiGet() returns the same static buffer each time.
+// Do not call it again before you are finished with the previous output.
+class NHttpTestInput {
+public:
+ NHttpTestInput(const char *fileName);
+ ~NHttpTestInput();
+ int32_t ntiGet(uint8_t **buffer, NHttpFlowData* sessionData, int64_t &testNumber);
+private:
+ FILE *msgFile;
+ uint8_t msgBuf[NHttpMsgHeader::MAXOCTETS];
+};
+
+#endif
+
--- /dev/null
+# A message section is the unit of data passed to the inspector and may by headers, body, a chunk, or trailing headers.
+# Blank lines separate message sections. Extra blank lines don't have any effect.
+# Lines beginning with '@' are control commands.
+# You cannot put comments or control commands in the middle of a message section.
+# If a message section begins with # or @ (presumably a very rare situation) you must escape it: \# or \@
+# Control commands:
+# @request or @response sets the message direction. Applies to subsequent sections until changed.
+# @tcpclose specifies that the section ends with a TCP close. Applies only to the very next section.
+# @<decimal number> sets the test number and hence the test output file name. Applies to subsequent sections until changed. Don't reuse numbers.
+# Commands must be all lower case with no white space.
+# The escape character is \ and it is used only within message sections.
+# \# and \@ are only necessary for the very first character of a message section. \r, \n, \t, \x, \X, and \\ may appear anywhere in a section.
+# \x and \X are identical and signify the beginning of an arbitrary two-digit hexadecimal escape value. Examples: 0x3A, 0X27, 0xeE. Case a-f or A-F
+# does not matter.
+
+@1
+@response
+HTTP/1.1 200 OK\r\n\r\n
+
+@2
+@response
+HTTP/1.0 315 Redirection test with spaces\r\n\r\n
+
+@3
+@request
+MKREDIRECTREF / HTTP/2.0\r\n\r\n
+
+@4
+@request
+BIND 1234567890?abcdef HTTP/3.8\r\n\r\n
+
+@5
+@response
+HTTP/2.0 600 Bogus-status-code\r\n\r\n
+
+@6
+@response
+HTTP/1.9 26 Short?status++co\nde\r\n\r\n
+
+@7
+@response
+HTTP/1.F 502 abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghij
+klmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstu
+vwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890\r\n\r\n
+
+@8
+@request
+GET /test/hi-there.txt HTTP/1.0\r\n\r\n
+
+@9
+@request
+GET /test/hi-there.htm HTTP/1.1\r\nAccept: text/*\r\nAccept-Language: en,fr\r\n\r\n
+
+@10
+@request
+GET /test/trigger/hi-there.htm HTTP/1.1\r\nAccept: text/*\r\nAccept-Language: en,fr\r\n\r\n
+
+@11
+@request
+@tcpclose
+GET /test/trigger/hi-there.htm HTTP/1.1\r\nAccept: text/*\r\nAccept-Language: en,fr\r\n\r\n
+
+@12
+@response
+HTTP/2.0 200 OK\r\nContent-type: \ttext/plain\t\r\nContent-LENGTH: 19\r\n\r\n
+
extern const BaseApi* sin_ftp_server;
extern const BaseApi* sin_rpc_decode;
extern const BaseApi* sin_telnet;
+extern const BaseApi* sin_nhttp;
#endif
const BaseApi* service_inspectors[] =
sin_ftp_server,
sin_rpc_decode,
sin_telnet,
+ sin_nhttp,
#endif
nullptr,
};