This is an automated email from the ASF dual-hosted git repository. bcall pushed a commit to branch 9.1.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit d5806bd715b83cb31477493dc5b1997437a95b54 Author: Mo Chen <uncorr...@gmail.com> AuthorDate: Mon Mar 28 17:22:23 2022 -0500 [ink_base64] Fix buffer size computation (#8736) These buffer size computation macros are meant to be used for callers of ats_base64_encode/decode. They were not taking into account a null terminator, which is always written by those functions. This causes a buffer overflow when encode or decode are called on buffers of certain sizes, e.g. decode on an input buffer of size 4. This change makes these buffer size calculations match the functions. It also updates unit tests for the access_control plugin, which uses these functions. Since this is a macro change, plugins which use encode/decode will need to be recompiled. (cherry picked from commit 3c1006eb1984304a78cea140889290720584e9a0) --- include/tscore/ink_base64.h | 15 +++++++++++++-- .../access_control/unit_tests/test_utils.cc | 18 +++++++++--------- plugins/experimental/access_control/utils.cc | 4 ++-- plugins/experimental/metalink/metalink.cc | 2 +- src/tscore/ink_base64.cc | 6 ++---- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/include/tscore/ink_base64.h b/include/tscore/ink_base64.h index a3c4d510a..1473a312a 100644 --- a/include/tscore/ink_base64.h +++ b/include/tscore/ink_base64.h @@ -42,5 +42,16 @@ bool ats_base64_encode(const unsigned char *inBuffer, size_t inBufferSize, char bool ats_base64_decode(const char *inBuffer, size_t inBufferSize, unsigned char *outBuffer, size_t outBufSize, size_t *length); // Little helper functions to calculate minimum required output buffer for encoding/decoding. -#define ATS_BASE64_ENCODE_DSTLEN(_length) ((_length * 8) / 6 + 4) -#define ATS_BASE64_DECODE_DSTLEN(_length) (((_length + 3) / 4) * 3) +// These sizes include one byte for null termination, because ats_base64_encode and ats_base64_decode will always write a null +// terminator. +inline constexpr size_t +ats_base64_encode_dstlen(size_t length) +{ + return ((length + 2) / 3) * 4 + 1; +} + +inline constexpr size_t +ats_base64_decode_dstlen(size_t length) +{ + return ((length + 3) / 4) * 3 + 1; +} diff --git a/plugins/experimental/access_control/unit_tests/test_utils.cc b/plugins/experimental/access_control/unit_tests/test_utils.cc index 43b1f5c49..5040964aa 100644 --- a/plugins/experimental/access_control/unit_tests/test_utils.cc +++ b/plugins/experimental/access_control/unit_tests/test_utils.cc @@ -36,7 +36,7 @@ TEST_CASE("Base64: estimate buffer size needed to encode a message", "[Base64][a /* Test with a zero decoded message length */ encodedLen = cryptoBase64EncodedSize(0); - CHECK(4 == encodedLen); + CHECK(1 == encodedLen); /* Test with a random non-zero decoded message length */ encodedLen = cryptoBase64EncodedSize(64); @@ -44,11 +44,11 @@ TEST_CASE("Base64: estimate buffer size needed to encode a message", "[Base64][a /* Test the space for padding. Size of encoding that would result in 2 x "=" padding */ encodedLen = cryptoBase64EncodedSize(strlen("176a1620e31b14782ba2b66de3edc5b3cb19630475b2ce2ee292d5fd0fe41c3abc")); - CHECK(92 == encodedLen); + CHECK(89 == encodedLen); /* Test the space for padding. Size of encoding that would result in 1 x "=" padding */ encodedLen = cryptoBase64EncodedSize(strlen("176a1620e31b14782ba2b66de3edc5b3cb19630475b2ce2ee292d5fd0fe41c3ab")); - CHECK(90 == encodedLen); + CHECK(89 == encodedLen); /* Test the space for padding. Size of encoding that would result in no padding */ encodedLen = cryptoBase64EncodedSize(strlen("176a1620e31b14782ba2b66de3edc5b3cb19630475b2ce2ee292d5fd0fe41c3a")); @@ -63,27 +63,27 @@ TEST_CASE("Base64: estimate buffer size needed to decode a message", "[Base64][a /* Padding with 2 x '=' */ encoded = "MTc2YTE2MjBlMzFiMTQ3ODJiYTJiNjZkZTNlZGM1YjNjYjE5NjMwNDc1YjJjZTJlZTI5MmQ1ZmQwZmU0MWMzYQ=="; encodedLen = cryptoBase64DecodeSize(encoded, strlen(encoded)); - CHECK(66 == encodedLen); + CHECK(67 == encodedLen); /* Padding with 1 x '=' */ encoded = "MTc2YTE2MjBlMzFiMTQ3ODJiYTJiNjZkZTNlZGM1YjNjYjE5NjMwNDc1YjJjZTJlZTI5MmQ1ZmQwZmU0MWMzYWI="; encodedLen = cryptoBase64DecodeSize(encoded, strlen(encoded)); - CHECK(66 == encodedLen); + CHECK(67 == encodedLen); /* Padding with 0 x "=" */ encoded = "MTc2YTE2MjBlMzFiMTQ3ODJiYTJiNjZkZTNlZGM1YjNjYjE5NjMwNDc1YjJjZTJlZTI5MmQ1ZmQwZmU0MWMzYWJj"; encodedLen = cryptoBase64DecodeSize(encoded, strlen(encoded)); - CHECK(66 == encodedLen); + CHECK(67 == encodedLen); /* Test empty encoded message calculation */ encoded = ""; encodedLen = cryptoBase64DecodeSize(encoded, strlen(encoded)); - CHECK(0 == encodedLen); + CHECK(1 == encodedLen); /* Test empty encoded message calculation */ encoded = nullptr; encodedLen = cryptoBase64DecodeSize(encoded, 0); - CHECK(0 == encodedLen); + CHECK(1 == encodedLen); } TEST_CASE("Base64: quick encode / decode", "[Base64][access_control][utility]") @@ -103,7 +103,7 @@ TEST_CASE("Base64: quick encode / decode", "[Base64][access_control][utility]") encodedMessageLen)); size_t decodedMessageEstimatedLen = cryptoBase64DecodeSize(encodedMessage, encodedMessageLen); - CHECK(66 == decodedMessageEstimatedLen); + CHECK(67 == decodedMessageEstimatedLen); char decodedMessage[encodedMessageEstimatedLen]; size_t decodedMessageLen = cryptoBase64Decode(encodedMessage, encodedMessageLen, decodedMessage, encodedMessageLen); diff --git a/plugins/experimental/access_control/utils.cc b/plugins/experimental/access_control/utils.cc index 53911e463..211716da5 100644 --- a/plugins/experimental/access_control/utils.cc +++ b/plugins/experimental/access_control/utils.cc @@ -296,7 +296,7 @@ cryptoMessageDigestEqual(const char *md1, size_t md1Len, const char *md2, size_t size_t cryptoBase64EncodedSize(size_t decodedSize) { - return ATS_BASE64_ENCODE_DSTLEN(decodedSize); + return ats_base64_encode_dstlen(decodedSize); } /** @@ -308,7 +308,7 @@ cryptoBase64EncodedSize(size_t decodedSize) size_t cryptoBase64DecodeSize(const char *encoded, size_t encodedLen) { - return ATS_BASE64_DECODE_DSTLEN(encodedLen); + return ats_base64_decode_dstlen(encodedLen); } /** diff --git a/plugins/experimental/metalink/metalink.cc b/plugins/experimental/metalink/metalink.cc index 9f7d201a4..b8eae9a8c 100644 --- a/plugins/experimental/metalink/metalink.cc +++ b/plugins/experimental/metalink/metalink.cc @@ -693,7 +693,7 @@ location_handler(TSCont contp, TSEvent event, void * /* edata ATS_UNUSED */) const char *value; int length; - char digest[33]; /* ATS_BASE64_DECODE_DSTLEN() */ + char digest[33]; /* ats_base64_decode_dstlen() */ SendData *data = static_cast<SendData *>(TSContDataGet(contp)); TSContDestroy(contp); diff --git a/src/tscore/ink_base64.cc b/src/tscore/ink_base64.cc index a1da352f3..bc68758bc 100644 --- a/src/tscore/ink_base64.cc +++ b/src/tscore/ink_base64.cc @@ -28,8 +28,6 @@ * authentication scheme does not require them. This implementation is * intended for web-related use, and line breaks are not implemented. * - * These routines return char*'s to malloc-ed strings. The caller is - * responsible for freeing the strings. */ #include "tscore/ink_platform.h" #include "tscore/ink_base64.h" @@ -44,7 +42,7 @@ ats_base64_encode(const unsigned char *inBuffer, size_t inBufferSize, char *outB char *obuf = outBuffer; char in_tail[4]; - if (outBufSize < ATS_BASE64_ENCODE_DSTLEN(inBufferSize)) { + if (outBufSize < ats_base64_encode_dstlen(inBufferSize)) { return false; } @@ -130,7 +128,7 @@ ats_base64_decode(const char *inBuffer, size_t inBufferSize, unsigned char *outB int inputBytesDecoded = 0; // Make sure there is sufficient space in the output buffer - if (outBufSize < ATS_BASE64_DECODE_DSTLEN(inBufferSize)) { + if (outBufSize < ats_base64_decode_dstlen(inBufferSize)) { return false; }