doc/snort_manual.tgz
doc/snort_manual.xml
doc/version.txt
+extra/m4/*.m4
extra/rule.xxd
extra/snort_examples-1.0.tar.gz
install-sh
nhttp_str_to_code.h
nhttp_api.cc nhttp_api.h
nhttp_tables.cc
- nhttp_uri_tables.cc
nhttp_module.cc
nhttp_module.h
nhttp_test_input.cc
nhttp_str_to_code.cc nhttp_str_to_code.h \
nhttp_api.cc nhttp_api.h \
nhttp_tables.cc \
-nhttp_uri_tables.cc \
nhttp_module.cc nhttp_module.h \
nhttp_test_input.cc nhttp_test_input.h \
nhttp_flow_data.cc nhttp_flow_data.h \
INF_BAD_URI,
INF_ZERO_NINE_REQ,
INF_ZERO_NINE_CONTINUE,
- INF_URI_PERCENT_NORMAL,
- INF_URI_PERCENT_ASCII,
+ INF_NOT_USED_2,
+ INF_URI_PERCENT_UNRESERVED,
INF_URI_PERCENT_UTF8,
INF_URI_PERCENT_UCODE,
INF_URI_PERCENT_OTHER,
};
// Types of character for URI scanning
-enum CharAction { CHAR_NORMAL=2, CHAR_PERCENT, CHAR_PATH, CHAR_INVALID, CHAR_EIGHTBIT };
+enum CharAction { CHAR_NORMAL=2, CHAR_PERCENT, CHAR_PATH, CHAR_EIGHTBIT, CHAR_SUBSTIT };
// Transfer codings
enum Transcoding { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_GZIP, TRANSCODE_DEFLATE,
#include <string.h>
#include <sys/types.h>
+#include "nhttp_uri_norm.h"
#include "nhttp_module.h"
+using namespace NHttpEnums;
+
const Parameter NHttpModule::nhttp_params[] =
{
{ "request_depth", Parameter::PT_INT, "-1:", "-1",
{ "response_depth", Parameter::PT_INT, "-1:", "-1",
"maximum response message body bytes to examine (-1 no limit)" },
{ "unzip", Parameter::PT_BOOL, nullptr, "true", "decompress gzip and deflate message bodies" },
+ { "bad_characters", Parameter::PT_BIT_LIST, "255", nullptr,
+ "alert when any of specified bytes are present in URI after percent decoding" },
+ { "ignore_unreserved", Parameter::PT_STRING, "(optional)", nullptr,
+ "do not alert when the specified unreserved characters are percent-encoded in a URI."
+ "Unreserved characters are 0-9, a-z, A-Z, period, underscore, tilde, and minus." },
+ { "backslash_to_slash", Parameter::PT_BOOL, nullptr, "false",
+ "replace \\ with / when normalizing URIs" },
+ { "plus_to_space", Parameter::PT_BOOL, nullptr, "true",
+ "replace + with <sp> when normalizing URIs" },
+ { "simplify_path", Parameter::PT_BOOL, nullptr, "true",
+ "reduce URI directory path to simplest form" },
#ifdef REG_TEST
{ "test_input", Parameter::PT_BOOL, nullptr, "false", "read HTTP messages from text file" },
{ "test_output", Parameter::PT_BOOL, nullptr, "false", "print out HTTP section data" },
{
params.unzip = val.get_bool();
}
+ else if (val.is("bad_characters"))
+ {
+ val.get_bits(params.uri_param.bad_characters);
+ }
+ else if (val.is("ignore_unreserved"))
+ {
+ const char* ignore = val.get_string();
+ while (*ignore != '\0')
+ {
+ params.uri_param.unreserved_char[*(ignore++)] = false;
+ }
+ }
+ else if (val.is("backslash_to_slash"))
+ {
+ params.uri_param.backslash_to_slash = val.get_bool();
+ params.uri_param.uri_char['\\'] = val.get_bool() ? CHAR_SUBSTIT : CHAR_NORMAL;
+ }
+ else if (val.is("plus_to_space"))
+ {
+ params.uri_param.plus_to_space = val.get_bool();
+ params.uri_param.uri_char['+'] = val.get_bool() ? CHAR_SUBSTIT : CHAR_NORMAL;
+ }
+ else if (val.is("simplify_path"))
+ {
+ params.uri_param.simplify_path = val.get_bool();
+ params.uri_param.uri_char['/'] = val.get_bool() ? CHAR_PATH : CHAR_NORMAL;
+ params.uri_param.uri_char['.'] = val.get_bool() ? CHAR_PATH : CHAR_NORMAL;
+ }
#ifdef REG_TEST
else if (val.is("test_input"))
{
return true;
}
+// Some values in these tables may be changed by configuration parameters.
+NHttpParaList::UriParam::UriParam() :
+ // Characters that should not be percent-encoded
+ // 0-9, a-z, A-Z, tilde, period, underscore, and minus
+ // Initializer string for std::bitset is in reverse order. The first character is element 255
+ // and the last is element 0.
+ unreserved_char { std::string(
+ "00000000" "00000000" "00000000" "00000000"
+ "00000000" "00000000" "00000000" "00000000"
+ "00000000" "00000000" "00000000" "00000000"
+ "00000000" "00000000" "00000000" "00000000"
+ "01000111" "11111111" "11111111" "11111110"
+ "10000111" "11111111" "11111111" "11111110"
+ "00000011" "11111111" "01100000" "00000000"
+ "00000000" "00000000" "00000000" "00000000" ) },
+
+ uri_char {
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_PERCENT, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_SUBSTIT, CHAR_NORMAL, CHAR_NORMAL, CHAR_PATH, CHAR_PATH,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_PATH, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+ CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
+
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
+ CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT
+ }
+{}
+
#ifndef NHTTP_MODULE_H
#define NHTTP_MODULE_H
+#include <string>
+#include <bitset>
+
#include "framework/module.h"
#include "nhttp_enum.h"
long request_depth;
long response_depth;
bool unzip;
+ struct UriParam
+ {
+ public:
+ UriParam();
+
+ bool backslash_to_slash;
+ bool plus_to_space;
+ bool simplify_path;
+ std::bitset<256> bad_characters;
+ std::bitset<256> unreserved_char;
+ NHttpEnums::CharAction uri_char[256];
+ };
+ UriParam uri_param;
#ifdef REG_TEST
bool test_input;
bool test_output;
const Field& NHttpMsgBody::get_classic_client_body()
{
- return classic_normalize(detect_data, classic_client_body, classic_client_body_alloc);
+ return classic_normalize(detect_data, classic_client_body, classic_client_body_alloc,
+ params->uri_param);
}
#ifdef REG_TEST
const Field& NHttpMsgHeadShared::get_classic_norm_header()
{
return classic_normalize(get_classic_raw_header(), classic_norm_header,
- classic_norm_header_alloc);
+ classic_norm_header_alloc, params->uri_param);
}
const Field& NHttpMsgHeadShared::get_classic_raw_cookie()
const Field& NHttpMsgHeadShared::get_classic_norm_cookie()
{
return classic_normalize(get_classic_raw_cookie(), classic_norm_cookie,
- classic_norm_cookie_alloc);
+ classic_norm_cookie_alloc, params->uri_param);
}
const Field& NHttpMsgHeadShared::get_header_value_norm(HeaderId header_id)
if (first_end < last_begin)
{
uri = new NHttpUri(start_line.start + first_end + 1, last_begin - first_end - 1,
- method_id, infractions, events);
+ method_id, params->uri_param, infractions, events);
}
else
{
int32_t uri_end;
for (uri_end = start_line.length - 1; is_sp_tab[start_line.start[uri_end]]; uri_end--);
uri = new NHttpUri(start_line.start + uri_begin, uri_end - uri_begin + 1, method_id,
- infractions, events);
+ params->uri_param, infractions, events);
}
else
{
}
}
-const Field& NHttpMsgSection::classic_normalize(const Field& raw, Field& norm, bool& norm_alloc)
+const Field& NHttpMsgSection::classic_normalize(const Field& raw, Field& norm, bool& norm_alloc,
+ const NHttpParaList::UriParam& uri_param)
{
if (norm.length != STAT_NOT_COMPUTE)
return norm;
- if ((raw.length <= 0) || !UriNormalizer::need_norm_path(raw))
+ if ((raw.length <= 0) || !UriNormalizer::classic_need_norm(raw, true, uri_param))
{
norm.set(raw);
return norm;
}
uint8_t* buffer = new uint8_t[raw.length + UriNormalizer::URI_NORM_EXPANSION];
- UriNormalizer::classic_normalize(raw, norm, buffer);
+ UriNormalizer::classic_normalize(raw, norm, buffer, uri_param);
norm_alloc = true;
return norm;
}
// Convenience methods shared by multiple subclasses
void update_depth() const;
- static const Field& classic_normalize(const Field& raw, Field& norm, bool& norm_alloc);
+ static const Field& classic_normalize(const Field& raw, Field& norm, bool& norm_alloc,
+ const NHttpParaList::UriParam& uri_param);
#ifdef REG_TEST
void print_section_title(FILE* output, const char* title) const;
void print_section_wrapup(FILE* output) const;
void NHttpUri::normalize()
{
- // FIXIT-P generating the normalized URI components directly into the normalized classic buffer
- // would save a lot of memory and some copying.
-
// Divide the URI up into its six components: scheme, host, port, path, query, and fragment
parse_uri();
parse_authority();
// Almost all HTTP requests are honest and rarely need expensive normalization processing. We
// do a quick scan for red flags and only perform normalization if something comes up.
// Otherwise we set the normalized fields to point at the raw values.
- if ((host.length > 0) && UriNormalizer::need_norm_no_path(host))
+ if ((host.length > 0) && UriNormalizer::need_norm(host, false, uri_param, infractions, events))
infractions += INF_URI_NEED_NORM_HOST;
- if ((path.length > 0) && UriNormalizer::need_norm_path(path))
+ if ((path.length > 0) && UriNormalizer::need_norm(path, true, uri_param, infractions, events))
infractions += INF_URI_NEED_NORM_PATH;
- if ((query.length > 0) && UriNormalizer::need_norm_no_path(query))
+ if ((query.length > 0) && UriNormalizer::need_norm(query, false, uri_param, infractions,
+ events))
infractions += INF_URI_NEED_NORM_QUERY;
- if ((fragment.length > 0) && UriNormalizer::need_norm_no_path(fragment))
+ if ((fragment.length > 0) && UriNormalizer::need_norm(fragment, false, uri_param, infractions,
+ events))
infractions += INF_URI_NEED_NORM_FRAGMENT;
if (!((infractions & INF_URI_NEED_NORM_PATH) || (infractions & INF_URI_NEED_NORM_HOST) ||
if (host.length > 0)
{
if (infractions & INF_URI_NEED_NORM_HOST)
- UriNormalizer::normalize(host, host_norm, false, current, infractions, events);
+ UriNormalizer::normalize(host, host_norm, false, current, uri_param, infractions,
+ events);
else
{
// The host component is not changing but other parts of the URI are being normalized.
if (path.length > 0)
{
if (infractions & INF_URI_NEED_NORM_PATH)
- UriNormalizer::normalize(path, path_norm, true, current, infractions, events);
+ UriNormalizer::normalize(path, path_norm, true, current, uri_param, infractions,
+ events);
else
{
memcpy(current, path.start, path.length);
memcpy(current, "?", 1);
current += 1;
if (infractions & INF_URI_NEED_NORM_QUERY)
- UriNormalizer::normalize(query, query_norm, false, current, infractions, events);
+ UriNormalizer::normalize(query, query_norm, false, current, uri_param, infractions,
+ events);
else
{
memcpy(current, query.start, query.length);
memcpy(current, "#", 1);
current += 1;
if (infractions & INF_URI_NEED_NORM_FRAGMENT)
- UriNormalizer::normalize(fragment, fragment_norm, false, current, infractions, events);
+ UriNormalizer::normalize(fragment, fragment_norm, false, current, uri_param,
+ infractions, events);
else
{
memcpy(current, fragment.start, fragment.length);
#define NHTTP_URI_H
#include "nhttp_str_to_code.h"
+#include "nhttp_module.h"
#include "nhttp_uri_norm.h"
#include "nhttp_field.h"
#include "nhttp_infractions.h"
class NHttpUri
{
public:
- NHttpUri(const uint8_t* start, int32_t length, NHttpEnums::MethodId method,
- NHttpInfractions& infractions_, NHttpEventGen& events_) :
- uri(length, start), method_id(method), infractions(infractions_), events(events_)
+ NHttpUri(const uint8_t* start, int32_t length, NHttpEnums::MethodId method_id_,
+ const NHttpParaList::UriParam& uri_param_, NHttpInfractions& infractions_,
+ NHttpEventGen& events_) :
+ uri(length, start), method_id(method_id_), uri_param(uri_param_),
+ infractions(infractions_), events(events_)
{ normalize(); }
~NHttpUri();
const Field& get_uri() const { return uri; }
private:
const Field uri;
const NHttpEnums::MethodId method_id;
+ const NHttpParaList::UriParam& uri_param;
NHttpInfractions& infractions;
NHttpEventGen& events;
#include <assert.h>
#include <sys/types.h>
+#include <cstring>
#include "nhttp_enum.h"
#include "nhttp_uri_norm.h"
using namespace NHttpEnums;
void UriNormalizer::normalize(const Field& input, Field& result, bool do_path, uint8_t* buffer,
- NHttpInfractions& infractions, NHttpEventGen& events)
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions, NHttpEventGen& events)
{
- // Normalize character escape sequences
- int32_t data_length = norm_char_clean(input.start, input.length, buffer, infractions, events);
+ // Normalize percent encodings and similar escape sequences
+ int32_t data_length = norm_char_clean(input, buffer, uri_param, infractions, events);
+
+ detect_bad_char(Field(data_length, buffer), uri_param, infractions, events);
+
+ norm_substitute(buffer, data_length, uri_param, infractions, events);
// Normalize path directory traversals
- if (do_path)
+ if (do_path && uri_param.simplify_path)
{
- norm_backslash(buffer, data_length, infractions, events);
data_length = norm_path_clean(buffer, data_length, infractions, events);
}
result.set(data_length, buffer);
}
-bool UriNormalizer::need_norm_no_path(const Field& uri_component)
+bool UriNormalizer::need_norm(const Field& uri_component, bool do_path,
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions, NHttpEventGen& events)
+{
+ bool need_it;
+ if (do_path && uri_param.simplify_path)
+ need_it = need_norm_path(uri_component, uri_param);
+ else
+ need_it = need_norm_no_path(uri_component, uri_param);
+
+ if (!need_it)
+ {
+ // Since we are not going to normalize we need to check for bad characters now
+ detect_bad_char(uri_component, uri_param, infractions, events);
+ }
+
+ return need_it;
+}
+
+bool UriNormalizer::need_norm_no_path(const Field& uri_component,
+ const NHttpParaList::UriParam& uri_param)
{
const int32_t& length = uri_component.length;
const uint8_t* const & buf = uri_component.start;
for (int32_t k = 0; k < length; k++)
{
- if ((uri_char[buf[k]] == CHAR_NORMAL) || (uri_char[buf[k]] == CHAR_PATH))
- continue;
- return true;
+ if ((uri_param.uri_char[buf[k]] == CHAR_PERCENT) ||
+ (uri_param.uri_char[buf[k]] == CHAR_SUBSTIT))
+ return true;
}
return false;
}
-bool UriNormalizer::need_norm_path(const Field& uri_component)
+bool UriNormalizer::need_norm_path(const Field& uri_component,
+ const NHttpParaList::UriParam& uri_param)
{
const int32_t& length = uri_component.length;
const uint8_t* const & buf = uri_component.start;
for (int32_t k = 0; k < length; k++)
{
- if (uri_char[buf[k]] == CHAR_NORMAL)
- continue;
- if ((buf[k] == '/') && ((k == 0) || (buf[k-1] != '/')))
- continue;
- if ( (buf[k] == '.') &&
- ((k == 0) || (uri_char[buf[k-1]] == CHAR_NORMAL)) &&
- ((k == length-1) || (uri_char[buf[k+1]] == CHAR_NORMAL)))
+ switch (uri_param.uri_char[buf[k]])
+ {
+ case CHAR_NORMAL:
+ case CHAR_EIGHTBIT:
continue;
- return true;
+ case CHAR_PERCENT:
+ case CHAR_SUBSTIT:
+ return true;
+ case CHAR_PATH:
+ if (buf[k] == '/')
+ {
+ // slash is safe if not preceded by another slash
+ if ((k == 0) || (buf[k-1] != '/'))
+ continue;
+ return true;
+ }
+ else if (buf[k] == '.')
+ {
+ // period is safe if not preceded or followed by another path character
+ if (((k == 0) || (uri_param.uri_char[buf[k-1]] != CHAR_PATH)) &&
+ ((k == length-1) || (uri_param.uri_char[buf[k+1]] != CHAR_PATH)))
+ continue;
+ return true;
+ }
+ else
+ {
+ return true;
+ }
+ }
}
return false;
}
-int32_t UriNormalizer::norm_char_clean(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf,
- NHttpInfractions& infractions, NHttpEventGen& events)
+int32_t UriNormalizer::norm_char_clean(const Field& input, uint8_t* out_buf,
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions, NHttpEventGen& events)
{
int32_t length = 0;
- for (int32_t k = 0; k < in_length; k++)
+ for (int32_t k = 0; k < input.length; k++)
{
- switch (uri_char[in_buf[k]])
+ switch (uri_param.uri_char[input.start[k]])
{
case CHAR_NORMAL:
case CHAR_PATH:
- out_buf[length++] = in_buf[k];
- break;
- case CHAR_INVALID:
- infractions += INF_URI_BAD_CHAR;
- events.create_event(EVENT_NON_RFC_CHAR);
- out_buf[length++] = in_buf[k];
- break;
case CHAR_EIGHTBIT:
- infractions += INF_URI_8BIT_CHAR;
- events.create_event(EVENT_BARE_BYTE);
- out_buf[length++] = in_buf[k];
+ case CHAR_SUBSTIT:
+ out_buf[length++] = input.start[k];
break;
case CHAR_PERCENT:
- if ((k+2 < in_length) && (as_hex[in_buf[k+1]] != -1) && (as_hex[in_buf[k+2]] != -1))
+ if ((k+2 < input.length) && (as_hex[input.start[k+1]] != -1) &&
+ (as_hex[input.start[k+2]] != -1))
{
- if (as_hex[in_buf[k+1]] <= 7)
- {
- uint8_t value = as_hex[in_buf[k+1]] * 16 + as_hex[in_buf[k+2]];
- if (good_percent[value])
- {
- // Normal % escape of an ASCII special character that is supposed to be
- // escaped
- infractions += INF_URI_PERCENT_NORMAL;
- out_buf[length++] = '%';
- }
- else
- {
- // Suspicious % escape of an ASCII character that does not need to be
- // escaped
- infractions += INF_URI_PERCENT_ASCII;
- events.create_event(EVENT_ASCII);
- if (uri_char[value] == CHAR_INVALID)
- {
- infractions += INF_URI_BAD_CHAR;
- events.create_event(EVENT_NON_RFC_CHAR);
- }
- out_buf[length++] = value;
- k += 2;
- }
- }
- else
- {
- // UTF-8 decoding not implemented yet
- infractions += INF_URI_PERCENT_UTF8;
- events.create_event(EVENT_UTF_8);
- out_buf[length++] = '%';
- }
+ // %hh => hex value
+ out_buf[length++] = as_hex[input.start[k+1]] * 16 + as_hex[input.start[k+2]];
+ k += 2;
}
- else if ((k+5 < in_length) && (in_buf[k+1] == 'u') && (as_hex[in_buf[k+2]] != -1) &&
- (as_hex[in_buf[k+3]] != -1)
- && (as_hex[in_buf[k+4]] != -1) && (as_hex[in_buf[k+5]] != -1))
+ else if ((k+1 < input.length) && (input.start[k+1] == '%'))
{
- // 'u' UTF-16 decoding not implemented yet
- infractions += INF_URI_PERCENT_UCODE;
- events.create_event(EVENT_U_ENCODE);
+ // %% => %
out_buf[length++] = '%';
+ k += 1;
}
else
{
- // Don't recognize it
- infractions += INF_URI_PERCENT_OTHER;
+ // don't recognize, pass through for now (FIXIT-H unfinished feature)
out_buf[length++] = '%';
}
+
+ // The result of percent decoding should not be an "unreserved" character. That's a
+ // strong clue someone is hiding something.
+ if (uri_param.unreserved_char[out_buf[length-1]])
+ {
+ infractions += INF_URI_PERCENT_UNRESERVED;
+ events.create_event(EVENT_ASCII);
+ }
break;
}
}
return length;
}
-// Convert URI backslashes to slashes
-void UriNormalizer::norm_backslash(uint8_t* buf, int32_t length, NHttpInfractions& infractions,
- NHttpEventGen& events)
+void UriNormalizer::detect_bad_char(const Field& uri_component,
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions, NHttpEventGen& events)
{
- for (int32_t k = 0; k < length; k++)
+ // If the bad character detection feature is not configured we quit
+ if (uri_param.bad_characters.count() == 0)
+ return;
+
+ for (int32_t k = 0; k < uri_component.length; k++)
+ {
+ if (uri_param.bad_characters[uri_component.start[k]])
+ {
+ infractions += INF_URI_BAD_CHAR;
+ events.create_event(EVENT_NON_RFC_CHAR);
+ return;
+ }
+ }
+}
+
+// Replace backslash with slash and plus with space
+void UriNormalizer::norm_substitute(uint8_t* buf, int32_t length,
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions, NHttpEventGen& events)
+{
+ if (uri_param.backslash_to_slash)
+ {
+ for (int32_t k = 0; k < length; k++)
+ {
+ if (buf[k] == '\\')
+ {
+ buf[k] = '/';
+ infractions += INF_URI_BACKSLASH;
+ events.create_event(EVENT_IIS_BACKSLASH);
+ }
+ }
+ }
+ if (uri_param.plus_to_space)
{
- if (buf[k] == '\\')
+ for (int32_t k = 0; k < length; k++)
{
- buf[k] = '/';
- infractions += INF_URI_BACKSLASH;
- events.create_event(EVENT_IIS_BACKSLASH);
+ if (buf[k] == '+')
+ {
+ buf[k] = ' ';
+ }
}
}
}
}
// Provide traditional URI-style normalization for buffers that usually are not URIs
-void UriNormalizer::classic_normalize(const Field& input, Field& result, uint8_t* buffer)
+void UriNormalizer::classic_normalize(const Field& input, Field& result, uint8_t* buffer,
+ const NHttpParaList::UriParam& uri_param)
{
// The requirements for generating events related to these normalizations are unclear. It
// definitely doesn't seem right to generate standard URI events. For now we won't generate
// infraction logic with legacy problems. The following centralizes all the messiness here so
// that we can conveniently modify it as requirements are better understood.
- class NHttpDummyEventGen : public NHttpEventGen
- {
- void create_event(NHttpEnums::EventSid) override {}
- };
-
NHttpInfractions unused;
NHttpDummyEventGen dummy_ev;
// Normalize character escape sequences
- int32_t data_length = norm_char_clean(input.start, input.length, buffer, unused, dummy_ev);
+ int32_t data_length = norm_char_clean(input, buffer, uri_param, unused, dummy_ev);
- // Normalize path directory traversals
- // Find the leading slash if there is one
- int32_t uri_offset;
- for (uri_offset = 0; (uri_offset < data_length) && (buffer[uri_offset] != '/'); uri_offset++);
- if (uri_offset < data_length)
+ if (uri_param.simplify_path)
{
- norm_backslash(buffer + uri_offset, data_length - uri_offset, unused, dummy_ev);
- data_length = uri_offset +
- norm_path_clean(buffer + uri_offset, data_length - uri_offset, unused, dummy_ev);
+ // Normalize path directory traversals
+ // Find the leading slash if there is one
+ uint8_t* first_slash = (uint8_t*)memchr(buffer, '/', data_length);
+ if (first_slash != nullptr)
+ {
+ const int32_t uri_offset = first_slash - buffer;
+ norm_substitute(buffer + uri_offset, data_length - uri_offset, uri_param, unused,
+ dummy_ev);
+ data_length = uri_offset +
+ norm_path_clean(buffer + uri_offset, data_length - uri_offset, unused, dummy_ev);
+ }
}
result.set(data_length, buffer);
}
+bool UriNormalizer::classic_need_norm(const Field& uri_component, bool do_path,
+ const NHttpParaList::UriParam& uri_param)
+{
+ NHttpInfractions unused;
+ NHttpDummyEventGen dummy_ev;
+
+ return need_norm(uri_component, do_path, uri_param, unused, dummy_ev);
+}
+
#define NHTTP_URI_NORM_H
#include "nhttp_field.h"
+#include "nhttp_module.h"
#include "nhttp_infractions.h"
#include "nhttp_event_gen.h"
public:
static const unsigned URI_NORM_EXPANSION = 1;
+ static bool need_norm(const Field& uri_component, bool do_path,
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions,
+ NHttpEventGen& events);
static void normalize(const Field& input, Field& result, bool do_path, uint8_t* buffer,
- NHttpInfractions& infractions, NHttpEventGen& events);
- static bool need_norm_path(const Field& uri_component);
- static bool need_norm_no_path(const Field& uri_component);
- static void classic_normalize(const Field& input, Field& result, uint8_t* buffer);
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions,
+ NHttpEventGen& events);
+ static bool classic_need_norm(const Field& uri_component, bool do_path,
+ const NHttpParaList::UriParam& uri_param);
+ static void classic_normalize(const Field& input, Field& result, uint8_t* buffer,
+ const NHttpParaList::UriParam& uri_param);
private:
- static const NHttpEnums::CharAction uri_char[256];
- static const bool good_percent[256];
-
- static int32_t norm_char_clean(const uint8_t* in_buf, int32_t in_length, uint8_t* out_buf,
- NHttpInfractions& infractions, NHttpEventGen& events);
- static void norm_backslash(uint8_t* buf, int32_t length, NHttpInfractions& infractions,
+ static bool need_norm_path(const Field& uri_component,
+ const NHttpParaList::UriParam& uri_param);
+ static bool need_norm_no_path(const Field& uri_component,
+ const NHttpParaList::UriParam& uri_param);
+ static int32_t norm_char_clean(const Field& input, uint8_t* out_buf,
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions,
+ NHttpEventGen& events);
+ static void norm_substitute(uint8_t* buf, int32_t length,
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions,
NHttpEventGen& events);
static int32_t norm_path_clean(uint8_t* buf, const int32_t in_length,
NHttpInfractions& infractions, NHttpEventGen& events);
+ static void detect_bad_char(const Field& uri_component,
+ const NHttpParaList::UriParam& uri_param, NHttpInfractions& infractions,
+ NHttpEventGen& events);
+
+ // An artifice used by the classic normalization methods to disable event generation
+ class NHttpDummyEventGen : public NHttpEventGen
+ {
+ void create_event(NHttpEnums::EventSid) override {}
+ };
};
#endif
+++ /dev/null
-//--------------------------------------------------------------------------
-// Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License Version 2 as published
-// by the Free Software Foundation. You may not use, modify or distribute
-// this program under any other version of the GNU General Public License.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//--------------------------------------------------------------------------
-// nhttp_uri_tables.cc author Tom Peters <thopeter@cisco.com>
-
-#include <string.h>
-#include <sys/types.h>
-
-#include "nhttp_enum.h"
-#include "nhttp_uri_norm.h"
-
-using namespace NHttpEnums;
-
-const CharAction UriNormalizer::uri_char[256] =
-{
- CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID,
- CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID,
- CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID,
- CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID, CHAR_INVALID,
-
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_PERCENT, CHAR_NORMAL, CHAR_NORMAL,
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_PATH, CHAR_PATH,
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
-
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_PATH, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
-
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL,
- CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_NORMAL, CHAR_INVALID,
-
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
-
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
-
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
-
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT,
- CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT, CHAR_EIGHTBIT
-};
-
-const bool UriNormalizer::good_percent[256] =
-{
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
-
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false,
-
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
-
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
-
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
-
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
-
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
-
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false
-};
-