Pending - build 145
--- merge in new http changes: events from splitter plus additional
- refactoring and cleanup
+-- new http refactoring and cleanup
+-- new http changes - events from splitter
-- fix dns assertion; remove unused variables
15/03/31 - build 144
#include "detect.h"
#include "snort.h"
#include "snort_debug.h"
+// FIXIT-L temporary
+#include "service_inspectors/nhttp_inspect/nhttp_inspect.h"
#define DECODE_BLEN 65535
static inline void ClearHttpBuffers(void)
{
http_mask = 0;
+ // FIXIT-L temporary
+ NHttpInspect::latest_section = nullptr;
}
static inline uint32_t GetHttpBufferMask(void)
static const uint32_t NHTTP_GID = 219;
// 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_NOSOURCE=-6, STAT_NOTCONFIGURED=-5, STAT_NOTCOMPUTE=-4, STAT_INSUFMEMORY=-3,
- STAT_PROBLEMATIC=-2,
- STAT_NOTPRESENT=-1, STAT_EMPTYSTRING=0, STAT_OTHER=1 } StatusCode;
+// values are actual length or field value.
+enum StatusCode { STAT_NOSOURCE=-6, STAT_NOTCONFIGURED=-5, STAT_NOTCOMPUTE=-4, STAT_INSUFMEMORY=-3,
+ STAT_PROBLEMATIC=-2, STAT_NOTPRESENT=-1, STAT_EMPTYSTRING=0, STAT_OTHER=1 };
// Message originator--client or server
-typedef enum { SRC__NOTCOMPUTE=-4, SRC_CLIENT=0, SRC_SERVER=1 } SourceId;
+enum SourceId { SRC__NOTCOMPUTE=-4, SRC_CLIENT=0, SRC_SERVER=1 };
// Type of message section
-typedef enum { SEC_DISCARD = -10, SEC_CLOSED = -9, SEC_ABORT = -8, SEC__NOTCOMPUTE=-4,
- SEC__NOTPRESENT=-1,
- SEC_REQUEST = 2, SEC_STATUS, SEC_HEADER, SEC_BODY, SEC_CHUNK,
- SEC_TRAILER } SectionType;
+enum SectionType { SEC_DISCARD = -10, SEC_CLOSED = -9, SEC_ABORT = -8, SEC__NOTCOMPUTE=-4,
+ SEC__NOTPRESENT=-1, SEC_REQUEST = 2, SEC_STATUS, SEC_HEADER, SEC_BODY, SEC_CHUNK,
+ SEC_TRAILER };
// Result of scanning by splitter
-typedef enum { SCAN_NOTFOUND, SCAN_FOUND, SCAN_DISCARD, SCAN_DISCARD_CONTINUE,
- SCAN_ABORT } ScanResult;
+enum ScanResult { SCAN_NOTFOUND, SCAN_FOUND, SCAN_DISCARD, SCAN_DISCARD_CONTINUE, SCAN_ABORT };
// Result of processing a message section--what needs to happen next
-typedef enum { RES_INSPECT, RES_IGNORE, RES_AGGREGATE } ProcessResult;
+enum ProcessResult { RES_INSPECT, RES_IGNORE, RES_AGGREGATE };
// 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__NOSOURCE=-6, VERS__NOTCOMPUTE=-4, VERS__PROBLEMATIC=-2, VERS__NOTPRESENT=-1,
- VERS__OTHER=1,
- VERS_1_0, VERS_1_1, VERS_2_0 } VersionId;
+// HTTP/X.Y token. There would never be a message with "HTTP/0.9"
+enum VersionId { VERS__NOSOURCE=-6, VERS__NOTCOMPUTE=-4, VERS__PROBLEMATIC=-2, VERS__NOTPRESENT=-1,
+ VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 };
// Every request method we have ever heard of
-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;
+enum MethodId { 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 };
// URI formats
-typedef enum { URI__NOSOURCE=-6, URI__NOTCOMPUTE=-4, URI__PROBLEMATIC=-2, URI_ASTERISK = 2,
- URI_AUTHORITY, URI_ABSPATH,
- URI_ABSOLUTE } UriType;
+enum UriType { URI__NOSOURCE=-6, URI__NOTCOMPUTE=-4, URI__PROBLEMATIC=-2, URI_ASTERISK = 2,
+ URI_AUTHORITY, URI_ABSPATH, URI_ABSOLUTE };
// URI schemes
-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;
+enum SchemeId { 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 };
// 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_X_FORWARDED_FOR, HEAD_TRUE_CLIENT_IP,
- HEAD__MAXVALUE } HeaderId;
+enum HeaderId { 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_X_FORWARDED_FOR, HEAD_TRUE_CLIENT_IP, HEAD__MAXVALUE };
// All the infractions we might find while parsing and analyzing a message
-typedef enum
+enum Infraction
{
INF_TRUNCATED = 0,
- INF_HEADTOOLONG,
- INF_BADREQLINE,
- INF_BADSTATLINE,
- INF_TOOMANYHEADERS,
- INF_BADHEADER,
- INF_BADSTATCODE,
- INF_UNKNOWNVERSION,
- INF_BADVERSION,
- INF_NOSCRATCH,
- INF_BADHEADERREPS,
- INF_BADHEADERDATA,
+ INF_HEAD_TOO_LONG,
+ INF_BAD_REQ_LINE,
+ INF_BAD_STAT_LINE,
+ INF_TOO_MANY_HEADERS,
+ INF_BAD_HEADER,
+ INF_BAD_STAT_CODE,
+ INF_UNKNOWN_VERSION,
+ INF_BAD_VERSION,
+ INF_NO_SCRATCH,
+ INF_BAD_HEADER_REPS,
+ INF_BAD_HEADER_DATA,
INF_FIXIT_L_UNUSED,
- INF_BADCHUNKSIZE,
- INF_BADPHRASE,
- INF_BADURI,
- INF_BADPORT,
- INF_URINEEDNORM,
- INF_URIPERCENTNORMAL,
- INF_URIPERCENTASCII,
- INF_URIPERCENTUTF8,
- INF_URIPERCENTUCODE,
- INF_URIPERCENTOTHER,
- INF_URIBADCHAR,
- INF_URI8BITCHAR,
- INF_URIMULTISLASH,
- INF_URIBACKSLASH,
- INF_URISLASHDOT,
- INF_URISLASHDOTDOT,
- INF_URIROOTTRAV,
- INF_TOOMUCHLEADINGWS,
- INF_ENDLESSHEADER,
- INF_LFWITHOUTCR,
-} Infraction;
+ INF_BAD_CHUNK_SIZE,
+ INF_BAD_PHRASE,
+ INF_BAD_URI,
+ INF_BAD_PORT,
+ INF_URI_NEED_NORM,
+ INF_URI_PERCENT_NORMAL,
+ INF_URI_PERCENT_ASCII,
+ INF_URI_PERCENT_UTF8,
+ INF_URI_PERCENT_UCODE,
+ INF_URI_PERCENT_OTHER,
+ INF_URI_BAD_CHAR,
+ INF_URI_8BIT_CHAR,
+ INF_URI_MULTISLASH,
+ INF_URI_BACKSLASH,
+ INF_URI_SLASH_DOT,
+ INF_URI_SLASH_DOT_DOT,
+ INF_URI_ROOT_TRAV,
+ INF_TOO_MUCH_LEADING_WS,
+ INF_ENDLESS_HEADER,
+ INF_LF_WITHOUT_CR,
+};
// Formats for output from a header normalization function
-typedef enum { NORM_NULL, NORM_FIELD, NORM_INT64, NORM_ENUM64, NORM_ENUM64LIST } NormFormat;
+enum NormFormat { NORM_NULL, NORM_FIELD, NORM_INT64, NORM_ENUM64, NORM_ENUM64LIST };
// Types of character for URI scanning
-typedef enum { CHAR_NORMAL=2, CHAR_PERCENT, CHAR_PATH, CHAR_INVALID, CHAR_EIGHTBIT } CharAction;
+enum CharAction { CHAR_NORMAL=2, CHAR_PERCENT, CHAR_PATH, CHAR_INVALID, CHAR_EIGHTBIT };
// Transfer codings
-typedef enum { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSCODE_GZIP,
- TRANSCODE_COMPRESS,
- TRANSCODE_DEFLATE } Transcoding;
+enum Transcoding { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSCODE_GZIP,
+ TRANSCODE_COMPRESS, TRANSCODE_DEFLATE };
-typedef enum
+enum EventSid
{ // FIXIT-L limit 64 before code changes required
EVENT_ASCII = 1,
EVENT_DOUBLE_DECODE,
EVENT_PDF_PARSE_FAILURE,
EVENT_LOSS_OF_SYNC,
EVENT_MAXVALUE
-} EventSid;
+};
extern const int8_t as_hex[256];
} // end namespace NHttpEnums
void reset() { events_generated = 0; }
void create_event(NHttpEnums::EventSid sid)
{
- assert((sid > 0) && (sid <= 64));
+ assert(((int)sid > 0) && ((int)sid <= 64));
if ((events_generated & (((uint64_t)1) << (sid-1))) == 0)
{
SnortEventqAdd(NHttpEnums::NHTTP_GID, (uint32_t)sid);
fprintf(output, "\n");
return;
}
- const int32_t print_length = (length <= 1200) ? length : 1200; // Limit the amount of data
- // printed
+ // Limit the amount of data printed
+ const int32_t print_length = (length <= 1200) ? length : 1200;
for (int k=0; k < print_length; k++)
{
if ((start[k] >= 0x20) && (start[k] <= 0x7E))
{
public:
NHttpInfractions() { }
- NHttpInfractions(int inf) : infractions(((uint64_t)1) << inf) { assert((inf >= 0) && (inf < 64)); }
+ NHttpInfractions(int inf) : infractions(((uint64_t)1) << inf)
+ { assert((inf >= 0) && (inf < 64)); }
void reset() { infractions = 0; }
bool none_found() const { return infractions == 0; }
NHttpInfractions& operator+=(const NHttpInfractions& rhs)
{ return (lhs.infractions & rhs.infractions) != 0; }
// The following method is for convenience of debug and test output only! The 64-bit
- // implementation will not be big enough forever and this interface cannot be all over the
- // code.
+ // implementation will not be big enough forever and this interface cannot be all over
+ // the code.
uint64_t get_raw() const { return infractions; }
private:
#include "nhttp_msg_chunk.h"
#include "nhttp_msg_trailer.h"
#include "nhttp_test_manager.h"
+#include "nhttp_field.h"
#include "nhttp_inspect.h"
using namespace NHttpEnums;
}
}
+THREAD_LOCAL NHttpMsgSection* NHttpInspect::latest_section = nullptr;
+
bool NHttpInspect::get_buf(InspectionBuffer::Type ibt, Packet*, InspectionBuffer& b)
{
switch ( ibt )
bool NHttpInspect::get_buf(unsigned id, Packet*, InspectionBuffer& b)
{
- const HttpBuffer* h = GetHttpBuffer((HTTP_BUFFER)id);
+ if (latest_section == nullptr)
+ return false;
- if (!h)
- {
+ const Field& legacy = latest_section->get_legacy(id);
+
+ if (legacy.length <= 0)
return false;
- }
- b.data = h->buf;
- b.len = h->length;
+ b.data = legacy.start;
+ b.len = legacy.length;
return true;
}
NHttpFlowData::nhttp_flow_id);
assert(session_data != nullptr);
- NHttpMsgSection* msg_section = nullptr;
-
switch (session_data->section_type[source_id])
{
case SEC_REQUEST:
- msg_section = new NHttpMsgRequest(data, dsize, session_data, source_id, buf_owner);
+ latest_section = new NHttpMsgRequest(data, dsize, session_data, source_id, buf_owner);
break;
case SEC_STATUS:
- msg_section = new NHttpMsgStatus(data, dsize, session_data, source_id, buf_owner);
+ latest_section = new NHttpMsgStatus(data, dsize, session_data, source_id, buf_owner);
break;
case SEC_HEADER:
- msg_section = new NHttpMsgHeader(data, dsize, session_data, source_id, buf_owner);
+ latest_section = new NHttpMsgHeader(data, dsize, session_data, source_id, buf_owner);
break;
case SEC_BODY:
- msg_section = new NHttpMsgBody(data, dsize, session_data, source_id, buf_owner);
+ latest_section = new NHttpMsgBody(data, dsize, session_data, source_id, buf_owner);
break;
case SEC_CHUNK:
- msg_section = new NHttpMsgChunk(data, dsize, session_data, source_id, buf_owner);
+ latest_section = new NHttpMsgChunk(data, dsize, session_data, source_id, buf_owner);
break;
case SEC_TRAILER:
- msg_section = new NHttpMsgTrailer(data, dsize, session_data, source_id, buf_owner);
+ latest_section = new NHttpMsgTrailer(data, dsize, session_data, source_id, buf_owner);
break;
default:
assert(0);
return RES_IGNORE;
}
- msg_section->analyze();
- msg_section->update_flow();
- msg_section->gen_events();
-
- ProcessResult return_value = msg_section->worth_detection();
- if (return_value == RES_INSPECT)
- {
- msg_section->legacy_clients();
- }
+ latest_section->analyze();
+ latest_section->update_flow();
+ latest_section->gen_events();
if (NHttpTestManager::use_test_output())
{
- msg_section->print_section(NHttpTestManager::get_output_file());
+ latest_section->print_section(NHttpTestManager::get_output_file());
fflush(NHttpTestManager::get_output_file());
if (NHttpTestManager::use_test_input())
{
fflush(stdout);
}
- return return_value;
+ return latest_section->worth_detection();
}
#include "nhttp_stream_splitter.h"
class NHttpApi;
+class NHttpMsgSection;
class NHttpInspect : public Inspector
{
NHttpStreamSplitter(is_client_to_server, this);
}
+ static THREAD_LOCAL NHttpMsgSection* latest_section; // FIXIT-L temporarily public
+
private:
friend NHttpApi;
friend NHttpStreamSplitter;
if (tcp_close && (body_octets < data_length))
infractions += INF_TRUNCATED;
+ // FIXIT-L try to find a more logical location for this
+ set_file_data((uint8_t*)data.start, (unsigned)data.length);
}
void NHttpMsgBody::gen_events()
}
}
-// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgBody::legacy_clients()
-{
- ClearHttpBuffers();
- legacy_request();
- legacy_status();
- legacy_header(false);
- if (data.length > 0)
- {
- SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, data.start, (unsigned)data.length);
- set_file_data((uint8_t*)data.start, (unsigned)data.length);
- }
-}
-
void print_section(FILE* output) override;
void gen_events() override;
void update_flow() override;
- void legacy_clients() override;
+ Field& get_data() { return data; }
protected:
int64_t data_length; // FIXIT-M this has no meaning in chunk subclass. Potential source of errors.
}
if (bytes_used < msg_text.length)
{
- infractions += INF_TOOMANYHEADERS;
+ infractions += INF_TOO_MANY_HEADERS;
}
}
}
else
{
- infractions += INF_BADHEADER;
+ infractions += INF_BAD_HEADER;
}
}
}
uint8_t* lower_name;
if ((lower_name = scratch_pad.request(header_name[index].length)) == nullptr)
{
- infractions += INF_NOSCRATCH;
+ infractions += INF_NO_SCRATCH;
header_name_id[index] = HEAD__INSUFMEMORY;
return;
}
void NHttpMsgHeadShared::gen_events()
{
- if (infractions && INF_TOOMANYHEADERS)
+ if (infractions && INF_TOO_MANY_HEADERS)
events.create_event(EVENT_MAX_HEADERS);
}
void NHttpMsgHeader::update_flow()
{
- const uint64_t disaster_mask = 0;
-
// The following logic to determine body type is by no means the last word on this topic.
// FIXIT-H need to distinguish methods such as POST that should have a body from those that
// should not.
session_data->type_expected[source_id] = SEC_CLOSED;
session_data->half_reset(source_id);
}
- else if (infractions && disaster_mask)
- {
- session_data->type_expected[source_id] = SEC_ABORT;
- session_data->half_reset(source_id);
- }
else if ((source_id == SRC_SERVER) && ((status_code_num <= 199) || (status_code_num == 204) ||
(status_code_num == 304)))
{
? RES_INSPECT : RES_IGNORE;
}
-// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgHeader::legacy_clients()
-{
- ClearHttpBuffers();
- legacy_request();
- legacy_status();
- legacy_header(false);
-}
-
void gen_events() override;
void update_flow() override;
NHttpEnums::ProcessResult worth_detection() override;
- void legacy_clients() override;
};
#endif
if (start_line.start[start_line.length-9] != ' ')
{
// space before "HTTP" missing or in wrong place
- infractions += INF_BADREQLINE;
+ infractions += INF_BAD_REQ_LINE;
return;
}
if (space >= start_line.length-9)
{
// leading space or no space
- infractions += INF_BADREQLINE;
+ infractions += INF_BAD_REQ_LINE;
return;
}
events.create_event(EVENT_UNKNOWN_METHOD);
// URI character encoding events
- if (uri && (uri->get_uri_infractions() && INF_URIPERCENTASCII))
+ if (uri && (uri->get_uri_infractions() && INF_URI_PERCENT_ASCII))
events.create_event(EVENT_ASCII);
- if (uri && (uri->get_uri_infractions() && INF_URIPERCENTUCODE))
+ if (uri && (uri->get_uri_infractions() && INF_URI_PERCENT_UCODE))
events.create_event(EVENT_U_ENCODE);
- if (uri && (uri->get_uri_infractions() && INF_URI8BITCHAR))
+ if (uri && (uri->get_uri_infractions() && INF_URI_8BIT_CHAR))
events.create_event(EVENT_BARE_BYTE);
- if (uri && (uri->get_uri_infractions() && INF_URIPERCENTUTF8))
+ if (uri && (uri->get_uri_infractions() && INF_URI_PERCENT_UTF8))
events.create_event(EVENT_UTF_8);
- if (uri && (uri->get_uri_infractions() && INF_URIBADCHAR))
+ if (uri && (uri->get_uri_infractions() && INF_URI_BAD_CHAR))
events.create_event(EVENT_NON_RFC_CHAR);
// URI path events
- if (uri && (uri->get_path_infractions() && INF_URIMULTISLASH))
+ if (uri && (uri->get_path_infractions() && INF_URI_MULTISLASH))
events.create_event(EVENT_MULTI_SLASH);
- if (uri && (uri->get_path_infractions() && INF_URIBACKSLASH))
+ if (uri && (uri->get_path_infractions() && INF_URI_BACKSLASH))
events.create_event(EVENT_IIS_BACKSLASH);
- if (uri && (uri->get_path_infractions() && INF_URISLASHDOT))
+ if (uri && (uri->get_path_infractions() && INF_URI_SLASH_DOT))
events.create_event(EVENT_SELF_DIR_TRAV);
- if (uri && (uri->get_path_infractions() && INF_URISLASHDOTDOT))
+ if (uri && (uri->get_path_infractions() && INF_URI_SLASH_DOT_DOT))
events.create_event(EVENT_DIR_TRAV);
- if (uri && (uri->get_path_infractions() && INF_URIROOTTRAV))
+ if (uri && (uri->get_path_infractions() && INF_URI_ROOT_TRAV))
events.create_event(EVENT_WEBROOT_DIR);
}
void NHttpMsgRequest::update_flow()
{
- const uint64_t disaster_mask = INF_BADREQLINE;
-
// The following logic to determine body type is by no means the last word on this topic.
if (tcp_close)
{
session_data->type_expected[source_id] = SEC_CLOSED;
session_data->half_reset(source_id);
}
- else if (infractions && disaster_mask)
+ else if (infractions && INF_BAD_REQ_LINE)
{
session_data->type_expected[source_id] = SEC_ABORT;
session_data->half_reset(source_id);
session_data->section_type[source_id] = SEC__NOTCOMPUTE;
}
-// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgRequest::legacy_clients()
-{
- ClearHttpBuffers();
- legacy_request();
-}
-
void print_section(FILE* output) override;
void gen_events() override;
void update_flow() override;
- void legacy_clients() override;
const Field& get_method() { return method; }
const Field& get_uri();
const Field& get_uri_norm_legacy();
#include "nhttp_msg_request.h"
#include "nhttp_msg_status.h"
#include "nhttp_msg_head_shared.h"
+#include "nhttp_msg_body.h"
using namespace NHttpEnums;
msg_text.print(output, "Input");
}
-void NHttpMsgSection::print_message_wrapup(FILE* output) const
+void NHttpMsgSection::print_message_wrapup(FILE* output)
{
fprintf(output, "Infractions: %" PRIx64 ", Events: %" PRIx64 ", TCP Close: %s\n",
infractions.get_raw(), events.get_raw(), tcp_close ? "True" : "False");
- fprintf(output, "Interface to old clients. http_mask = %x.\n", http_mask);
- for (int i=0; i < HTTP_BUFFER_MAX; i++)
+ for (unsigned k=1; k < HTTP_BUFFER_MAX; k++)
{
- if ((1 << i) & http_mask)
- Field(http_buffer[i].length, http_buffer[i].buf).print(output, http_buffer_name[i]);
+ get_legacy(k).print(output, http_buffer_name[k]);
}
if (g_file_data.len > 0)
{
fprintf(output, "\n");
}
-void NHttpMsgSection::legacy_request()
+const Field& NHttpMsgSection::get_legacy(unsigned buffer_id)
{
- NHttpMsgRequest* const request = transaction->get_request();
- if (request == nullptr)
- return;
- if (request->get_method().length > 0)
+ // When current section is trailers, that is what will be used for header and cookie buffers.
+ switch (buffer_id)
{
- SetHttpBuffer(HTTP_BUFFER_METHOD, request->get_method().start,
- (unsigned)request->get_method().length);
- }
- if (request->get_uri().length > 0)
- {
- SetHttpBuffer(HTTP_BUFFER_RAW_URI, request->get_uri().start,
- (unsigned)request->get_uri().length);
- }
- if (request->get_uri_norm_legacy().length > 0)
- {
- SetHttpBuffer(HTTP_BUFFER_URI, request->get_uri_norm_legacy().start,
- (unsigned)request->get_uri_norm_legacy().length);
- }
-}
-
-void NHttpMsgSection::legacy_status()
-{
- NHttpMsgStatus* const status = transaction->get_status();
- if (status == nullptr)
- return;
- if (status->get_status_code().length > 0)
- {
- SetHttpBuffer(HTTP_BUFFER_STAT_CODE, status->get_status_code().start,
- (unsigned)status->get_status_code().length);
- }
- if (status->get_reason_phrase().length > 0)
- {
- SetHttpBuffer(HTTP_BUFFER_STAT_MSG, status->get_reason_phrase().start,
- (unsigned)status->get_reason_phrase().length);
- }
-}
-
-void NHttpMsgSection::legacy_header(bool use_trailer)
-{
- NHttpMsgHeadShared* const header = use_trailer ?
- (NHttpMsgHeadShared*)transaction->get_trailer(source_id) :
- (NHttpMsgHeadShared*)transaction->get_header(source_id);
- if (header == nullptr)
- return;
-
- if (header->get_headers().length > 0)
- {
- SetHttpBuffer(HTTP_BUFFER_RAW_HEADER, header->get_headers().start,
- (unsigned)header->get_headers().length);
- SetHttpBuffer(HTTP_BUFFER_HEADER, header->get_headers().start,
- (unsigned)header->get_headers().length);
- }
-
- legacy_cookie(header, source_id);
-}
-
-// FIXIT-M there can be multiple cookie headers in one message.
-void NHttpMsgSection::legacy_cookie(NHttpMsgHeadShared* header, SourceId source_id)
-{
- HeaderId cookie_head = (source_id == SRC_CLIENT) ? HEAD_COOKIE : HEAD_SET_COOKIE;
-
- for (int k=0; k < header->get_num_headers(); k++)
- {
- if (header->get_header_name_id(k) == cookie_head)
+ case HTTP_BUFFER_CLIENT_BODY:
+ {
+ NHttpMsgBody* body = transaction->get_body();
+ return (body != nullptr) ? body->get_data() : Field::FIELD_NULL;
+ }
+ case HTTP_BUFFER_COOKIE:
+ {
+ NHttpMsgHeadShared* header = transaction->get_latest_header(source_id);
+ if (header == nullptr)
+ return Field::FIELD_NULL;
+ HeaderId cookie_head = (source_id == SRC_CLIENT) ? HEAD_COOKIE : HEAD_SET_COOKIE;
+ return header->get_header_value_norm(cookie_head);
+ }
+ case HTTP_BUFFER_HEADER:
+ {
+ NHttpMsgHeadShared* header = transaction->get_latest_header(source_id);
+ return (header != nullptr) ? header->get_headers() : Field::FIELD_NULL;
+ }
+ case HTTP_BUFFER_METHOD:
+ {
+ NHttpMsgRequest* request = transaction->get_request();
+ return (request != nullptr) ? request->get_method() : Field::FIELD_NULL;
+ }
+ case HTTP_BUFFER_RAW_COOKIE:
+ {
+ NHttpMsgHeadShared* header = transaction->get_latest_header(source_id);
+ if (header == nullptr)
+ return Field::FIELD_NULL;
+ HeaderId cookie_head = (source_id == SRC_CLIENT) ? HEAD_COOKIE : HEAD_SET_COOKIE;
+ // FIXIT-M there can be multiple cookie header in one message.
+ for (int k=0; k < header->get_num_headers(); k++)
{
- if (header->get_header_value(k).length > 0)
- SetHttpBuffer(HTTP_BUFFER_RAW_COOKIE, header->get_header_value(k).start,
- (unsigned)header->get_header_value(k).length);
- break;
+ if (header->get_header_name_id(k) == cookie_head)
+ {
+ return header->get_header_value(k);
+ }
}
- }
-
- if (header->get_header_value_norm(cookie_head).length > 0)
- {
- SetHttpBuffer(HTTP_BUFFER_COOKIE, header->get_header_value_norm(cookie_head).start,
- (unsigned)header->get_header_value_norm(cookie_head).length);
+ return Field::FIELD_NULL;
+ }
+ case HTTP_BUFFER_RAW_HEADER:
+ {
+ NHttpMsgHeadShared* header = transaction->get_latest_header(source_id);
+ return (header != nullptr) ? header->get_headers() : Field::FIELD_NULL;
+ }
+ case HTTP_BUFFER_RAW_URI:
+ {
+ NHttpMsgRequest* request = transaction->get_request();
+ return (request != nullptr) ? request->get_uri() : Field::FIELD_NULL;
+ }
+ case HTTP_BUFFER_STAT_CODE:
+ {
+ NHttpMsgStatus* status = transaction->get_status();
+ return (status != nullptr) ? status->get_status_code() : Field::FIELD_NULL;
+ }
+ case HTTP_BUFFER_STAT_MSG:
+ {
+ NHttpMsgStatus* status = transaction->get_status();
+ return (status != nullptr) ? status->get_reason_phrase() : Field::FIELD_NULL;
+ }
+ case HTTP_BUFFER_URI:
+ {
+ NHttpMsgRequest* request = transaction->get_request();
+ return (request != nullptr) ? request->get_uri_norm_legacy() : Field::FIELD_NULL;
+ }
+ default:
+ assert(0);
+ return Field::FIELD_NULL;
}
}
#ifndef NHTTP_MSG_SECTION_H
#define NHTTP_MSG_SECTION_H
+#include "detection/detection_util.h"
+
#include "nhttp_scratch_pad.h"
#include "nhttp_field.h"
#include "nhttp_flow_data.h"
// Manages the splitter and communication between message sections
virtual void update_flow() = 0;
- // Populates the raw and normalized buffer interface used by old Snort
- virtual void legacy_clients() = 0;
+ const Field& get_legacy(unsigned buffer_id);
// Should this section be sent directly to detection after inspection?
virtual NHttpEnums::ProcessResult worth_detection() { return NHttpEnums::RES_INSPECT; }
// Convenience methods
void print_message_title(FILE* output, const char* title) const;
- void print_message_wrapup(FILE* output) const;
- void legacy_request();
- void legacy_status();
- void legacy_header(bool use_trailer);
- void legacy_cookie(NHttpMsgHeadShared* header, NHttpEnums::SourceId source_id);
+ void print_message_wrapup(FILE* output);
const Field msg_text;
if (version.length != 8)
{
version_id = VERS__PROBLEMATIC;
- infractions += INF_BADVERSION;
+ infractions += INF_BAD_VERSION;
return;
}
if (memcmp(version.start, "HTTP/", 5) || (version.start[6] != '.'))
{
version_id = VERS__PROBLEMATIC;
- infractions += INF_BADVERSION;
+ infractions += INF_BAD_VERSION;
}
else if ((version.start[5] == '1') && (version.start[7] == '1'))
{
(version.start[7] >= '0') && (version.start[7] <= '9'))
{
version_id = VERS__OTHER;
- infractions += INF_UNKNOWNVERSION;
+ infractions += INF_UNKNOWN_VERSION;
}
else
{
version_id = VERS__PROBLEMATIC;
- infractions += INF_BADVERSION;
+ infractions += INF_BAD_VERSION;
}
}
// as error. HTTP/X.Y<SP>###<SP><text>
if ((start_line.length < 13) || (start_line.start[8] != ' ') || (start_line.start[12] != ' '))
{
- infractions += INF_BADSTATLINE;
+ infractions += INF_BAD_STAT_LINE;
return;
}
version.start = start_line.start;
if ((reason_phrase.start[k] <= 31) || (reason_phrase.start[k] >= 127))
{
// Illegal character in reason phrase
- infractions += INF_BADPHRASE;
+ infractions += INF_BAD_PHRASE;
break;
}
}
'0') || (status_code.start[1] > '9') ||
(status_code.start[2] < '0') || (status_code.start[2] > '9'))
{
- infractions += INF_BADSTATCODE;
+ infractions += INF_BAD_STAT_CODE;
status_code_num = STAT_PROBLEMATIC;
return;
}
(status_code.start[2] - '0');
if ((status_code_num < 100) || (status_code_num > 599))
{
- infractions += INF_BADSTATCODE;
+ infractions += INF_BAD_STAT_CODE;
}
}
void NHttpMsgStatus::update_flow()
{
- const uint64_t disaster_mask = INF_BADSTATLINE;
-
// The following logic to determine body type is by no means the last word on this topic.
if (tcp_close)
{
session_data->type_expected[source_id] = SEC_CLOSED;
session_data->half_reset(source_id);
}
- else if (infractions && disaster_mask)
+ else if (infractions && INF_BAD_STAT_LINE)
{
session_data->type_expected[source_id] = SEC_ABORT;
session_data->half_reset(source_id);
session_data->section_type[source_id] = SEC__NOTCOMPUTE;
}
-// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgStatus::legacy_clients()
-{
- ClearHttpBuffers();
- legacy_request();
- legacy_status();
-}
-
void print_section(FILE* output) override;
void gen_events() override;
void update_flow() override;
- void legacy_clients() override;
const Field& get_status_code() { return status_code; }
const Field& get_reason_phrase() { return reason_phrase; }
return (msg_text.length > 0) ? RES_INSPECT : RES_IGNORE;
}
-// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgTrailer::legacy_clients()
-{
- ClearHttpBuffers();
- legacy_request();
- legacy_status();
- legacy_header(true);
-}
-
void gen_events() override;
void update_flow() override;
NHttpEnums::ProcessResult worth_detection() override;
- void legacy_clients() override;
};
#endif
non_leading_zeros++;
if (non_leading_zeros > 18)
{
- infractions += INF_BADHEADERDATA;
+ infractions += INF_BAD_HEADER_DATA;
return STAT_PROBLEMATIC;
}
if ((value < 0) || (value > 9))
{
- infractions += INF_BADHEADERDATA;
+ infractions += INF_BAD_HEADER_DATA;
return STAT_PROBLEMATIC;
}
total = total*10 + value;
}
else
{
- infractions += INF_TOOMUCHLEADINGWS;
+ infractions += INF_TOO_MUCH_LEADING_WS;
events.create_event(EVENT_LOSS_OF_SYNC);
return SCAN_ABORT;
}
// Alert on \n not preceded by \r. Correct cases are \r\n\r\n and \r\n.
if (!((num_crlf == 4) || ((num_crlf == 2) && (first_lf == 0))))
{
- infractions += INF_LFWITHOUTCR;
+ infractions += INF_LF_WITHOUT_CR;
events.create_event(EVENT_IIS_DELIMITER);
}
num_flush = k + 1 + peek_octets;
case SCAN_NOTFOUND:
if (splitter->get_octets_seen() == MAXOCTETS)
{
- session_data->infractions[source_id] += INF_ENDLESSHEADER;
+ session_data->infractions[source_id] += INF_ENDLESS_HEADER;
session_data->events[source_id].create_event(EVENT_LOSS_OF_SYNC);
// FIXIT-H need to process this data (except chunk header) not just discard it.
session_data->type_expected[source_id] = SEC_ABORT;
#include "nhttp_msg_status.h"
#include "nhttp_msg_header.h"
#include "nhttp_msg_trailer.h"
+#include "nhttp_msg_body.h"
using namespace NHttpEnums;
}
else if (!session_data->add_to_pipeline(session_data->transaction[SRC_CLIENT]))
{
- // The pipeline is full and just overflowed. FIXIT-M we should alert and set
- // infraction.
+ // The pipeline is full and just overflowed. FIXIT-M alert and set infraction.
delete session_data->transaction[SRC_CLIENT];
}
}
class NHttpMsgHeader;
class NHttpMsgTrailer;
class NHttpMsgSection;
+class NHttpMsgBody;
+class NHttpMsgHeadShared;
class NHttpTransaction
{
NHttpMsgHeader* get_header(NHttpEnums::SourceId source_id) const { return header[source_id]; }
void set_header(NHttpMsgHeader* header_, NHttpEnums::SourceId source_id)
- {
- header[source_id] =
- header_;
- }
+ { header[source_id] = header_; }
NHttpMsgTrailer* get_trailer(NHttpEnums::SourceId source_id) const
+ { return trailer[source_id]; }
+ void set_trailer(NHttpMsgTrailer* trailer_, NHttpEnums::SourceId source_id)
+ { trailer[source_id] = trailer_; }
+
+ NHttpMsgBody* get_body() const { return latest_body; }
+ void set_body(NHttpMsgBody* latest_body_) { latest_body = latest_body_; }
+
+ // Convenience method
+ NHttpMsgHeadShared* get_latest_header(NHttpEnums::SourceId source_id)
{
- return trailer[source_id];
+ return (trailer[source_id] != nullptr) ? (NHttpMsgHeadShared*)trailer[source_id] :
+ (NHttpMsgHeadShared*)header[source_id];
}
- void set_trailer(NHttpMsgTrailer* trailer_, NHttpEnums::SourceId
- source_id) { trailer[source_id] = trailer_; }
-
- void set_body(NHttpMsgSection* latest_body_) { latest_body = latest_body_; }
private:
NHttpTransaction() = default;
NHttpMsgStatus* status = nullptr;
NHttpMsgHeader* header[2] = { nullptr, nullptr };
NHttpMsgTrailer* trailer[2] = { nullptr, nullptr };
- NHttpMsgSection* latest_body = nullptr;
+ NHttpMsgBody* latest_body = nullptr;
};
#endif
}
else
{
- format_infractions += INF_BADURI;
+ format_infractions += INF_BAD_URI;
uri_type = URI__PROBLEMATIC;
scheme.length = STAT_PROBLEMATIC;
authority.length = STAT_PROBLEMATIC;
uint8_t* lower_scheme;
if ((lower_scheme = scratch_pad.request(scheme.length)) == nullptr)
{
- scheme_infractions += INF_NOSCRATCH;
+ scheme_infractions += INF_NO_SCRATCH;
scheme_id = SCH__INSUFMEMORY;
return scheme_id;
}
port_value = port_value * 10 + (port.start[k] - '0');
if ((port.start[k] < '0') || (port.start[k] > '9') || (port_value > 65535))
{
- port_infractions += INF_BADPORT;
+ port_infractions += INF_BAD_PORT;
port_value = STAT_PROBLEMATIC;
break;
}
{
if ((uri_char[in_buf[k]] == CHAR_NORMAL) || (uri_char[in_buf[k]] == CHAR_PATH))
continue;
- infractions += INF_URINEEDNORM;
+ infractions += INF_URI_NEED_NORM;
return false;
}
return true;
continue;
if ((in_buf[k] == '/') && ((k == 0) || (in_buf[k-1] != '/')))
continue;
- infractions += INF_URINEEDNORM;
+ infractions += INF_URI_NEED_NORM;
return false;
}
return true;
out_buf[length++] = in_buf[k];
break;
case CHAR_INVALID:
- infractions += INF_URIBADCHAR;
+ infractions += INF_URI_BAD_CHAR;
out_buf[length++] = in_buf[k];
break;
case CHAR_EIGHTBIT:
- infractions += INF_URI8BITCHAR;
+ infractions += INF_URI_8BIT_CHAR;
out_buf[length++] = in_buf[k];
break;
case CHAR_PERCENT:
{
// Normal % escape of an ASCII special character that is supposed to be
// escaped
- infractions += INF_URIPERCENTNORMAL;
+ infractions += INF_URI_PERCENT_NORMAL;
out_buf[length++] = '%';
}
else
{
// Suspicious % escape of an ASCII character that does not need to be
// escaped
- infractions += INF_URIPERCENTASCII;
+ infractions += INF_URI_PERCENT_ASCII;
if (uri_char[value] == CHAR_INVALID)
- infractions += INF_URIBADCHAR;
+ infractions += INF_URI_BAD_CHAR;
out_buf[length++] = value;
k += 2;
}
else
{
// UTF-8 decoding not implemented yet
- infractions += INF_URIPERCENTUTF8;
+ infractions += INF_URI_PERCENT_UTF8;
out_buf[length++] = '%';
}
}
&& (as_hex[in_buf[k+4]] != -1) && (as_hex[in_buf[k+5]] != -1))
{
// 'u' UTF-16 decoding not implemented yet
- infractions += INF_URIPERCENTUCODE;
+ infractions += INF_URI_PERCENT_UCODE;
out_buf[length++] = '%';
}
else
{
// Don't recognize it
- infractions += INF_URIPERCENTOTHER;
+ infractions += INF_URI_PERCENT_OTHER;
out_buf[length++] = '%';
}
break;
else
{
out_buf[k] = '/';
- infractions += INF_URIBACKSLASH;
+ infractions += INF_URI_BACKSLASH;
}
}
return in_length;
// Ignore this slash if it directly follows another slash
else if ((k < in_length) && (length >= 1) && (out_buf[length-1] == '/'))
{
- infractions += INF_URIMULTISLASH;
+ infractions += INF_URI_MULTISLASH;
}
// This slash is the end of a /./ pattern, ignore this slash and remove the period from the
// output
else if ((length >= 2) && (out_buf[length-1] == '.') && (out_buf[length-2] == '/'))
{
- infractions += INF_URISLASHDOT;
+ infractions += INF_URI_SLASH_DOT;
length -= 1;
}
// This slash is the end of a /../ pattern, normalization depends on whether there is a
else if ((length >= 3) && (out_buf[length-1] == '.') && (out_buf[length-2] == '.') &&
(out_buf[length-3] == '/'))
{
- infractions += INF_URISLASHDOTDOT;
+ infractions += INF_URI_SLASH_DOT_DOT;
// Traversing above the root of the absolute path. A path of the form
// /../../../foo/bar/whatever cannot be further normalized. Instead of taking away a
// directory we leave the .. and write out the new slash. This code can write out the
((length >= 6) && (out_buf[length-4] == '.') && (out_buf[length-5] == '.') &&
(out_buf[length-6] == '/')))
{
- infractions += INF_URIROOTTRAV;
+ infractions += INF_URI_ROOT_TRAV;
out_buf[length++] = '/';
}
// Remove the previous directory from the output. "/foo/bar/../" becomes "/foo/"