Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Base64 backports 6.0.x/v2 #9292

Merged
merged 9 commits into from
Jul 28, 2023
10 changes: 5 additions & 5 deletions .github/workflows/builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ jobs:
- name: Extracting suricata-verify
run: tar xf prep/suricata-verify.tar.gz
- name: Running suricata-verify
run: python3 ./suricata-verify/run.py -q
run: python3 ./suricata-verify/run.py -q --debug-failed
# Now install and make sure headers and libraries aren't
# installed until requested.
- run: make install
Expand Down Expand Up @@ -581,7 +581,7 @@ jobs:
- name: Extracting suricata-verify
run: tar xf prep/suricata-verify.tar.gz
- name: Running suricata-verify
run: python3 ./suricata-verify/run.py -q
run: python3 ./suricata-verify/run.py -q --debug-failed
- run: make install
- run: suricata-update -V
- run: suricatasc -h
Expand Down Expand Up @@ -671,7 +671,7 @@ jobs:
- name: Extracting suricata-verify
run: tar xf prep/suricata-verify.tar.gz
- name: Running suricata-verify
run: python3 ./suricata-verify/run.py -q
run: python3 ./suricata-verify/run.py -q --debug-failed
- run: make install
- run: suricata-update -V
- run: suricatasc -h
Expand Down Expand Up @@ -751,7 +751,7 @@ jobs:
- name: Extracting suricata-verify
run: tar xf prep/suricata-verify.tar.gz
- name: Running suricata-verify
run: python3 ./suricata-verify/run.py -q
run: python3 ./suricata-verify/run.py -q --debug-failed
- run: make install
- run: suricata-update -V
- run: suricatasc -h
Expand Down Expand Up @@ -1531,7 +1531,7 @@ jobs:
run: |
./src/suricata --build-info
./src/suricata -u -l /tmp/
python3 ./suricata-verify/run.py -q
python3 ./suricata-verify/run.py -q --debug-failed

windows-msys2-mingw64-windivert:
name: Windows MSYS2 MINGW64 (WinDivert)
Expand Down
65 changes: 51 additions & 14 deletions src/util-base64.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ static inline int GetBase64Value(uint8_t c)
return val;
}

/**
* \brief Checks if the given char in a byte array is Base64 alphabet
*
* \param Char that needs to be checked
*
* \return True if the char was Base64 alphabet, False otherwise
*/
bool IsBase64Alphabet(uint8_t encoded_byte)
{
if (GetBase64Value(encoded_byte) < 0 && encoded_byte != '=') {
return false;
}
return true;
}

/**
* \brief Decodes a 4-byte base64-encoded block into a 3-byte ascii-encoded block
*
Expand Down Expand Up @@ -106,9 +121,9 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src,
/* Get decimal representation */
val = GetBase64Value(src[i]);
if (val < 0) {
if ((mode == BASE64_MODE_RFC2045) && (src[i] == ' ')) {
if (mode == BASE64_MODE_RFC2045 && src[i] != '=') {
if (bbidx == 0) {
/* Special case where last block of data has a leading space */
/* Special case where last block of data has a leading space or invalid char */
leading_sp++;
}
sp++;
Expand Down Expand Up @@ -155,9 +170,21 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src,
}
}

if (!valid && mode == BASE64_MODE_RFC4648) {
padding = B64_BLOCK - bbidx;
*decoded_bytes += ASCII_BLOCK - padding;
if (bbidx > 0 && bbidx < 4 && ((!valid && mode == BASE64_MODE_RFC4648))) {
/* Decoded bytes for 1 or 2 base64 encoded bytes is 1 */
padding = bbidx > 1 ? B64_BLOCK - bbidx : 2;
uint32_t numDecoded_blk = ASCII_BLOCK - (padding < B64_BLOCK ? padding : ASCII_BLOCK);
if (dest_size < *decoded_bytes + numDecoded_blk) {
SCLogDebug("Destination buffer full");
ecode = BASE64_ECODE_BUF;
return ecode;
}
/* if the destination size is not at least 3 Bytes long, it'll give a dynamic
* buffer overflow while decoding, so, return and let the caller take care of the
* remaining bytes to be decoded which should always be < 4 at this stage */
if (dest_size - *decoded_bytes < 3)
return BASE64_ECODE_BUF;
*decoded_bytes += numDecoded_blk;
DecodeBase64Block(dptr, b64);
*consumed_bytes += bbidx;
}
Expand All @@ -183,7 +210,7 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src,
{ \
uint32_t consumed_bytes = 0, num_decoded = 0; \
uint8_t dst[dest_size]; \
Base64Ecode code = DecodeBase64(dst, strlen(fin_str), (const uint8_t *)src, strlen(src), \
Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src), \
&consumed_bytes, &num_decoded, BASE64_MODE_RFC2045); \
FAIL_IF(code != ecode); \
FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0); \
Expand All @@ -195,7 +222,7 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src,
{ \
uint32_t consumed_bytes = 0, num_decoded = 0; \
uint8_t dst[dest_size]; \
Base64Ecode code = DecodeBase64(dst, strlen(fin_str), (const uint8_t *)src, strlen(src), \
Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src), \
&consumed_bytes, &num_decoded, BASE64_MODE_RFC4648); \
FAIL_IF(code != ecode); \
FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0); \
Expand All @@ -220,7 +247,7 @@ static int B64DecodeInCompleteString(void)
* SGVsbG8gV29ybGR6 : Hello Worldz
* */
const char *src = "SGVsbG8gV29ybGR";
const char *fin_str = "Hello Wor"; // bc it'll error out on last 3 bytes
const char *fin_str = "Hello Wor";
TEST_RFC2045(src, fin_str, strlen(fin_str), strlen(fin_str), strlen(src) - 3, BASE64_ECODE_OK);
PASS;
}
Expand All @@ -233,7 +260,7 @@ static int B64DecodeCompleteStringWSp(void)

const char *src = "SGVs bG8 gV29y bGQ=";
const char *fin_str = "Hello World";
TEST_RFC2045(src, fin_str, strlen(fin_str) + 1, strlen(fin_str), strlen(src), BASE64_ECODE_OK);
TEST_RFC2045(src, fin_str, strlen(fin_str) + 3, strlen(fin_str), strlen(src), BASE64_ECODE_OK);
PASS;
}

Expand All @@ -246,7 +273,8 @@ static int B64DecodeInCompleteStringWSp(void)

const char *src = "SGVs bG8 gV29y bGQ";
const char *fin_str = "Hello Wor";
TEST_RFC2045(src, fin_str, strlen(fin_str), strlen(fin_str), strlen(src) - 3, BASE64_ECODE_OK);
TEST_RFC2045(src, fin_str, strlen(fin_str) + 1 /* 12 B in dest_size */, strlen(fin_str),
strlen(src) - 3, BASE64_ECODE_OK);
PASS;
}

Expand Down Expand Up @@ -303,18 +331,22 @@ static int B64TestVectorsRFC2045(void)
const char *fin_str8 = "foobar";

const char *src9 = "Zm$9vYm.Fy";
const char *fin_str9 = "";
const char *fin_str9 = "foobar";

const char *src10 = "Y21Wd2IzSjBaVzFoYVd4bWNtRjFaRUJoZEc4dVoyOTJMbUYxOmpqcHh4b3Rhb2w%5";
const char *fin_str10 = "cmVwb3J0ZW1haWxmcmF1ZEBhdG8uZ292LmF1:jjpxxotaol9";

TEST_RFC2045(src1, fin_str1, strlen(fin_str1), strlen(fin_str1), strlen(src1), BASE64_ECODE_OK);
TEST_RFC2045(src1, fin_str1, ASCII_BLOCK * 2, strlen(fin_str1), strlen(src1), BASE64_ECODE_OK);
TEST_RFC2045(src2, fin_str2, ASCII_BLOCK * 2, strlen(fin_str2), strlen(src2), BASE64_ECODE_OK);
TEST_RFC2045(src3, fin_str3, ASCII_BLOCK * 2, strlen(fin_str3), strlen(src3), BASE64_ECODE_OK);
TEST_RFC2045(src4, fin_str4, ASCII_BLOCK * 2, strlen(fin_str4), strlen(src4), BASE64_ECODE_OK);
TEST_RFC2045(src5, fin_str5, ASCII_BLOCK * 2, strlen(fin_str5), strlen(src5), BASE64_ECODE_OK);
TEST_RFC2045(src6, fin_str6, ASCII_BLOCK * 2, strlen(fin_str6), strlen(src6), BASE64_ECODE_OK);
TEST_RFC2045(src7, fin_str7, ASCII_BLOCK * 2, strlen(fin_str7), strlen(src7), BASE64_ECODE_OK);
TEST_RFC2045(src8, fin_str8, ASCII_BLOCK * 2, strlen(fin_str8), strlen(src8), BASE64_ECODE_OK);
TEST_RFC2045(src9, fin_str9, ASCII_BLOCK * 2, 0, 0,
BASE64_ECODE_ERR); // TODO this should be accepted just like the previous string
TEST_RFC2045(src9, fin_str9, ASCII_BLOCK * 2, strlen(fin_str9), strlen(src9), BASE64_ECODE_OK);
TEST_RFC2045(src10, fin_str10, strlen(fin_str10) + 2, strlen(fin_str10), strlen(src10),
BASE64_ECODE_OK);
PASS;
}

Expand Down Expand Up @@ -347,6 +379,9 @@ static int B64TestVectorsRFC4648(void)
const char *src9 = "Zm$9vYm.Fy";
const char *fin_str9 = "f";

const char *src10 = "Y21Wd2IzSjBaVzFoYVd4bWNtRjFaRUJoZEc4dVoyOTJMbUYxOmpqcHh4b3Rhb2w%3D";
const char *fin_str10 = "cmVwb3J0ZW1haWxmcmF1ZEBhdG8uZ292LmF1:jjpxxotaol";

TEST_RFC4648(src1, fin_str1, ASCII_BLOCK * 2, strlen(fin_str1), strlen(src1), BASE64_ECODE_OK);
TEST_RFC4648(src2, fin_str2, ASCII_BLOCK * 2, strlen(fin_str2), strlen(src2), BASE64_ECODE_OK);
TEST_RFC4648(src3, fin_str3, ASCII_BLOCK * 2, strlen(fin_str3), strlen(src3), BASE64_ECODE_OK);
Expand All @@ -356,6 +391,8 @@ static int B64TestVectorsRFC4648(void)
TEST_RFC4648(src7, fin_str7, ASCII_BLOCK * 2, strlen(fin_str7), strlen(src7), BASE64_ECODE_OK);
TEST_RFC4648(src8, fin_str8, ASCII_BLOCK * 2, 1 /* f */, 2 /* Zm */, BASE64_ECODE_ERR);
TEST_RFC4648(src9, fin_str9, ASCII_BLOCK * 2, 1 /* f */, 2 /* Zm */, BASE64_ECODE_ERR);
TEST_RFC4648(src10, fin_str10, strlen(fin_str10) + 1, strlen(fin_str10), strlen(src10) - 3,
BASE64_ECODE_ERR);
PASS;
}

Expand Down
5 changes: 3 additions & 2 deletions src/util-base64.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ typedef enum {
* BASE64("fooba") = "Zm9vYmE="
* BASE64("foobar") = "Zm9vYmFy"
* BASE64("foobar") = "Zm 9v Ym Fy" <-- Notice how the spaces are ignored
* BASE64("f") = "Zm$9vYm.Fy" # TODO according to RFC, All line breaks or *other characters*
* not found in base64 alphabet must be ignored by decoding software
* BASE64("foobar") = "Zm$9vYm.Fy" # According to RFC 2045, All line breaks or *other
* characters* not found in base64 alphabet must be ignored by decoding software
* */
BASE64_MODE_RFC2045, /* SPs are allowed during transfer but must be skipped by Decoder */
BASE64_MODE_STRICT,
Expand Down Expand Up @@ -95,6 +95,7 @@ typedef enum {
/* Function prototypes */
Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t len,
uint32_t *consumed_bytes, uint32_t *decoded_bytes, Base64Mode mode);
bool IsBase64Alphabet(uint8_t encoded_byte);

#endif

Expand Down
7 changes: 4 additions & 3 deletions src/util-decode-mime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1197,15 +1197,15 @@ static uint32_t ProcessBase64Remainder(

/* Strip spaces in remainder */
for (uint8_t i = 0; i < state->bvr_len; i++) {
if (state->bvremain[i] != ' ') {
if (IsBase64Alphabet(state->bvremain[i])) {
block[cnt++] = state->bvremain[i];
}
}

/* if we don't have 4 bytes see if we can fill it from `buf` */
if (buf && len > 0 && cnt != B64_BLOCK) {
for (uint32_t i = 0; i < len && cnt < B64_BLOCK; i++) {
if (buf[i] != ' ') {
if (IsBase64Alphabet(buf[i])) {
block[cnt++] = buf[i];
}
buf_consumed++;
Expand Down Expand Up @@ -1289,7 +1289,8 @@ static inline MimeDecRetCode ProcessBase64BodyLineCopyRemainder(
return MIME_DEC_ERR_DATA;

for (uint32_t i = offset; i < buf_len; i++) {
if (buf[i] != ' ') {
// Skip any characters outside of the base64 alphabet as per RFC 2045
if (IsBase64Alphabet(buf[i])) {
DEBUG_VALIDATE_BUG_ON(state->bvr_len >= B64_BLOCK);
if (state->bvr_len >= B64_BLOCK)
return MIME_DEC_ERR_DATA;
Expand Down
Loading