From: Mike Stepanek (mstepane) Date: Fri, 12 Jul 2019 17:14:05 +0000 (-0400) Subject: Merge pull request #1667 in SNORT/snort3 from ~MDAGON/snort3:hpack_string to master X-Git-Tag: 3.0.0-258~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ab6d442df08bca73bd97e21f2e03ef553d6e7e49;p=thirdparty%2Fsnort3.git Merge pull request #1667 in SNORT/snort3 from ~MDAGON/snort3:hpack_string to master Squashed commit of the following: commit 74d40186fe6b3dd1207eb70e621e966de29051df Author: mdagon Date: Wed Jul 3 12:04:12 2019 -0400 http2: hpack string decode --- diff --git a/src/service_inspectors/http2_inspect/CMakeLists.txt b/src/service_inspectors/http2_inspect/CMakeLists.txt index 90b40758f..b9406e634 100644 --- a/src/service_inspectors/http2_inspect/CMakeLists.txt +++ b/src/service_inspectors/http2_inspect/CMakeLists.txt @@ -5,8 +5,10 @@ set (FILE_LIST http2_enum.h http2_flow_data.cc http2_flow_data.h - http2_hpack_decode.cc - http2_hpack_decode.h + http2_hpack_int_decode.cc + http2_hpack_int_decode.h + http2_hpack_string_decode.cc + http2_hpack_string_decode.h http2_inspect.cc http2_inspect_impl.cc http2_inspect.h diff --git a/src/service_inspectors/http2_inspect/http2_enum.h b/src/service_inspectors/http2_inspect/http2_enum.h index a1fc4ca1d..63a255732 100644 --- a/src/service_inspectors/http2_inspect/http2_enum.h +++ b/src/service_inspectors/http2_inspect/http2_enum.h @@ -51,6 +51,7 @@ enum EventSid EVENT__NONE = -1, EVENT_INT_DECODE_FAILURE = 1, EVENT_INT_LEADING_ZEROS = 2, + EVENT_STRING_DECODE_FAILURE = 3, EVENT__MAX_VALUE }; @@ -62,6 +63,9 @@ enum Infraction INF_INT_MISSING_BYTES = 1, INF_INT_OVERFLOW = 2, INF_INT_LEADING_ZEROS = 3, + INF_STRING_EMPTY_BUFF = 4, + INF_STRING_MISSING_BYTES = 5, + INF_OUT_BUFF_OUT_OF_SPACE = 6, INF__MAX_VALUE }; diff --git a/src/service_inspectors/http2_inspect/http2_hpack_decode.cc b/src/service_inspectors/http2_inspect/http2_hpack_int_decode.cc similarity index 86% rename from src/service_inspectors/http2_inspect/http2_hpack_decode.cc rename to src/service_inspectors/http2_inspect/http2_hpack_int_decode.cc index e723012bd..e2ab8c434 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack_decode.cc +++ b/src/service_inspectors/http2_inspect/http2_hpack_int_decode.cc @@ -15,13 +15,13 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// http2_hpack_decode.cc author Maya Dagon +// http2_hpack_int_decode.cc author Maya Dagon #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include "http2_hpack_decode.h" +#include "http2_hpack_int_decode.h" #include "http2_enum.h" @@ -37,20 +37,20 @@ Http2HpackIntDecode::Http2HpackIntDecode(uint8_t prefix, Http2EventGen* events, assert ((0 < prefix) && (prefix < 9)); } -bool Http2HpackIntDecode::translate(const Field& msg, int32_t& bytes_consumed, uint64_t& result) +bool Http2HpackIntDecode::translate(const uint8_t* in_buff, const uint32_t in_len, + uint32_t& bytes_consumed, uint64_t& result) { bytes_consumed = 0; result = 0; - if (bytes_consumed >= msg.length()) + if (in_len == 0) { *infractions += INF_INT_EMPTY_BUFF; events->create_event(EVENT_INT_DECODE_FAILURE); return false; } - const uint8_t* buff = msg.start(); - const uint8_t prefix_val = buff[bytes_consumed++] & prefix_mask; + const uint8_t prefix_val = in_buff[bytes_consumed++] & prefix_mask; if (prefix_val < prefix_mask) { @@ -61,13 +61,13 @@ bool Http2HpackIntDecode::translate(const Field& msg, int32_t& bytes_consumed, u uint8_t byte = 0; for (uint8_t multiplier = 0; multiplier < 64; multiplier += 7) { - if (bytes_consumed >= msg.length()) + if (bytes_consumed >= in_len) { *infractions += INF_INT_MISSING_BYTES; events->create_event(EVENT_INT_DECODE_FAILURE); return false; } - byte = buff[bytes_consumed++]; + byte = in_buff[bytes_consumed++]; // For multiplier == 63, do overflow checks if (multiplier == 63) diff --git a/src/service_inspectors/http2_inspect/http2_hpack_decode.h b/src/service_inspectors/http2_inspect/http2_hpack_int_decode.h similarity index 86% rename from src/service_inspectors/http2_inspect/http2_hpack_decode.h rename to src/service_inspectors/http2_inspect/http2_hpack_int_decode.h index bc2211111..48ef44557 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack_decode.h +++ b/src/service_inspectors/http2_inspect/http2_hpack_int_decode.h @@ -15,14 +15,13 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// http2_hpack_decode.h author Maya Dagon +// http2_hpack_int_decode.h author Maya Dagon -#ifndef HTTP2_HPACK_DECODE_H -#define HTTP2_HPACK_DECODE_H +#ifndef HTTP2_HPACK_INT_DECODE_H +#define HTTP2_HPACK_INT_DECODE_H #include "http2_enum.h" #include "main/snort_types.h" -#include "service_inspectors/http_inspect/http_field.h" #include "utils/event_gen.h" #include "utils/infractions.h" @@ -35,7 +34,8 @@ class Http2HpackIntDecode { public: Http2HpackIntDecode(uint8_t prefix, Http2EventGen* events, Http2Infractions* infractions); - bool translate(const Field& msg, int32_t& bytes_consumed, uint64_t& result); + bool translate(const uint8_t* in_buff, const uint32_t in_len, uint32_t& bytes_consumed, + uint64_t& result); private: const uint8_t prefix_mask; diff --git a/src/service_inspectors/http2_inspect/http2_hpack_string_decode.cc b/src/service_inspectors/http2_inspect/http2_hpack_string_decode.cc new file mode 100644 index 000000000..2c24afc6c --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_hpack_string_decode.cc @@ -0,0 +1,98 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 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. +//-------------------------------------------------------------------------- +// http2_hpack_string_decode.cc author Maya Dagon + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "http2_hpack_string_decode.h" + +#include "http2_enum.h" + +using namespace Http2Enums; + +static const uint8_t HUFFMAN_FLAG = 0x80; + +Http2HpackStringDecode::Http2HpackStringDecode(Http2EventGen* events, + Http2Infractions* infractions) : decode7(new Http2HpackIntDecode(7, events, infractions)), + events(events), infractions(infractions) +{ } + +bool Http2HpackStringDecode::translate(const uint8_t* in_buff, const uint32_t in_len, + uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written) +{ + bytes_consumed = 0; + bytes_written = 0; + + if (in_len == 0) + { + *infractions += INF_STRING_EMPTY_BUFF; + events->create_event(EVENT_STRING_DECODE_FAILURE); + return false; + } + + const bool isHuffman = (in_buff[0] & HUFFMAN_FLAG) != 0; + + // Get length + uint64_t encoded_len; + if (!decode7->translate(in_buff, in_len, bytes_consumed, encoded_len)) + return false; + + if (encoded_len > (uint64_t)(in_len - bytes_consumed)) + { + *infractions += INF_STRING_MISSING_BYTES; + events->create_event(EVENT_STRING_DECODE_FAILURE); + return false; + } + + if (encoded_len == 0) + return true; + + if (!isHuffman) + return get_string(in_buff, encoded_len, bytes_consumed, out_buff, out_len, bytes_written); + + return get_huffman_string(in_buff, encoded_len, bytes_consumed, out_buff, out_len, + bytes_written); +} + +Http2HpackStringDecode::~Http2HpackStringDecode() +{ + assert(decode7 != nullptr); + delete decode7; +} + +bool Http2HpackStringDecode::get_string(const uint8_t* in_buff, const uint32_t encoded_len, + uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written) +{ + if (encoded_len > out_len) + { + *infractions += INF_OUT_BUFF_OUT_OF_SPACE; + events->create_event(EVENT_STRING_DECODE_FAILURE); + return false; + } + + while (bytes_written < encoded_len) + out_buff[bytes_written++] = in_buff[bytes_consumed++]; + + return true; +} + +bool Http2HpackStringDecode::get_huffman_string(const uint8_t*, const uint32_t, uint32_t&, + uint8_t*, const uint32_t, uint32_t&) { return false; } + diff --git a/src/service_inspectors/http2_inspect/http2_hpack_string_decode.h b/src/service_inspectors/http2_inspect/http2_hpack_string_decode.h new file mode 100644 index 000000000..172d76831 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_hpack_string_decode.h @@ -0,0 +1,50 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 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. +//-------------------------------------------------------------------------- +// http2_hpack_string_decode.h author Maya Dagon + +#ifndef HTTP2_HPACK_STRING_DECODE_H +#define HTTP2_HPACK_STRING_DECODE_H + +#include "http2_enum.h" +#include "http2_hpack_int_decode.h" + +#include "main/snort_types.h" +#include "utils/event_gen.h" +#include "utils/infractions.h" + +class Http2HpackStringDecode +{ +public: + Http2HpackStringDecode(Http2EventGen* events, Http2Infractions* infractions); + bool translate(const uint8_t* in_buff, const uint32_t in_len, uint32_t& bytes_consumed, + uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written); + ~Http2HpackStringDecode(); + +private: + bool get_string(const uint8_t* in_buff, const uint32_t encoded_len, uint32_t& bytes_consumed, + uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written); + bool get_huffman_string(const uint8_t* in_buff, const uint32_t encoded_len, + uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written); + + Http2HpackIntDecode* const decode7; + Http2EventGen* const events; + Http2Infractions* const infractions; +}; + +#endif + diff --git a/src/service_inspectors/http2_inspect/http2_tables.cc b/src/service_inspectors/http2_inspect/http2_tables.cc index cb52a6795..b50303848 100644 --- a/src/service_inspectors/http2_inspect/http2_tables.cc +++ b/src/service_inspectors/http2_inspect/http2_tables.cc @@ -30,8 +30,9 @@ using namespace Http2Enums; const snort::RuleMap Http2Module::http2_events[] = { - { EVENT_INT_DECODE_FAILURE, "Failed to decode integer value" }, + { EVENT_INT_DECODE_FAILURE, "Error in HPACK integer value" }, { EVENT_INT_LEADING_ZEROS, "Integer value has leading zeros" }, + { EVENT_STRING_DECODE_FAILURE, "Error in HPACK string value" }, { 0, nullptr } }; diff --git a/src/service_inspectors/http2_inspect/test/CMakeLists.txt b/src/service_inspectors/http2_inspect/test/CMakeLists.txt index 6402e3e66..37e0ed038 100644 --- a/src/service_inspectors/http2_inspect/test/CMakeLists.txt +++ b/src/service_inspectors/http2_inspect/test/CMakeLists.txt @@ -14,7 +14,12 @@ add_cpputest( http2_stream_splitter_impl_test ../http2_tables.cc ../../../framework/module.cc ) -add_cpputest( http2_hpack_decode_test +add_cpputest( http2_hpack_int_decode_test SOURCES - ../http2_hpack_decode.cc + ../http2_hpack_int_decode.cc +) +add_cpputest( http2_hpack_string_decode_test + SOURCES + ../http2_hpack_int_decode.cc + ../http2_hpack_string_decode.cc ) diff --git a/src/service_inspectors/http2_inspect/test/http2_hpack_decode_test.cc b/src/service_inspectors/http2_inspect/test/http2_hpack_int_decode_test.cc similarity index 62% rename from src/service_inspectors/http2_inspect/test/http2_hpack_decode_test.cc rename to src/service_inspectors/http2_inspect/test/http2_hpack_int_decode_test.cc index facce5ec3..706f1f8b2 100644 --- a/src/service_inspectors/http2_inspect/test/http2_hpack_decode_test.cc +++ b/src/service_inspectors/http2_inspect/test/http2_hpack_int_decode_test.cc @@ -16,14 +16,14 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// http2_hpack_decode_test.cc author Maya Dagon +// http2_hpack_int_decode_test.cc author Maya Dagon #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "../http2_enum.h" -#include "../http2_hpack_decode.h" +#include "../http2_hpack_int_decode.h" #include #include @@ -37,11 +37,10 @@ int DetectionEngine::queue_event(unsigned int, unsigned int, Actions::Type) { re using namespace Http2Enums; - // // The following tests should result in a successful decode, no infractions/events // -TEST_GROUP(http2_hpack_decode_success) +TEST_GROUP(http2_hpack_int_decode_success) { Http2EventGen events; Http2Infractions inf; @@ -55,134 +54,125 @@ TEST_GROUP(http2_hpack_decode_success) } }; -TEST(http2_hpack_decode_success, 10_using_5_bits) +TEST(http2_hpack_int_decode_success, 10_using_5_bits) { - // prepare field to decode - example from RFC 7541 c.1.1 + // prepare buf to decode - example from RFC 7541 c.1.1 uint8_t buf = 10; - Field f(1, &buf, false); // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(f, bytes_processed, res); + bool success = decode->translate(&buf, 1, bytes_processed, res); // check results CHECK(success == true); CHECK(res == 10); CHECK(bytes_processed == 1); } -TEST(http2_hpack_decode_success, 10_using_5_bits_wtail) +TEST(http2_hpack_int_decode_success, 10_using_5_bits_wtail) { - // prepare field to decode - same as above with an extra byte as leftover + // prepare buf to decode - same as above with an extra byte as leftover uint8_t buf[2] = { 10, 0xff }; - Field f(2, buf, false); // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(f, bytes_processed, res); + bool success = decode->translate(buf, 2, bytes_processed, res); // check results CHECK(success == true); CHECK(res == 10); CHECK(bytes_processed == 1); } -TEST(http2_hpack_decode_success, 1337_using_5_bits) +TEST(http2_hpack_int_decode_success, 1337_using_5_bits) { - // prepare field to decode - example from RFC 7541 c.1.2 + // prepare buf to decode - example from RFC 7541 c.1.2 uint8_t buf[3] = { 31, 0x9a, 10 }; - Field f(3, buf, false); // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(f, bytes_processed, res); + bool success = decode->translate(buf, 3, bytes_processed, res); // check results CHECK(success == true); CHECK(res == 1337); CHECK(bytes_processed == 3); } -TEST(http2_hpack_decode_success, 42_using_8_bits) +TEST(http2_hpack_int_decode_success, 42_using_8_bits) { - // prepare decode object + // prepare decode object Http2HpackIntDecode decode_8(8, &events, &inf); - // prepare field to decode - example from RFC 7541 c.1.3 + // prepare buf to decode - example from RFC 7541 c.1.3 uint8_t buf = 42; - Field f(1, &buf, false); // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode_8.translate(f, bytes_processed, res); + bool success = decode_8.translate(&buf, 1, bytes_processed, res); // check results CHECK(success == true); CHECK(res == 42); CHECK(bytes_processed == 1); } -TEST(http2_hpack_decode_success, max_val_using_5_bit) +TEST(http2_hpack_int_decode_success, max_val_using_5_bit) { - // prepare field to decode - 2^64-1 - uint8_t buf[11] = { 31, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1}; - Field f(11, buf, false); + // prepare buf to decode - 2^64-1 + uint8_t buf[11] = { 31, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1 }; // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(f, bytes_processed, res); + bool success = decode->translate(buf, 11, bytes_processed, res); // check results CHECK(success == true); CHECK(res == 0xFFFFFFFFFFFFFFFF); CHECK(bytes_processed == 11); } -TEST(http2_hpack_decode_success, 31_using_5_bits) -{ - // prepare field to decode - 2^N -1 - uint8_t buf[2] = {31, 0}; - Field f(2, buf, false); +TEST(http2_hpack_int_decode_success, 31_using_5_bits) +{ + // prepare buf to decode - 2^N -1 + uint8_t buf[2] = { 31, 0 }; // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(f, bytes_processed, res); + bool success = decode->translate(buf, 2, bytes_processed, res); // check results CHECK(success == true); CHECK(res == 31); CHECK(bytes_processed == 2); } -TEST(http2_hpack_decode_success, 0_using_5_bits) -{ - // prepare field to decode - 0 using 5 bits +TEST(http2_hpack_int_decode_success, 0_using_5_bits) +{ + // prepare buf to decode - 0 using 5 bits uint8_t buf = 0; - Field f(1, &buf, false); // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode->translate(f, bytes_processed, res); + bool success = decode->translate(&buf, 1, bytes_processed, res); // check results CHECK(success == true); CHECK(res == 0); CHECK(bytes_processed == 1); } - // // The following tests should result in a failure and set infractions/events // -TEST_GROUP(http2_hpack_decode_failure) +TEST_GROUP(http2_hpack_int_decode_failure) { }; -TEST(http2_hpack_decode_failure, 0_len_field) +TEST(http2_hpack_int_decode_failure, 0_len_field) { - // prepare decode object + // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; Http2HpackIntDecode decode_8(8, &local_events, &local_inf); - // prepare field to decode - use field length 0 + // prepare buf to decode - use buf length 0 uint8_t buf = 42; - Field f(0, &buf, false); // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = decode_8.translate(f, bytes_processed, res); + bool success = decode_8.translate(&buf, 0, bytes_processed, res); // check results CHECK(success == false); CHECK(bytes_processed == 0); @@ -190,19 +180,18 @@ TEST(http2_hpack_decode_failure, 0_len_field) CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1))); } -TEST(http2_hpack_decode_failure, too_short) +TEST(http2_hpack_int_decode_failure, too_short) { - // prepare decode object + // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; Http2HpackIntDecode local_decode(5, &local_events, &local_inf); - // prepare field to decode - buffer ends before decode finished + // prepare buf to decode - buffer ends before decode finished uint8_t buf[3] = { 31, 0x9a, 10 }; - Field f(2, buf, false); // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(f, bytes_processed, res); + bool success = local_decode.translate(buf, 2, bytes_processed, res); // check results CHECK(success == false); CHECK(bytes_processed == 2); @@ -210,19 +199,18 @@ TEST(http2_hpack_decode_failure, too_short) CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1))); } -TEST(http2_hpack_decode_failure, multiplier_bigger_than_63) +TEST(http2_hpack_int_decode_failure, multiplier_bigger_than_63) { // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; Http2HpackIntDecode local_decode(5, &local_events, &local_inf); - // prepare field to decode - multiplier > 63 + // prepare buf to decode - multiplier > 63 uint8_t buf[13] = { 31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 1 }; - Field f(13, buf, false); // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(f, bytes_processed, res); + bool success = local_decode.translate(buf, 13, bytes_processed, res); // check results CHECK(success == false); CHECK(bytes_processed == 11); @@ -230,19 +218,18 @@ TEST(http2_hpack_decode_failure, multiplier_bigger_than_63) CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1))); } -TEST(http2_hpack_decode_failure, add_val_overflow) +TEST(http2_hpack_int_decode_failure, add_val_overflow) { - // prepare decode object + // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; Http2HpackIntDecode local_decode(5, &local_events, &local_inf); - // prepare field to decode - value to add itself is already creating overflow + // prepare buf to decode - value to add itself is already creating overflow uint8_t buf[12] = { 31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF, 1 }; - Field f(12, buf, false); // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(f, bytes_processed, res); + bool success = local_decode.translate(buf, 12, bytes_processed, res); // check results CHECK(success == false); CHECK(bytes_processed == 11); @@ -250,19 +237,18 @@ TEST(http2_hpack_decode_failure, add_val_overflow) CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1))); } -TEST(http2_hpack_decode_failure, add_val_overflow2) +TEST(http2_hpack_int_decode_failure, add_val_overflow2) { - // prepare decode object + // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; Http2HpackIntDecode local_decode(5, &local_events, &local_inf); - // prepare field to decode - adding value to result kept so far creates overflow - uint8_t buf[11] = { 31, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1}; - Field f(11, buf, false); + // prepare buf to decode - adding value to result kept so far creates overflow + uint8_t buf[11] = { 31, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1 }; // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(f, bytes_processed, res); + bool success = local_decode.translate(buf, 11, bytes_processed, res); // check results CHECK(success == false); CHECK(bytes_processed == 11); @@ -270,19 +256,18 @@ TEST(http2_hpack_decode_failure, add_val_overflow2) CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1))); } -TEST(http2_hpack_decode_failure, 2_64_using_5_bit) +TEST(http2_hpack_int_decode_failure, 2_64_using_5_bit) { - // prepare decode object + // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; Http2HpackIntDecode local_decode(5, &local_events, &local_inf); - // prepare field to decode - 2^64 - uint8_t buf[11] = { 31, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1}; - Field f(11, buf, false); + // prepare buf to decode - 2^64 + uint8_t buf[11] = { 31, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1 }; // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(f, bytes_processed, res); + bool success = local_decode.translate(buf, 11, bytes_processed, res); // check results CHECK(success == false); CHECK(bytes_processed == 11); @@ -290,28 +275,26 @@ TEST(http2_hpack_decode_failure, 2_64_using_5_bit) CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1))); } - // // The following tests should result in a successful decode and set // leading zeros infraction and event // -TEST_GROUP(http2_hpack_decode_leading_zeros) +TEST_GROUP(http2_hpack_int_decode_leading_zeros) { }; -TEST(http2_hpack_decode_leading_zeros, leading_zeros) +TEST(http2_hpack_int_decode_leading_zeros, leading_zeros) { - // prepare decode object + // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; Http2HpackIntDecode local_decode(5, &local_events, &local_inf); - // prepare field to decode - MSB is zero - uint8_t buf[3] = { 31, 0x80, 0}; - Field f(3, buf, false); + // prepare buf to decode - MSB is zero + uint8_t buf[3] = { 31, 0x80, 0 }; // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(f, bytes_processed, res); + bool success = local_decode.translate(buf, 3, bytes_processed, res); // check results CHECK(success == true); CHECK(res == 31); @@ -320,20 +303,19 @@ TEST(http2_hpack_decode_leading_zeros, leading_zeros) CHECK(local_events.get_raw() == (1<<(EVENT_INT_LEADING_ZEROS-1))); } -TEST(http2_hpack_decode_leading_zeros, leading_0_byte_11) +TEST(http2_hpack_int_decode_leading_zeros, leading_0_byte_11) { - // prepare decode object + // prepare decode object Http2EventGen local_events; Http2Infractions local_inf; Http2HpackIntDecode local_decode(5, &local_events, &local_inf); - // prepare field to decode - multiplier 63 doesn't create overflow, should alert on + // prepare buf to decode - multiplier 63 doesn't create overflow, should alert on // leading 0 - uint8_t buf[11] = { 31, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0}; - Field f(11, buf, false); + uint8_t buf[11] = { 31, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0 }; // decode - int32_t bytes_processed = 0; + uint32_t bytes_processed = 0; uint64_t res = 0; - bool success = local_decode.translate(f, bytes_processed, res); + bool success = local_decode.translate(buf, 11, bytes_processed, res); // check results CHECK(success == true); CHECK(res == 0x7FFFFFFFFFFFFFFF); @@ -346,3 +328,4 @@ int main(int argc, char** argv) { return CommandLineTestRunner::RunAllTests(argc, argv); } + diff --git a/src/service_inspectors/http2_inspect/test/http2_hpack_string_decode_test.cc b/src/service_inspectors/http2_inspect/test/http2_hpack_string_decode_test.cc new file mode 100644 index 000000000..478fef0ac --- /dev/null +++ b/src/service_inspectors/http2_inspect/test/http2_hpack_string_decode_test.cc @@ -0,0 +1,280 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 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. +//-------------------------------------------------------------------------- + +// http2_hpack_string_decode_test.cc author Maya Dagon + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../http2_enum.h" +#include "../http2_hpack_string_decode.h" +#include "../../http_inspect/http_enum.h" + +#include +#include +#include + +namespace snort +{ +// Stubs whose sole purpose is to make the test code link +int DetectionEngine::queue_event(unsigned int, unsigned int, Actions::Type) { return 0; } +} + +using namespace Http2Enums; + +// +// The following tests should result in a successful decode, no infractions/events +// +TEST_GROUP(http2_hpack_string_decode_success) +{ + Http2EventGen events; + Http2Infractions inf; + Http2HpackStringDecode* const decode = new Http2HpackStringDecode(&events, &inf); + + void teardown() override + { + CHECK(inf.none_found() == true); + CHECK(events.none_found() == true); + delete decode; + } +}; + +TEST(http2_hpack_string_decode_success, custom_key_len_10) +{ + // prepare buf to decode - example from RFC 7541 c.3.3 + uint8_t buf[11] = { 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79 }; + // decode + uint32_t bytes_processed = 0, bytes_written = 0; + uint8_t res[10]; + bool success = decode->translate(buf, 11, bytes_processed, res, 10, bytes_written); + // check results + CHECK(success == true); + CHECK(memcmp(res, "custom-key", 10) == 0); + CHECK(bytes_processed == 11); + CHECK(bytes_written == 10); +} + +TEST(http2_hpack_string_decode_success, custom_key_len_10_wtail) +{ + // prepare buf to decode - same as above with an extra byte as leftover + uint8_t buf[12] = { 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x79 }; + // decode + uint32_t bytes_processed = 0, bytes_written = 0; + uint8_t res[12]; + bool success = decode->translate(buf, 12, bytes_processed, res, 12, bytes_written); + // check results + CHECK(success == true); + CHECK(memcmp(res, "custom-key", 10) == 0); + CHECK(bytes_processed == 11); + CHECK(bytes_written == 10); +} + +TEST(http2_hpack_string_decode_success, int_is_more_than_1_byte) +{ + // prepare buf to decode - length is 2^7 + uint8_t buf[130]; + buf[0] = 0x7f; + buf[1] = 1; + memset(&buf[2], 'A', 128); + // decode + uint32_t bytes_processed = 0, bytes_written = 0; + uint8_t res[130]; + bool success = decode->translate(buf, 130, bytes_processed, res, 130, bytes_written); + // check results + CHECK(success == true); + CHECK(bytes_processed == 130); + CHECK(bytes_written == 128); + CHECK(memcmp(res, &buf[2], 128) == 0); +} + +TEST(http2_hpack_string_decode_success, empty_string) +{ + // prepare buf to decode - length is 0 (empty string) + uint8_t buf = 0; + // decode + uint32_t bytes_processed = 0, bytes_written = 0; + uint8_t res = 10; // random value, just to check it wasn't overwritten + bool success = decode->translate(&buf, 1, bytes_processed, &res, 1, bytes_written); + // check results + CHECK(success == true); + CHECK(bytes_processed == 1); + CHECK(bytes_written == 0); + CHECK(res == 10); +} + +TEST(http2_hpack_string_decode_success, string_len_1) +{ + // prepare buf to decode - length is 1 + uint8_t buf[2] = { 1, 'A' }; + // decode + uint32_t bytes_processed = 0, bytes_written = 0; + uint8_t res = 0; + bool success = decode->translate(buf, 2, bytes_processed, &res, 1, bytes_written); + // check results + CHECK(success == true); + CHECK(bytes_processed == 2); + CHECK(bytes_written == 1); + CHECK(res == 'A'); +} + +TEST(http2_hpack_string_decode_success, max_field_length) +{ + // prepare buf to decode - int + string == MAX_OCTETS (Field limitation) + uint8_t buf[HttpEnums::MAX_OCTETS]; + buf[0] = 0x7F; + buf[1] = 0xA1; + buf[2] = 0xF1; + buf[3]= 0x3; + memset(&buf[4], 'A', HttpEnums::MAX_OCTETS-4); + // decode + uint32_t bytes_processed = 0, bytes_written = 0; + uint8_t res[HttpEnums::MAX_OCTETS]; + bool success = decode->translate(buf, HttpEnums::MAX_OCTETS, bytes_processed, res, + HttpEnums::MAX_OCTETS, bytes_written); + // check results + CHECK(success == true); + CHECK(bytes_processed == HttpEnums::MAX_OCTETS); + CHECK(bytes_written == (HttpEnums::MAX_OCTETS-4)); + CHECK(memcmp(res, &buf[4], bytes_written) == 0); +} + +// +// The following tests should trigger infractions/events +// +TEST_GROUP(http2_hpack_string_decode_infractions) +{ +}; + +TEST(http2_hpack_string_decode_infractions, 0_len_field) +{ + // prepare decode object + Http2EventGen local_events; + Http2Infractions local_inf; + Http2HpackStringDecode local_decode(&local_events, &local_inf); + // prepare buf to decode - use field length 0 + uint8_t buf = 0; + // decode + uint32_t bytes_processed = 0, bytes_written = 0; + uint8_t res; + bool success = local_decode.translate(&buf, 0, bytes_processed, &res, 1, bytes_written); + // check results + CHECK(success == false); + CHECK(bytes_processed == 0); + CHECK(bytes_written == 0); + CHECK(local_inf.get_raw() == (1<