assert(count > 0);
- // Search Header IDs from all the headers in this message. concatenate_repeats means the header
- // can properly be present more than once. The standard normalization is to concatenate all the
- // repeated field values into a comma-separated list. Otherwise only the first value will be
- // normalized and the rest will be ignored.
+ // Search Header IDs from all the headers in this message. All repeated field values are
+ // concatenated into a comma-separated list.
+ // FIXIT-L Set-Cookie is a special case in the RFC because multiple Set-Cookie headers are
+ // widely used but comma-concatenation of cookies is incorrect. That would be a concern for us
+ // if we actually used the cookies. But since we just want a single value to show to the
+ // pattern matcher, concatenating is probably fine. In the future we may wish to revisit this
+ // issue. Specifically, semicolon-concatenation may be better.
int num_matches = 0;
int32_t buffer_length = 0;
if (++num_matches == 1)
curr_match = k; // remembering location of the first matching header
buffer_length += header_value[k].length();
- if (!concatenate_repeats || (num_matches >= count))
+ if (num_matches >= count)
break;
}
}
- assert((!concatenate_repeats && (num_matches == 1)) ||
- (concatenate_repeats && (num_matches == count)));
+ assert(num_matches == count);
buffer_length += num_matches - 1; // allow space for concatenation commas
// We are allocating two buffers to store the normalized field value. The raw field value will
uint8_t* const temp_space = new uint8_t[buffer_length];
memset(norm_value, 0, buffer_length);
memset(temp_space, 0, buffer_length);
- uint8_t* working = (num_normalizers%2 == 0) ? norm_value : temp_space;
+ uint8_t* const norm_start = (num_normalizers%2 == 0) ? norm_value : temp_space;
+ uint8_t* working = norm_start;
int32_t data_length = 0;
for (int j=0; j < num_matches; j++)
{
data_length += growth;
}
+ // Many fields names can appear more than once but some should not. If an event or infraction
+ // is defined we will check as part of normalization. A comma-separated header value is
+ // equivalent to a repeated header name. This is JIT code and we will not check for repeated
+ // headers unless someone asks for that header.
+ if ((repeat_event != EVENT__NONE) || (repeat_inf != INF__NONE))
+ {
+ if (count >= 2)
+ {
+ *infractions += repeat_inf;
+ events->create_event(repeat_event);
+ }
+ else
+ {
+ for (int k=0; k < data_length; k++)
+ {
+ if (norm_start[k] == ',')
+ {
+ *infractions += repeat_inf;
+ events->create_event(repeat_event);
+ break;
+ }
+ }
+ }
+ }
+
for (int i=0; i < num_normalizers; i++)
{
if (i%2 != num_normalizers%2)
{ HEAD_TRUE_CLIENT_IP, "true-client-ip" },
{ HEAD_X_WORKING_WITH, "x-working-with" },
{ HEAD_CONTENT_TRANSFER_ENCODING, "content-transfer-encoding" },
+ { HEAD_MIME_VERSION, "mime-version" },
{ 0, nullptr }
};
};
const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_BASIC
- { false, nullptr, nullptr, nullptr };
+ { EVENT__NONE, INF__NONE, nullptr, nullptr, nullptr };
+
+const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_NO_REPEAT
+ { EVENT_REPEATED_HEADER, INF_REPEATED_HEADER, nullptr, nullptr, nullptr };
+
+const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_CASE_INSENSITIVE
+ { EVENT__NONE, INF__NONE, norm_to_lower, nullptr, nullptr };
const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_NUMBER
- { false, norm_remove_lws, nullptr, nullptr };
+ { EVENT_REPEATED_HEADER, INF_REPEATED_HEADER, norm_remove_lws, nullptr, nullptr };
const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_TOKEN_LIST
- { true, norm_remove_lws, norm_to_lower, nullptr };
+ { EVENT__NONE, INF__NONE, norm_remove_lws, norm_to_lower, nullptr };
-const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_CHARSET
- { true, norm_remove_quotes_lws, norm_to_lower, nullptr };
+const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_METHOD_LIST
+ { EVENT__NONE, INF__NONE, norm_remove_lws, nullptr, nullptr };
-const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_CAT
- { true, norm_remove_lws, nullptr, nullptr };
+// FIXIT-L implement a date normalization function that converts the three legal formats into a
+// single standard format. For now we do nothing special for dates. This object is a placeholder
+// to keep track of which headers have date values.
+const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_DATE
+ { EVENT__NONE, INF__NONE, nullptr, nullptr, nullptr };
-const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_COOKIE
- { true, nullptr, nullptr, nullptr };
+// FIXIT-M implement a URI normalization function, probably by extending existing URI capabilities
+// to cover relative formats
+const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_URI
+ { EVENT__NONE, INF__NONE, nullptr, nullptr, nullptr };
+
+const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_CONTENT_LENGTH
+ { EVENT_MULTIPLE_CONTLEN, INF_MULTIPLE_CONTLEN, norm_remove_lws, nullptr, nullptr };
+
+const HeaderNormalizer HttpMsgHeadShared::NORMALIZER_CHARSET
+ { EVENT__NONE, INF__NONE, norm_remove_quotes_lws, norm_to_lower, nullptr };
#if defined(__clang__)
// Designated initializers are not supported in C++11. However we're going to play compilation
const HeaderNormalizer* const HttpMsgHeadShared::header_norms[HEAD__MAX_VALUE] = {
[0] = &NORMALIZER_BASIC,
[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_COOKIE,
- [HEAD_SET_COOKIE] = &NORMALIZER_COOKIE,
+ [HEAD_CACHE_CONTROL] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_CONNECTION] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_DATE] = &NORMALIZER_DATE,
+ [HEAD_PRAGMA] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_TRAILER] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_COOKIE] = &NORMALIZER_BASIC,
+ [HEAD_SET_COOKIE] = &NORMALIZER_BASIC,
[HEAD_TRANSFER_ENCODING] = &NORMALIZER_TOKEN_LIST,
[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_CAT,
- [HEAD_ACCEPT_LANGUAGE] = &NORMALIZER_CAT,
+ [HEAD_ACCEPT] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_ACCEPT_CHARSET] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_ACCEPT_ENCODING] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_ACCEPT_LANGUAGE] = &NORMALIZER_TOKEN_LIST,
[HEAD_AUTHORIZATION] = &NORMALIZER_BASIC,
- [HEAD_EXPECT] = &NORMALIZER_BASIC,
+ [HEAD_EXPECT] = &NORMALIZER_CASE_INSENSITIVE,
[HEAD_FROM] = &NORMALIZER_BASIC,
- [HEAD_HOST] = &NORMALIZER_BASIC,
+ [HEAD_HOST] = &NORMALIZER_NO_REPEAT,
[HEAD_IF_MATCH] = &NORMALIZER_BASIC,
- [HEAD_IF_MODIFIED_SINCE] = &NORMALIZER_BASIC,
+ [HEAD_IF_MODIFIED_SINCE] = &NORMALIZER_DATE,
[HEAD_IF_NONE_MATCH] = &NORMALIZER_BASIC,
[HEAD_IF_RANGE] = &NORMALIZER_BASIC,
- [HEAD_IF_UNMODIFIED_SINCE] = &NORMALIZER_BASIC,
+ [HEAD_IF_UNMODIFIED_SINCE] = &NORMALIZER_DATE,
[HEAD_MAX_FORWARDS] = &NORMALIZER_BASIC,
[HEAD_PROXY_AUTHORIZATION] = &NORMALIZER_BASIC,
[HEAD_RANGE] = &NORMALIZER_BASIC,
- [HEAD_REFERER] = &NORMALIZER_BASIC,
- [HEAD_TE] = &NORMALIZER_BASIC,
+ [HEAD_REFERER] = &NORMALIZER_URI,
+ [HEAD_TE] = &NORMALIZER_TOKEN_LIST,
[HEAD_USER_AGENT] = &NORMALIZER_BASIC,
- [HEAD_ACCEPT_RANGES] = &NORMALIZER_BASIC,
- [HEAD_AGE] = &NORMALIZER_BASIC,
+ [HEAD_ACCEPT_RANGES] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_AGE] = &NORMALIZER_NUMBER,
[HEAD_ETAG] = &NORMALIZER_BASIC,
- [HEAD_LOCATION] = &NORMALIZER_BASIC,
+ [HEAD_LOCATION] = &NORMALIZER_URI,
[HEAD_PROXY_AUTHENTICATE] = &NORMALIZER_BASIC,
- [HEAD_RETRY_AFTER] = &NORMALIZER_BASIC,
+ [HEAD_RETRY_AFTER] = &NORMALIZER_BASIC, // may be date or number
[HEAD_SERVER] = &NORMALIZER_BASIC,
- [HEAD_VARY] = &NORMALIZER_BASIC,
+ [HEAD_VARY] = &NORMALIZER_TOKEN_LIST,
[HEAD_WWW_AUTHENTICATE] = &NORMALIZER_BASIC,
- [HEAD_ALLOW] = &NORMALIZER_BASIC,
+ [HEAD_ALLOW] = &NORMALIZER_METHOD_LIST,
[HEAD_CONTENT_ENCODING] = &NORMALIZER_TOKEN_LIST,
- [HEAD_CONTENT_LANGUAGE] = &NORMALIZER_BASIC,
- [HEAD_CONTENT_LENGTH] = &NORMALIZER_NUMBER,
- [HEAD_CONTENT_LOCATION] = &NORMALIZER_BASIC,
+ [HEAD_CONTENT_LANGUAGE] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_CONTENT_LENGTH] = &NORMALIZER_CONTENT_LENGTH,
+ [HEAD_CONTENT_LOCATION] = &NORMALIZER_URI,
[HEAD_CONTENT_MD5] = &NORMALIZER_BASIC,
[HEAD_CONTENT_RANGE] = &NORMALIZER_BASIC,
[HEAD_CONTENT_TYPE] = &NORMALIZER_CHARSET,
- [HEAD_EXPIRES] = &NORMALIZER_BASIC,
- [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC,
- [HEAD_X_FORWARDED_FOR] = &NORMALIZER_CAT,
+ [HEAD_EXPIRES] = &NORMALIZER_DATE,
+ [HEAD_LAST_MODIFIED] = &NORMALIZER_DATE,
+ [HEAD_X_FORWARDED_FOR] = &NORMALIZER_BASIC,
[HEAD_TRUE_CLIENT_IP] = &NORMALIZER_BASIC,
[HEAD_X_WORKING_WITH] = &NORMALIZER_BASIC,
- [HEAD_CONTENT_TRANSFER_ENCODING] = &NORMALIZER_CAT,
+ [HEAD_CONTENT_TRANSFER_ENCODING] = &NORMALIZER_TOKEN_LIST,
+ [HEAD_MIME_VERSION] = &NORMALIZER_BASIC,
};
/* *INDENT-ON* */
{ EVENT_CHUNKED_ONE_POINT_ZERO, "HTTP 1.0 message with Transfer-Encoding header" },
{ EVENT_CTE_HEADER, "Content-Transfer-Encoding used as HTTP header" },
{ EVENT_ILLEGAL_TRAILER, "illegal field in chunked message trailers" },
+ { EVENT_REPEATED_HEADER, "header field inappropriately appears twice or has two "
+ "values" },
{ 0, nullptr }
};
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2017 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.
+//--------------------------------------------------------------------------
+
+// http_normalizers_test.cc author Tom Peters <thopeter@cisco.com>
+// unit test main
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "service_inspectors/http_inspect/http_field.h"
+#include "service_inspectors/http_inspect/http_normalizers.h"
+#include "service_inspectors/http_inspect/http_test_manager.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+using namespace HttpEnums;
+
+// Stubs whose sole purpose is to make the test code link
+const bool HttpEnums::is_sp_tab[256] {};
+const bool HttpEnums::is_sp_tab_quote_dquote[256] {};
+long HttpTestManager::print_amount {};
+bool HttpTestManager::print_hex {};
+
+TEST_GROUP(norm_decimal_integer_test) {};
+
+TEST(norm_decimal_integer_test, examples)
+{
+ CHECK(norm_decimal_integer(Field(1, (const uint8_t*)"0")) == 0);
+ CHECK(norm_decimal_integer(Field(2, (const uint8_t*)"27")) == 27);
+ CHECK(norm_decimal_integer(Field(5, (const uint8_t*)"00027")) == 27);
+ CHECK(norm_decimal_integer(Field(2, (const uint8_t*)"-27")) == STAT_PROBLEMATIC);
+ CHECK(norm_decimal_integer(Field(6, (const uint8_t*)"27,382")) == 27);
+ CHECK(norm_decimal_integer(Field(3, (const uint8_t*)",27")) == STAT_PROBLEMATIC);
+ CHECK(norm_decimal_integer(Field(3, (const uint8_t*)",27")) == STAT_PROBLEMATIC);
+ CHECK(norm_decimal_integer(Field(6, (const uint8_t*)"00000=")) == STAT_PROBLEMATIC);
+ CHECK(norm_decimal_integer(Field(6, (const uint8_t*)"32.578")) == STAT_PROBLEMATIC);
+ CHECK(norm_decimal_integer(Field(18, (const uint8_t*)"123456789012345678")) ==
+ 123456789012345678);
+ CHECK(norm_decimal_integer(Field(19, (const uint8_t*)"1234567890123456789")) ==
+ STAT_PROBLEMATIC);
+ CHECK(norm_decimal_integer(Field(19, (const uint8_t*)"0123456789012345678")) ==
+ 123456789012345678);
+ CHECK(norm_decimal_integer(Field(20, (const uint8_t*)"01234567890123456789")) ==
+ STAT_PROBLEMATIC);
+ CHECK(norm_decimal_integer(Field(25, (const uint8_t*)"0000000000000000000000027")) == 27);
+ CHECK(norm_decimal_integer(Field(8, (const uint8_t*)"0040E,27")) == STAT_PROBLEMATIC);
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+