diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 6e8a0d2824d9..b3eea82c7f8b 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) diff --git a/src/util-base64.c b/src/util-base64.c index e84224e5b01d..13a8e1eabb23 100644 --- a/src/util-base64.c +++ b/src/util-base64.c @@ -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 * @@ -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++; @@ -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; } @@ -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); \ @@ -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); \ @@ -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; } @@ -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; } @@ -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; } @@ -303,9 +331,12 @@ 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); @@ -313,8 +344,9 @@ static int B64TestVectorsRFC2045(void) 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; } @@ -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); @@ -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; } diff --git a/src/util-base64.h b/src/util-base64.h index 57bf7135a86b..ce490d46584a 100644 --- a/src/util-base64.h +++ b/src/util-base64.h @@ -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, @@ -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 diff --git a/src/util-decode-mime.c b/src/util-decode-mime.c index d5e2f1c2e5a9..b6c231b36374 100644 --- a/src/util-decode-mime.c +++ b/src/util-decode-mime.c @@ -1197,7 +1197,7 @@ 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]; } } @@ -1205,7 +1205,7 @@ static uint32_t ProcessBase64Remainder( /* 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++; @@ -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;