From: Mike Stepanek (mstepane) Date: Tue, 28 Jul 2020 17:15:33 +0000 (+0000) Subject: Merge pull request #2343 in SNORT/snort3 from ~MDAGON/snort3:translate to master X-Git-Tag: 3.0.2-3~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=488173f10ffa4d95f39d9d1e6c289ba88a46cb0d;p=thirdparty%2Fsnort3.git Merge pull request #2343 in SNORT/snort3 from ~MDAGON/snort3:translate to master Squashed commit of the following: commit 0c98ff023f2575ab3a36c8b09c6fa62de234913f Author: mdagon Date: Fri Jul 10 11:25:18 2020 -0400 payload_injector: add HTTP page translation --- diff --git a/src/payload_injector/CMakeLists.txt b/src/payload_injector/CMakeLists.txt index b3d82db5e..cf9c91772 100644 --- a/src/payload_injector/CMakeLists.txt +++ b/src/payload_injector/CMakeLists.txt @@ -5,6 +5,7 @@ set (PAYLOAD_INJECTOR_INCLUDES add_library ( payload_injector OBJECT ${PAYLOAD_INJECTOR_INCLUDES} payload_injector_module.cc + payload_injector_translate_page.cc ) install(FILES ${PAYLOAD_INJECTOR_INCLUDES} diff --git a/src/payload_injector/dev_notes.txt b/src/payload_injector/dev_notes.txt index 8a16a8c1a..0c99cf1d1 100644 --- a/src/payload_injector/dev_notes.txt +++ b/src/payload_injector/dev_notes.txt @@ -6,4 +6,9 @@ It coordinates with the Active component of Snort to perform these functions. Currently it is being used for HTTP/1 injections. HTTP/2 support is in development. +get_http2_payload supports translation of HTTP block/redirect page to HTTP2. +Current implementation is limited, the constraints are specified in +payload_injector_translate_page.cc. + + diff --git a/src/payload_injector/payload_injector_module.cc b/src/payload_injector/payload_injector_module.cc index 919df8ffc..8a1d33a64 100644 --- a/src/payload_injector/payload_injector_module.cc +++ b/src/payload_injector/payload_injector_module.cc @@ -62,7 +62,7 @@ bool PayloadInjectorModule::end(const char*, int, SnortConfig*) } InjectionReturnStatus PayloadInjectorModule::inject_http_payload(Packet* p, - InjectionControl& control) + const InjectionControl& control) { InjectionReturnStatus status = INJECTION_SUCCESS; diff --git a/src/payload_injector/payload_injector_module.h b/src/payload_injector/payload_injector_module.h index 67717ede3..7bd6eb241 100644 --- a/src/payload_injector/payload_injector_module.h +++ b/src/payload_injector/payload_injector_module.h @@ -43,6 +43,7 @@ enum InjectionReturnStatus : int8_t ERR_STREAM_NOT_ESTABLISHED = -2, ERR_HTTP2_STREAM_ID_0 = -3, ERR_UNIDENTIFIED_PROTOCOL = -4, + ERR_PAGE_TRANSLATION = -4, }; struct InjectionControl @@ -64,7 +65,7 @@ public: bool end(const char*, int, snort::SnortConfig*) override; - static InjectionReturnStatus inject_http_payload(snort::Packet* p, InjectionControl& control); + static InjectionReturnStatus inject_http_payload(snort::Packet* p, const InjectionControl& control); #ifdef UNIT_TEST void set_configured(bool val) { configured = val; } @@ -72,6 +73,11 @@ public: private: static bool configured; + +#ifdef UNIT_TEST +public: +#endif + static InjectionReturnStatus get_http2_payload(InjectionControl control, uint8_t *& http2_payload, uint32_t & payload_len); }; #endif diff --git a/src/payload_injector/payload_injector_translate_page.cc b/src/payload_injector/payload_injector_translate_page.cc new file mode 100644 index 000000000..c0179af26 --- /dev/null +++ b/src/payload_injector/payload_injector_translate_page.cc @@ -0,0 +1,247 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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. +//-------------------------------------------------------------------------- + +// payload_injector_translate_page.cc author Maya Dagon + +// Translates HTTP 1.1 block/redirect page to HTTP2. +// 1. Headers are separated by /r/n +// 2. Headers end with /r/n +// 3. Must have headers and body +// 4. Translated header length <= 2000 +// 5. Supported: HTTP/1.1 403, HTTP/1.1 307, Connection: close, +// Content-Length: , Content-Type: , Set-Cookie: , Location: + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "payload_injector_module.h" + +#include "service_inspectors/http2_inspect/http2_enum.h" +#include "utils/util.h" + +static const char status_403[] = "HTTP/1.1 403"; +static uint8_t status_403_h2[] = { 0, 7, ':', 's', 't', 'a', 't', 'u', 's', 3, '4', '0', '3' }; +static const char status_307[] = "HTTP/1.1 307"; +static uint8_t status_307_h2[] = { 0, 7, ':', 's', 't', 'a', 't', 'u', 's', 3, '3', '0', '7' }; +static const char connection[] = "Connection: close"; +static const uint8_t connection_h2[] = { 0, 10, 'c','o','n','n','e','c','t','i','o','n', + 5, 'c', 'l', 'o', 's', 'e' }; +static const char content_length[] = "Content-Length: "; +static const char content_type[] = "Content-Type: "; +static const char cookie[] = "Set-Cookie: "; +static const char location[] = "Location: "; + +static const uint32_t max_hdr_size = 2000; + +// Write literal header field +static InjectionReturnStatus write_indexed(char* hdr, uint32_t len, uint8_t*& out, + uint32_t& out_free_space, const uint8_t* ind, uint8_t ind_size) +{ + const char* sep = (char*)memchr(hdr,':',len); + assert(sep != nullptr); + const uint32_t skip_len = strlen(": "); + assert((sep - hdr) >= skip_len); + const uint32_t val_len = len - (sep - hdr) - skip_len; + const uint8_t max_val_len = (1<<7) - 1; // FIXIT-E bigger than this will have to be 7 bit + // prefix + // encoded - currently not supported + if ((val_len == 0) || (val_len > max_val_len)) + return ERR_PAGE_TRANSLATION; + + if (out_free_space < (val_len + 1 + ind_size)) + { +#ifndef UNIT_TEST + assert(false); // increase max_hdr_size +#endif + return ERR_PAGE_TRANSLATION; + } + + memcpy(out, ind, ind_size); + out += ind_size; + out[0] = val_len; + memcpy(out + 1, sep + skip_len, val_len); + out += 1 + val_len; + out_free_space -= val_len + 1 + ind_size; + + return INJECTION_SUCCESS; +} + +// Write fixed translation +static InjectionReturnStatus write_translation(uint8_t*& out, uint32_t& out_free_space, + const uint8_t* translation, uint8_t size) +{ + if (out_free_space < size) + { +#ifndef UNIT_TEST + assert(false); // increase max_hdr_size +#endif + return ERR_PAGE_TRANSLATION; + } + + memcpy(out, translation, size); + out += size; + out_free_space -= size; + + return INJECTION_SUCCESS; +} + +static InjectionReturnStatus translate_hdr_field(char* hdr, uint32_t len, uint8_t*& out, + uint32_t& out_free_space) +{ + if (len > strlen(status_403) && memcmp(hdr, status_403, strlen(status_403)) == 0) + { + return write_translation(out, out_free_space,status_403_h2, sizeof(status_403_h2)); + } + else if (len > strlen(status_307) && memcmp(hdr, status_307, strlen(status_307)) == 0) + { + return write_translation(out, out_free_space,status_307_h2, sizeof(status_307_h2)); + } + else if (len == strlen(connection) && memcmp(hdr, connection, strlen(connection))==0) + { + return write_translation(out, out_free_space, connection_h2, sizeof(connection_h2)); + } + // The following use literal header field without indexing. + // The header field name index to the static table is represented using 4-bit prefix. + else if (len > strlen(content_length) && memcmp(hdr, content_length, strlen(content_length))== + 0) + { + const uint8_t ind_rep[] = { 0xf, 0xd }; // 0000 + 28 in 4 bit prefix + return write_indexed(hdr, len, out, out_free_space, ind_rep, sizeof(ind_rep)); + } + else if (len > strlen(content_type) && memcmp(hdr, content_type, strlen(content_type))==0) + { + const uint8_t ind_rep[] = { 0xf, 0x10 }; // 0000 + 31 in 4 bit prefix + return write_indexed(hdr, len, out, out_free_space, ind_rep, sizeof(ind_rep)); + } + else if (len > strlen(cookie) && memcmp(hdr, cookie, strlen(cookie))==0) + { + const uint8_t ind_rep[] = { 0xf, 0x28 }; // 0000 + 55 in 4 bit prefix + return write_indexed(hdr, len, out, out_free_space, ind_rep, sizeof(ind_rep)); + } + else if (len > strlen(location) && memcmp(hdr, location, strlen(location))==0) + { + const uint8_t ind_rep[] = { 0xf, 0x1f }; // 0000 + 46 in 4 bit prefix + return write_indexed(hdr, len, out, out_free_space, ind_rep, sizeof(ind_rep)); + } + else + return ERR_PAGE_TRANSLATION; +} + +static InjectionReturnStatus get_http2_hdr(char* http_page, uint32_t len, + uint8_t* http2_hdr, uint32_t& hdr_len, uint32_t& body_offset) +{ + InjectionReturnStatus status = ERR_PAGE_TRANSLATION; + body_offset = 0; + + uint32_t hdr_free_space = max_hdr_size; + char* page_cur = http_page; + uint8_t* hdr_cur = http2_hdr; + while ((page_cur - http_page) < len) + { + char* cr_newline = strstr(page_cur, "\r\n"); + if (cr_newline != nullptr) + { + if (cr_newline == page_cur) + { + // reached end of headers + if ((page_cur - http_page + 2) < len) + body_offset = page_cur - http_page + 2; + break; + } + status = translate_hdr_field(page_cur, cr_newline-page_cur, hdr_cur, hdr_free_space); + if (status != INJECTION_SUCCESS) + break; + page_cur = cr_newline + 2; + } + else + break; + } + + if (status == ERR_PAGE_TRANSLATION || body_offset == 0) + return ERR_PAGE_TRANSLATION; + + hdr_len = hdr_cur - http2_hdr; + + return INJECTION_SUCCESS; +} + +static void write_3_bytes_of_int(uint8_t* out, uint32_t val) +{ +#ifdef WORDS_BIGENDIAN + out[2] = (val & (0xff000000)) >> 24; + out[1] = (val & (0xff0000)) >> 16; + out[0] = (val & (0xff00)) >> 8; +#else + out[2] = val & 0xff; + out[1] = (val & (0xff00)) >> 8; + out[0] = (val & (0xff0000)) >> 16; +#endif +} + +static void write_frame_hdr(uint8_t*& out, uint32_t len, uint8_t type, uint8_t flags, uint32_t + stream_id) +{ + write_3_bytes_of_int(out, len); + out[3] = type; + out[4] = flags; + stream_id = htonl(stream_id); + memcpy(out+5, &stream_id, 4); + out += Http2Enums::FRAME_HEADER_LENGTH; +} + +InjectionReturnStatus PayloadInjectorModule::get_http2_payload(InjectionControl control, + uint8_t*& http2_payload, uint32_t& payload_len) +{ + if (control.http_page == nullptr || control.http_page_len == 0) + return ERR_PAGE_TRANSLATION; + + // create a string version to run with strstr + char* page_string = (char*)snort_alloc(control.http_page_len + 1); + memcpy(page_string, control.http_page, control.http_page_len); + page_string[control.http_page_len] = '\0'; + + uint8_t http2_hdr[max_hdr_size]; + uint32_t hdr_len, body_offset; + InjectionReturnStatus status = get_http2_hdr(page_string, control.http_page_len, http2_hdr, + hdr_len, body_offset); + + snort_free(page_string); + + if (status == ERR_PAGE_TRANSLATION) + return status; + + const uint32_t body_len = control.http_page_len - body_offset; + // FIXIT-E support larger body size + if (body_len > 1<<14) + return ERR_PAGE_TRANSLATION; + + payload_len = 2*Http2Enums::FRAME_HEADER_LENGTH + hdr_len + body_len; + http2_payload = (uint8_t*)snort_alloc(payload_len); + + uint8_t* http2_payload_cur = http2_payload; + // FIXIT-E update flags + write_frame_hdr(http2_payload_cur, hdr_len, Http2Enums::FT_HEADERS, 0, control.stream_id); + memcpy(http2_payload_cur, http2_hdr, hdr_len); + http2_payload_cur += hdr_len; + write_frame_hdr(http2_payload_cur, body_len, Http2Enums::FT_DATA, 0, control.stream_id); + memcpy(http2_payload_cur, control.http_page + body_offset, body_len); + + return INJECTION_SUCCESS; +} + diff --git a/src/payload_injector/test/CMakeLists.txt b/src/payload_injector/test/CMakeLists.txt index 693bcc56f..531233974 100644 --- a/src/payload_injector/test/CMakeLists.txt +++ b/src/payload_injector/test/CMakeLists.txt @@ -3,3 +3,8 @@ add_cpputest (payload_injector_test ../payload_injector_module.cc ../../framework/module.cc ) + +add_cpputest (payload_injector_translate_test + SOURCES + ../payload_injector_translate_page.cc +) diff --git a/src/payload_injector/test/payload_injector_translate_test.cc b/src/payload_injector/test/payload_injector_translate_test.cc new file mode 100644 index 000000000..f60ec1f45 --- /dev/null +++ b/src/payload_injector/test/payload_injector_translate_test.cc @@ -0,0 +1,392 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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. +//-------------------------------------------------------------------------- + +// payload_injector_translate_test.cc author Maya Dagon + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "payload_injector/payload_injector_module.h" + +#include "utils/util.h" + +#include +#include + +TEST_GROUP(payload_injector_translate_test) +{ + uint8_t* http2_payload; + uint32_t payload_len; + InjectionReturnStatus status; +}; + +TEST(payload_injector_translate_test, basic_hdr_translation) +{ + char http_page[] = "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: " + "504\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n\n\n\n"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + CHECK(status == INJECTION_SUCCESS); + + uint8_t out[] = { 0x0, 0x0, 0x40, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x7, 0x3a, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x3, 0x34, 0x30, 0x33, 0x0, 0xa, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0xf, 0xd, + 0x3, 0x35, + 0x30, 0x34, 0xf, 0x10, 0x18, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, + 0x6c, + 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x55, 0x54, 0x46, + 0x2d, + 0x38, 0x0, 0x0, 0x62, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3c, 0x21, 0x44, 0x4f, + 0x43, 0x54, + 0x59, 0x50, 0x45, 0x20, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0x3c, 0x68, 0x74, + 0x6d, + 0x6c, 0x3e, 0x3c, 0x68, 0x65, 0x61, 0x64, 0x3e, 0xa, 0x3c, 0x6d, 0x65, 0x74, + 0x61, + 0x68, 0x74, 0x74, 0x70, 0x2d, 0x65, 0x71, 0x75, 0x69, 0x76, 0x3d, 0x22, 0x63, + 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x22, 0x20, 0x63, + 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, + 0x74, + 0x6d, 0x6c, 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x55, + 0x54, + 0x46, 0x2d, 0x38, 0x22, 0x20, 0x2f, 0x3e, 0xa }; + CHECK(payload_len == sizeof(out)); + CHECK(memcmp(http2_payload, out, payload_len) == 0); + + snort_free(http2_payload); +} + +TEST(payload_injector_translate_test, basic_hdr_translation2) +{ + char http_page[] = + "HTTP/1.1 307 Proxy Redirect\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == INJECTION_SUCCESS); + + uint8_t out[] = { 0x0, 0x0, 0x36, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x7, 0x3a, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x3, 0x33, 0x30, 0x37, 0xf, 0x1f, 0x8, 0x68, 0x74, + 0x74, 0x70, + 0x73, 0x3a, 0x2f, 0x2f, 0xf, 0x28, 0x1b, 0x30, 0x34, 0x66, 0x32, 0x3b, 0x20, + 0x4d, + 0x61, 0x78, 0x2d, 0x41, 0x67, 0x65, 0x3a, 0x20, 0x36, 0x30, 0x30, 0x3b, 0x20, + 0x70, + 0x61, 0x74, 0x68, 0x3d, 0x2f, 0x3b, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x42, + 0x6f, 0x64, 0x79, 0xa }; + CHECK(payload_len == sizeof(out)); + CHECK(memcmp(http2_payload, out, payload_len) == 0); + + snort_free(http2_payload); +} + +TEST(payload_injector_translate_test, only_body) +{ + char http_page[] = "\r\nbody"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, no_body) +{ + char http_page[] = + "HTTP/1.1 307 Proxy Redirect\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\n"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, missing_last_rn) +{ + char http_page[] = + "HTTP/1.1 307 Proxy Redirect\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\nBody\n"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, missing_space_after_colon) +{ + char http_page[] = + "HTTP/1.1 307 Proxy Redirect\r\nLocation:https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, extra_space_before_colon) +{ + char http_page[] = + "HTTP/1.1 307 Proxy Redirect\r\nLocation :https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, unsuporrted_status) +{ + char http_page[] = + "HTTP/1.1 200 OK\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, unsupported_hdr) +{ + char http_page[] = + "HTTP/1.1 307 Proxy Redirect\r\nLocation:https://\r\nRetry-after: 120\r\n\r\nBody\n"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, hdr_ends_wo_value) +{ + char http_page[] = "HTTP/1.1 307 Proxy Redirect\r\nLocation: "; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, missing_value) +{ + char http_page[] = "HTTP/1.1 307 Proxy Redirect\r\nLocation: \r\n\r\nBody\n"; + + InjectionControl control; + control.stream_id = 1; + control.http_page = (uint8_t*)http_page; + control.http_page_len = strlen(http_page); + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +// Header value has maximum supported length + 1 +TEST(payload_injector_translate_test, val_len_too_big) +{ + const uint32_t size = strlen("Location: ") + 128 + strlen("\r\n\r\nbody"); + uint8_t http_page[size]; + memset(http_page, 'a', size); + memcpy(http_page, "Location: ", strlen("Location: ")); + memcpy(http_page+128+strlen("Location: "), "\r\n\r\nbody", strlen("\r\n\r\nbody")); + + InjectionControl control; + control.stream_id = 1; + control.http_page = http_page; + control.http_page_len = size; + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +// Header value has maximum supported length 127 +TEST(payload_injector_translate_test, max_val) +{ + const uint32_t size = strlen("Location: ") + 127 + strlen("\r\n\r\nbody"); + uint8_t http_page[size]; + memset(http_page, 'a', size); + memcpy(http_page, "Location: ", strlen("Location: ")); + memcpy(http_page+127+strlen("Location: "), "\r\n\r\nbody", strlen("\r\n\r\nbody")); + + InjectionControl control; + control.stream_id = 1; + control.http_page = http_page; + control.http_page_len = size; + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == INJECTION_SUCCESS); + snort_free(http2_payload); +} + +// Translated header is exactly 2000. +// Verify correct frame length when length is more than 1 byte. +// Verify correct behavior for body length is 1. +TEST(payload_injector_translate_test, http2_hdr_is_max) +{ + const uint32_t size = strlen("Connection: close\r\n") * 110 + strlen("Location: ") + + strlen("\r\n\r\nb") + 17; + uint8_t http_page[size]; + + memset(http_page, 'a', size); + uint8_t* cur_pos = http_page; + for (int i=0; i < 110; i++) + { + memcpy(cur_pos, "Connection: close\r\n", strlen("Connection: close\r\n")); + cur_pos += strlen("Connection: close\r\n"); + } + memcpy(cur_pos, "Location: ", strlen("Location: ")); + memcpy(http_page+size-strlen("\r\n\r\nb"), "\r\n\r\nb", strlen("\r\n\r\nb")); + + InjectionControl control; + control.http_page = http_page; + control.http_page_len = size; + control.stream_id = 0xf000; + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == INJECTION_SUCCESS); + CHECK(payload_len == 2019); + uint8_t hdr[] = { 0x0, 0x7, 0xd0, 0x1, 0x0, 0x0, 0x0, 0xf0, 0x0 }; + CHECK(memcmp(http2_payload, hdr, sizeof(hdr))==0); + uint8_t body[] = { 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 'b' }; + CHECK(memcmp(http2_payload + 2009, body, sizeof(body))==0); + snort_free(http2_payload); +} + +// Translated header is 2001. Goes through write_indexed code path. +TEST(payload_injector_translate_test, http2_hdr_too_big) +{ + const uint32_t size = strlen("Connection: close\r\n") * 110 + strlen("Location: ") + + strlen("\r\n\r\nbody") + 18; + uint8_t http_page[size]; + + memset(http_page, 'a', size); + uint8_t* cur_pos = http_page; + for (int i=0; i < 110; i++) + { + memcpy(cur_pos, "Connection: close\r\n", strlen("Connection: close\r\n")); + cur_pos += strlen("Connection: close\r\n"); + } + memcpy(cur_pos, "Location: ", strlen("Location: ")); + memcpy(http_page+size-strlen("\r\n\r\nbody"), "\r\n\r\nbody", strlen("\r\n\r\nbody")); + + InjectionControl control; + control.stream_id = 1; + control.http_page = http_page; + control.http_page_len = size; + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + + CHECK(status == ERR_PAGE_TRANSLATION); +} + +// Translated header > 2000. Goes through write_translation code path. +TEST(payload_injector_translate_test, http2_hdr_too_big2) +{ + const uint32_t size = strlen("Connection: close\r\n") * 112 + strlen("\r\nbody"); + uint8_t http_page[size]; + + uint8_t* cur_pos = http_page; + for (int i=0; i < 112; i++) + { + memcpy(cur_pos, "Connection: close\r\n", strlen("Connection: close\r\n")); + cur_pos += strlen("Connection: close\r\n"); + } + memcpy(http_page+size-strlen("\r\nbody"), "\r\nbody", strlen("\r\nbody")); + + InjectionControl control; + control.stream_id = 1; + control.http_page = http_page; + control.http_page_len = size; + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, payload_body_larger_than_max) +{ + static const uint32_t size = (1<<14) + 1 + strlen("HTTP/1.1 403 Forbidden\r\n\r\n"); + uint8_t http_page[size]; + memset(http_page,'a',size); + memcpy(http_page,"HTTP/1.1 403 Forbidden\r\n\r\n", strlen("HTTP/1.1 403 Forbidden\r\n\r\n")); + + InjectionControl control; + control.stream_id = 1; + control.http_page = http_page; + control.http_page_len = size; + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, http_page_is_nullptr) +{ + InjectionControl control; + control.http_page = nullptr; + control.http_page_len = 1; + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + CHECK(status == ERR_PAGE_TRANSLATION); +} + +TEST(payload_injector_translate_test, http_page_is_0_length) +{ + uint8_t http_page[] = {1}; + + InjectionControl control; + control.http_page = http_page; + control.http_page_len = 0; + status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len); + CHECK(status == ERR_PAGE_TRANSLATION); +} + +int main(int argc, char** argv) +{ + return CommandLineTestRunner::RunAllTests(argc, argv); +} +