doc/snort_manual.chunked/
doc/snort_manual.html
doc/snort_manual.pdf
+doc/snort_manual.text
doc/snort_manual.tgz
doc/snort_manual.xml
doc/version.txt
missing
snort
snort.pc
+src/framework/api_options.h
+src/framework/stamp-h2
src/snort
src/tags
src/test/suite_decl.h
+Pending - build 154
+
+-- new_http_inspect parsing and event handling updates
+
15/05/22 - build 153
-- new_http_inspect parsing updates
INF_STATUS_WS,
INF_STATUS_TAB,
INF_URI_SPACE,
+ INF_TOO_LONG_HEADER,
};
// Formats for output from a header normalization function
EVENT_IMPROPER_WS,
EVENT_BAD_VERS,
EVENT_UNKNOWN_VERS,
+ EVENT_BAD_HEADER,
EVENT_MAXVALUE
};
{ infractions |= rhs.infractions; return *this; }
friend NHttpInfractions operator+(NHttpInfractions lhs, const NHttpInfractions& rhs)
{ lhs += rhs; return lhs; }
- friend bool operator&&(const NHttpInfractions& lhs, const NHttpInfractions& rhs)
+ friend bool operator&(const NHttpInfractions& lhs, const NHttpInfractions& rhs)
{ return (lhs.infractions & rhs.infractions) != 0; }
// The following method is for convenience of debug and test output only! The 64-bit
int32_t bytes_used = 0;
num_headers = 0;
int num_seps;
+ // session_data->num_head_lines is computed without consideration of wrapping and may overstate
+ // actual number of headers. Rely on num_headers which is calculated correctly.
header_line = new Field[session_data->num_head_lines[source_id]];
while (bytes_used < msg_text.length)
{
header_line[num_headers].start = msg_text.start + bytes_used;
header_line[num_headers].length = find_header_end(header_line[num_headers].start,
- msg_text.length - bytes_used, &num_seps);
+ msg_text.length - bytes_used, num_seps);
assert(num_headers < session_data->num_head_lines[source_id]);
+ if (header_line[num_headers].length > MAX_HEADER_LENGTH)
+ {
+ infractions += INF_TOO_LONG_HEADER;
+ events.create_event(EVENT_LONG_HDR);
+ }
bytes_used += header_line[num_headers++].length + num_seps;
- if (num_headers >= MAXHEADERS)
+ if (num_headers >= MAX_HEADERS)
{
break;
}
}
if (bytes_used < msg_text.length)
{
+ // FIXIT-M eventually need to separate max header alert from internal maximum
infractions += INF_TOO_MANY_HEADERS;
+ events.create_event(EVENT_MAX_HEADERS);
}
}
// The final header in the block will not be terminated by CRLF (splitter design) but will
// terminate at the end of the buffer. length is returned.
//
-// Bare LF without CR is accepted as the terminator unless preceded by backslash character. FIXIT-L
-// this does not consider whether \LF is contained within a quoted string and perhaps this should
-// be revisited. The current approach errs in the direction of not incorrectly dividing a single
-// header into two headers.
-//
-// FIXIT-M any abuse of backslashes in headers should be a preprocessor alert.
+// Bare LF without CR is accepted as the terminator.
-uint32_t NHttpMsgHeadShared::find_header_end(const uint8_t* buffer, int32_t length, int* const
- num_seps)
+uint32_t NHttpMsgHeadShared::find_header_end(const uint8_t* buffer, int32_t length, int& num_seps)
{
- for (int32_t k=0; k < length-1; k++)
+ // k=1 because the splitter would not give us a header consisting solely of LF.
+ for (int32_t k=1; k < length; k++)
{
- if ((buffer[k] != '\\') && (buffer[k+1] == '\n'))
+ if (buffer[k] == '\n')
{
- if ((k+2 >= length) || ((buffer[k+2] != ' ') && (buffer[k+2] != '\t')))
+ // Check for wrapping
+ if ((k+1 == length) || !is_sp_tab[buffer[k+1]])
{
- *num_seps = (buffer[k] == '\r') ? 2 : 1;
- return k + 2 - *num_seps;
+ num_seps = (buffer[k-1] == '\r') ? 2 : 1;
+ if (num_seps == 1)
+ {
+ infractions += INF_LF_WITHOUT_CR;
+ events.create_event(EVENT_IIS_DELIMITER);
+ }
+ return k + 1 - num_seps;
}
}
}
- *num_seps = 0;
+ num_seps = 0;
return length;
}
// Divide header field lines into field name and field value
void NHttpMsgHeadShared::parse_header_lines()
{
- header_name = new Field[session_data->num_head_lines[source_id]];
- header_value = new Field[session_data->num_head_lines[source_id]];
- header_name_id = new HeaderId[session_data->num_head_lines[source_id]];
+ header_name = new Field[num_headers];
+ header_value = new Field[num_headers];
+ header_name_id = new HeaderId[num_headers];
int colon;
for (int k=0; k < num_headers; k++)
else
{
infractions += INF_BAD_HEADER;
+ events.create_event(EVENT_BAD_HEADER);
}
}
}
const Field& NHttpMsgHeadShared::get_header_value_norm(NHttpEnums::HeaderId header_id)
{
header_norms[header_id]->normalize(header_id, header_count[header_id], scratch_pad,
- infractions,
- header_name_id, header_value, num_headers, header_value_norm[header_id]);
+ infractions, header_name_id, header_value, num_headers, header_value_norm[header_id]);
return header_value_norm[header_id];
}
-void NHttpMsgHeadShared::gen_events()
-{
- if (infractions && INF_TOO_MANY_HEADERS)
- events.create_event(EVENT_MAX_HEADERS);
-}
-
void NHttpMsgHeadShared::print_headers(FILE* output)
{
char title_buf[100];
~NHttpMsgHeadShared();
void analyze() override;
- void gen_events() override;
int32_t get_num_headers() const { return num_headers; }
const Field& get_headers() const { return msg_text; }
static const StrCode trans_code_list[];
void parse_header_block();
- static uint32_t find_header_end(const uint8_t* buffer, int32_t length, int* const num_seps);
+ uint32_t find_header_end(const uint8_t* buffer, int32_t length, int& num_seps);
void parse_header_lines();
void derive_header_name_id(int index);
void print_headers(FILE* output);
// All of these are indexed by the relative position of the header field in the message
- static const int MAXHEADERS = 200; // I'm an arbitrary number. FIXIT-L
+ static const int MAX_HEADERS = 200; // I'm an arbitrary number. FIXIT-L
+ static const int MAX_HEADER_LENGTH = 4096; // Based on max cookie size of some browsers
int32_t num_headers = NHttpEnums::STAT_NOTCOMPUTE;
Field* header_line = nullptr;
Field* header_name = nullptr;
void NHttpMsgHeader::gen_events()
{
- NHttpMsgHeadShared::gen_events();
if (header_count[HEAD_CONTENT_LENGTH] > 1)
events.create_event(EVENT_MULTIPLE_CONTLEN);
}
void NHttpMsgRequest::gen_events()
{
- if (infractions && INF_BAD_REQ_LINE)
+ if (infractions & INF_BAD_REQ_LINE)
return;
if ((start_line.start[method.length] == '\t') ||
events.create_event(EVENT_UNKNOWN_METHOD);
// URI character encoding events
- if (uri && (uri->get_uri_infractions() && INF_URI_PERCENT_ASCII))
+ if (uri && (uri->get_uri_infractions() & INF_URI_PERCENT_ASCII))
events.create_event(EVENT_ASCII);
- if (uri && (uri->get_uri_infractions() && INF_URI_PERCENT_UCODE))
+ if (uri && (uri->get_uri_infractions() & INF_URI_PERCENT_UCODE))
events.create_event(EVENT_U_ENCODE);
- if (uri && (uri->get_uri_infractions() && INF_URI_8BIT_CHAR))
+ if (uri && (uri->get_uri_infractions() & INF_URI_8BIT_CHAR))
events.create_event(EVENT_BARE_BYTE);
- if (uri && (uri->get_uri_infractions() && INF_URI_PERCENT_UTF8))
+ if (uri && (uri->get_uri_infractions() & INF_URI_PERCENT_UTF8))
events.create_event(EVENT_UTF_8);
- if (uri && (uri->get_uri_infractions() && INF_URI_BAD_CHAR))
+ 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_URI_MULTISLASH))
+ if (uri && (uri->get_path_infractions() & INF_URI_MULTISLASH))
events.create_event(EVENT_MULTI_SLASH);
- if (uri && (uri->get_path_infractions() && INF_URI_BACKSLASH))
+ if (uri && (uri->get_path_infractions() & INF_URI_BACKSLASH))
events.create_event(EVENT_IIS_BACKSLASH);
- if (uri && (uri->get_path_infractions() && INF_URI_SLASH_DOT))
+ if (uri && (uri->get_path_infractions() & INF_URI_SLASH_DOT))
events.create_event(EVENT_SELF_DIR_TRAV);
- if (uri && (uri->get_path_infractions() && INF_URI_SLASH_DOT_DOT))
+ if (uri && (uri->get_path_infractions() & INF_URI_SLASH_DOT_DOT))
events.create_event(EVENT_DIR_TRAV);
- if (uri && (uri->get_path_infractions() && INF_URI_ROOT_TRAV))
+ if (uri && (uri->get_path_infractions() & INF_URI_ROOT_TRAV))
events.create_event(EVENT_WEBROOT_DIR);
}
void NHttpMsgRequest::update_flow()
{
// The following logic to determine body type is by no means the last word on this topic.
- if (infractions && INF_BAD_REQ_LINE)
+ if (infractions & INF_BAD_REQ_LINE)
{
session_data->type_expected[source_id] = SEC_ABORT;
session_data->half_reset(source_id);
void NHttpMsgStatus::gen_events()
{
- if (infractions && INF_BAD_STAT_LINE)
+ if (infractions & INF_BAD_STAT_LINE)
return;
if (status_code.start > start_line.start + 9)
void NHttpMsgStatus::update_flow()
{
// The following logic to determine body type is by no means the last word on this topic.
- if (infractions && INF_BAD_STAT_LINE)
+ if (infractions & INF_BAD_STAT_LINE)
{
session_data->type_expected[source_id] = SEC_ABORT;
session_data->half_reset(source_id);
using namespace NHttpEnums;
NHttpMsgTrailer::NHttpMsgTrailer(const uint8_t* buffer, const uint16_t buf_size,
- NHttpFlowData* session_data_,
- SourceId source_id_, bool buf_owner) :
+ NHttpFlowData* session_data_, SourceId source_id_, bool buf_owner) :
NHttpMsgHeadShared(buffer, buf_size, session_data_, source_id_, buf_owner)
{
transaction->set_trailer(this, source_id);
void NHttpMsgTrailer::gen_events()
{
- NHttpMsgHeadShared::gen_events();
}
void NHttpMsgTrailer::print_section(FILE* output)
{ EVENT_IMPROPER_WS, "Illegal extra whitespace in start line" },
{ EVENT_BAD_VERS, "Corrupted HTTP version" },
{ EVENT_UNKNOWN_VERS, "Unknown HTTP version" },
+ { EVENT_BAD_HEADER, "Format error in HTTP header" },
{ 0, nullptr }
};
HTTP/2.0 200 OK\r\nContent-type: text/plain\r\nTransfer-Encoding: gzip\r\nTransfer-Encoding: identity\r\nTransfer-Encoding: compress\r\nTransfer-Encoding: deflate
\r\nTransfer-Encoding: foo\r\nTransfer-Encoding: chunked\r\n\r\n
+@5008
+@break
+@request
+GET /max/header/request/200/is/ok HTTP/1.1\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+\r\n
# ***********************************************************************************************
# Invalid headers without body
-# @6001
-@break
-@request
-
+@6001
+@break
+@request
+GET /max/header/request/201/is/too/much HTTP/1.1\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\nh1:val\r\nh2:val\r\nh3:val\r\nh4:val\r\nh5:val\r\nh6:val\r\nh7:val\r\nh8:val\r\nh9:val\r\n
+h0:val\r\n\r\n
# ***********************************************************************************************
# Valid Content-Length and body
using namespace NHttpEnums;
void UriNormalizer::normalize(const Field& input, Field& result, bool do_path,
- ScratchPad& scratch_pad,
- NHttpInfractions& infractions)
+ ScratchPad& scratch_pad, NHttpInfractions& infractions)
{
if (result.length != STAT_NOTCOMPUTE)
return;
static bool path_check(const uint8_t* in_buf, int32_t in_length,
NHttpInfractions& infractions);
- static int32_t norm_char_clean(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const
- void* not_used);
- static int32_t norm_backslash(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const
- void* not_used);
- static int32_t norm_path_clean(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&, const
- void* not_used);
+ static int32_t norm_char_clean(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&,
+ const void* not_used);
+ static int32_t norm_backslash(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&,
+ const void* not_used);
+ static int32_t norm_path_clean(const uint8_t*, int32_t, uint8_t*, NHttpInfractions&,
+ const void* not_used);
};
#endif