@ -31,7 +31,7 @@
# include "util-unittest.h"
# include "util-memcmp.h"
# include "util-print.h"
# include "util-validate.h"
# include "rust.h"
/* Character constants */
@ -1226,17 +1226,37 @@ static uint8_t ProcessBase64Remainder(
uint32_t ret ;
uint8_t remainder = 0 ;
uint32_t remdec = 0 ;
SCLogDebug ( " Base64 line remainder found: %u " , state - > bvr_len ) ;
uint32_t consumed_bytes = 0 ;
uint32_t cnt = 0 ;
/* Fill in block with first few bytes of current line */
remainder = B64_BLOCK - state - > bvr_len ;
remainder = remainder < len ? remainder : len ;
if ( remainder & & buf ) {
memcpy ( state - > bvremain + state - > bvr_len , buf , remainder ) ;
if ( buf ) {
uint8_t tmp [ B64_BLOCK ] ;
uint32_t rem = 0 ;
for ( int i = 0 ; i < state - > bvr_len ; i + + ) {
/* Special case of SP in remainder */
if ( state - > bvremain [ i ] ! = ' ' ) {
tmp [ cnt + + ] = state - > bvremain [ i ] ;
} else {
rem + + ;
}
}
if ( cnt ! = 4 ) {
/* Special case where the buf where we take extra bytes from contains SP */
for ( int i = 0 ; i < len & & cnt < 4 ; i + + ) {
if ( buf [ i ] ! = ' ' ) {
tmp [ cnt + + ] = buf [ i ] ;
} else {
rem + + ;
}
}
}
memcpy ( state - > bvremain , tmp , cnt ) ;
state - > bvr_len + = remainder ;
remainder + = rem ;
}
state - > bvr_len + = remainder ;
/* If data chunk buffer will be full, then clear it now */
if ( DATA_CHUNK_SIZE - state - > data_chunk_len < ASCII_BLOCK ) {
@ -1248,18 +1268,17 @@ static uint8_t ProcessBase64Remainder(
}
}
/* Only decode if divisible by 4 */
if ( state - > bvr_len = = B64_BLOCK | | force ) {
remdec = DecodeBase64 ( state - > data_chunk + state - > data_chunk_len , state - > bvremain ,
state - > bvr_len , BASE64_MODE_RFC2045 ) ;
if ( remdec > 0 ) {
uint32_t avail_space = DATA_CHUNK_SIZE - state - > data_chunk_len ;
Base64Ecode code = DecodeBase64 ( state - > data_chunk + state - > data_chunk_len , avail_space ,
state - > bvremain , state - > bvr_len , & consumed_bytes , & remdec , BASE64_MODE_RFC2045 ) ;
if ( remdec > 0 & & ( code = = BASE64_ECODE_OK | | code = = BASE64_ECODE_BUF ) ) {
/* Track decoded length */
state - > stack - > top - > data - > decoded_body_len + = remdec ;
/* Update length */
state - > data_chunk_len + = remdec ;
/* If data chunk buffer is now full, then clear */
if ( DATA_CHUNK_SIZE - state - > data_chunk_len < ASCII_BLOCK ) {
@ -1271,7 +1290,7 @@ static uint8_t ProcessBase64Remainder(
" failed " ) ;
}
}
} else {
} else if ( code = = BASE64_ECODE_ERR ) {
/* Track failed base64 */
state - > stack - > top - > data - > anomaly_flags | = ANOM_INVALID_BASE64 ;
state - > msg - > anomaly_flags | = ANOM_INVALID_BASE64 ;
@ -1300,9 +1319,8 @@ static int ProcessBase64BodyLine(const uint8_t *buf, uint32_t len,
MimeDecParseState * state )
{
int ret = MIME_DEC_OK ;
uint8_t rem1 = 0 , rem2 = 0 ;
uint32_t numDecoded , remaining , offset , avail , tobuf ;
uint8_t rem1 = 0 ;
uint32_t numDecoded , remaining , offset ;
/* Track long line */
if ( len > MAX_ENC_LINE_LEN ) {
state - > stack - > top - > data - > anomaly_flags | = ANOM_LONG_ENC_LINE ;
@ -1311,89 +1329,83 @@ static int ProcessBase64BodyLine(const uint8_t *buf, uint32_t len,
len , MAX_ENC_LINE_LEN ) ;
}
if ( state - > bvr_len + len < B64_BLOCK ) {
memcpy ( state - > bvremain + state - > bvr_len , buf , len ) ;
state - > bvr_len + = len ;
len = 0 ;
}
/* First process remaining from previous line */
if ( state - > bvr_len > 0 ) {
SCLogDebug ( " Base64 line remainder found: %u " , state - > bvr_len ) ;
/* Process remainder and return number of bytes pulled from current buffer */
rem1 = ProcessBase64Remainder ( buf , ( uint8_t ) len , state , 0 ) ;
int32_t remainder_b64 = len - rem1 ;
if ( remainder_b64 < B64_BLOCK ) {
memcpy ( state - > bvremain , buf + rem1 , remainder_b64 ) ;
state - > bvr_len + = remainder_b64 ;
return ret ;
}
}
/* No error and at least some more data needs to be decoded */
if ( ( int ) ( len - rem1 ) > 0 ) {
/* Determine whether we need to save a remainder if not divisible by 4 */
rem2 = ( len - rem1 ) % B64_BLOCK ;
if ( rem2 > 0 ) {
remaining = len - rem1 ;
offset = rem1 ;
while ( remaining > 0 & & remaining > = B64_BLOCK ) {
uint32_t consumed_bytes = 0 ;
uint32_t avail_space = DATA_CHUNK_SIZE - state - > data_chunk_len ;
Base64Ecode code = DecodeBase64 ( state - > data_chunk + state - > data_chunk_len , avail_space ,
buf + offset , remaining , & consumed_bytes , & numDecoded , BASE64_MODE_RFC2045 ) ;
SCLogDebug ( " Base64 saving remainder: %u " , rem2 ) ;
memcpy ( state - > bvremain , buf + ( len - rem2 ) , rem2 ) ;
state - > bvr_len = rem2 ;
}
DEBUG_VALIDATE_BUG_ON ( consumed_bytes > remaining ) ;
/* Process remaining in loop in case buffer fills up */
remaining = len - rem1 - rem2 ;
offset = rem1 ;
while ( remaining > 0 ) {
/* Determine amount to add to buffer */
avail = ( DATA_CHUNK_SIZE - state - > data_chunk_len ) * B64_BLOCK / ASCII_BLOCK ;
tobuf = avail > remaining ? remaining : avail ;
while ( tobuf % 4 ! = 0 ) {
tobuf - - ;
}
uint32_t leftover_bytes = remaining - consumed_bytes ;
if ( numDecoded > 0 & & ( code = = BASE64_ECODE_OK | | code = = BASE64_ECODE_BUF ) ) {
/* Track decoded length */
state - > stack - > top - > data - > decoded_body_len + = numDecoded ;
/* Update length */
state - > data_chunk_len + = numDecoded ;
if ( tobuf < B64_BLOCK ) {
SCLogDebug ( " Error: Invalid state for decoding base-64 block" ) ;
if ( ( int ) ( DATA_CHUNK_SIZE - state - > data_chunk_len ) < 0 ) {
SCLogDebug ( " Error: Invalid Chunk length: %u " , state - > data_chunk_len ) ;
return MIME_DEC_ERR_PARSE ;
}
SCLogDebug ( " Decoding: %u " , len - rem1 - rem2 ) ;
numDecoded = DecodeBase64 ( state - > data_chunk + state - > data_chunk_len , buf + offset ,
tobuf , BASE64_MODE_RFC2045 ) ;
if ( numDecoded > 0 ) {
/* Track decoded length */
state - > stack - > top - > data - > decoded_body_len + = numDecoded ;
/* Update length */
state - > data_chunk_len + = numDecoded ;
if ( ( int ) ( DATA_CHUNK_SIZE - state - > data_chunk_len ) < 0 ) {
SCLogDebug ( " Error: Invalid Chunk length: %u " ,
state - > data_chunk_len ) ;
ret = MIME_DEC_ERR_PARSE ;
/* If buffer full, then invoke callback */
if ( DATA_CHUNK_SIZE - state - > data_chunk_len < ASCII_BLOCK ) {
/* Invoke pre-processor and callback */
ret = ProcessDecodedDataChunk ( state - > data_chunk , state - > data_chunk_len , state ) ;
if ( ret ! = MIME_DEC_OK ) {
SCLogDebug ( " Error: ProcessDecodedDataChunk() function failed " ) ;
break ;
}
/* If buffer full, then invoke callback */
if ( DATA_CHUNK_SIZE - state - > data_chunk_len < ASCII_BLOCK ) {
/* Invoke pre-processor and callback */
ret = ProcessDecodedDataChunk ( state - > data_chunk ,
state - > data_chunk_len , state ) ;
if ( ret ! = MIME_DEC_OK ) {
SCLogDebug ( " Error: ProcessDecodedDataChunk() "
" function failed " ) ;
}
}
} else {
/* Track failed base64 */
state - > stack - > top - > data - > anomaly_flags | = ANOM_INVALID_BASE64 ;
state - > msg - > anomaly_flags | = ANOM_INVALID_BASE64 ;
SCLogDebug ( " Error: DecodeBase64() function failed " ) ;
PrintChars ( SC_LOG_DEBUG , " Base64 failed string " , buf + offset , tobuf ) ;
}
/* Update counts */
remaining - = tobuf ;
offset + = tobuf ;
} else if ( code = = BASE64_ECODE_ERR ) {
/* Track failed base64 */
state - > stack - > top - > data - > anomaly_flags | = ANOM_INVALID_BASE64 ;
state - > msg - > anomaly_flags | = ANOM_INVALID_BASE64 ;
SCLogDebug ( " Error: DecodeBase64() function failed " ) ;
ret = MIME_DEC_ERR_DATA ;
break ;
}
if ( leftover_bytes < B64_BLOCK ) {
memcpy ( state - > bvremain , buf + offset + consumed_bytes , leftover_bytes ) ;
state - > bvr_len = leftover_bytes ;
return MIME_DEC_OK ;
}
/* Update counts */
remaining - = consumed_bytes ;
offset + = consumed_bytes ;
/* If remaining is 4 by this time, it's likely due to error/spaces during processing */
if ( remaining = = 4 ) {
memcpy ( state - > bvremain , buf + offset , remaining ) ;
state - > bvr_len = remaining ;
break ;
}
if ( ( remaining > 0 & & remaining < B64_BLOCK ) | |
( remaining > 0 & & ( remaining < B64_BLOCK ) & & consumed_bytes < remaining ) ) {
memcpy ( state - > bvremain , buf + offset , remaining ) ;
state - > bvr_len = remaining ;
break ;
}
}
return ret ;
}
@ -3169,8 +3181,8 @@ static int MimeDecParseFullMsgTest02(void)
MimeDecFreeEntity ( entity ) ;
if ( expected_count ! = line_count ) {
SCLogInfo ( " Warning: Line count is invalid: expected - %d actual - %d " ,
expected_count, line_count) ;
SCLogInfo ( " Warning: Line count is invalid: expected - %d actual - %d " , expected_count ,
line_count) ;
return 0 ;
}
@ -3180,6 +3192,7 @@ static int MimeDecParseFullMsgTest02(void)
static int MimeBase64DecodeTest01 ( void )
{
int ret = 0 ;
uint32_t consumed_bytes = 0 , num_decoded = 0 ;
const char * msg = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890@ "
" #$%^&*()-=_+,./;'[]<>?: " ;
@ -3190,7 +3203,8 @@ static int MimeBase64DecodeTest01(void)
if ( dst = = NULL )
return 0 ;
ret = DecodeBase64 ( dst , ( const uint8_t * ) base64msg , strlen ( base64msg ) , BASE64_MODE_RFC2045 ) ;
ret = DecodeBase64 ( dst , strlen ( msg ) + 1 , ( const uint8_t * ) base64msg , strlen ( base64msg ) ,
& consumed_bytes , & num_decoded , BASE64_MODE_RFC2045 ) ;
if ( memcmp ( dst , msg , strlen ( msg ) ) = = 0 ) {
ret = 1 ;
@ -3320,6 +3334,207 @@ static int MimeDecParseLongFilename01(void)
PASS ;
}
static int MimeDecParseSmallRemInp ( void )
{
// Remainder dA
// New input: AAAA
char mimemsg [ ] = " TWltZSBkZWNvZGluZyB pcyBzbyBO T1QgZnV uISBJIGNhbm5vdA " ;
uint32_t line_count = 0 ;
MimeDecGetConfig ( ) - > decode_base64 = true ;
MimeDecGetConfig ( ) - > decode_quoted_printable = true ;
MimeDecGetConfig ( ) - > extract_urls = true ;
/* Init parser */
MimeDecParseState * state = MimeDecInitParser ( & line_count , TestDataChunkCallback ) ;
state - > stack - > top - > data - > ctnt_flags | = CTNT_IS_ATTACHMENT ;
const char * str = " From: Sender1 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " To: Recipient1 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " Content-Type: text/plain " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " Content-Transfer-Encoding: base64 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) mimemsg , strlen ( mimemsg ) , 1 , state ) ) ;
str = " AAAA " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
/* Completed */
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseComplete ( state ) ) ;
MimeDecEntity * msg = state - > msg ;
FAIL_IF_NOT ( msg ) ;
/* filename is not too long */
FAIL_IF ( msg - > anomaly_flags & ANOM_LONG_FILENAME ) ;
MimeDecFreeEntity ( msg ) ;
/* De Init parser */
MimeDecDeInitParser ( state ) ;
PASS ;
}
static int MimeDecParseRemSp ( void )
{
// Should have remainder vd A
char mimemsg [ ] = " TWltZSBkZWNvZGluZyBpc yBzbyBOT1QgZnVuISBJIGNhbm5vd A " ;
uint32_t line_count = 0 ;
MimeDecGetConfig ( ) - > decode_base64 = true ;
MimeDecGetConfig ( ) - > decode_quoted_printable = true ;
MimeDecGetConfig ( ) - > extract_urls = true ;
/* Init parser */
MimeDecParseState * state = MimeDecInitParser ( & line_count , TestDataChunkCallback ) ;
state - > stack - > top - > data - > ctnt_flags | = CTNT_IS_ATTACHMENT ;
const char * str = " From: Sender1 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " To: Recipient1 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " Content-Type: text/plain " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " Content-Transfer-Encoding: base64 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) mimemsg , strlen ( mimemsg ) , 1 , state ) ) ;
/* Completed */
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseComplete ( state ) ) ;
MimeDecEntity * msg = state - > msg ;
FAIL_IF_NOT ( msg ) ;
/* filename is not too long */
FAIL_IF ( msg - > anomaly_flags & ANOM_LONG_FILENAME ) ;
MimeDecFreeEntity ( msg ) ;
/* De Init parser */
MimeDecDeInitParser ( state ) ;
PASS ;
}
static int MimeDecVerySmallInp ( void )
{
// Remainder: A
// New input: aA
char mimemsg [ ] = " TWltZSBkZWNvZGluZyB pcyBzbyBO T1QgZnV uISBJIGNhbm5vA " ;
uint32_t line_count = 0 ;
MimeDecGetConfig ( ) - > decode_base64 = true ;
MimeDecGetConfig ( ) - > decode_quoted_printable = true ;
MimeDecGetConfig ( ) - > extract_urls = true ;
/* Init parser */
MimeDecParseState * state = MimeDecInitParser ( & line_count , TestDataChunkCallback ) ;
state - > stack - > top - > data - > ctnt_flags | = CTNT_IS_ATTACHMENT ;
const char * str = " From: Sender1 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " To: Recipient1 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " Content-Type: text/plain " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " Content-Transfer-Encoding: base64 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) mimemsg , strlen ( mimemsg ) , 1 , state ) ) ;
str = " aA " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
/* Completed */
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseComplete ( state ) ) ;
MimeDecEntity * msg = state - > msg ;
FAIL_IF_NOT ( msg ) ;
/* filename is not too long */
FAIL_IF ( msg - > anomaly_flags & ANOM_LONG_FILENAME ) ;
MimeDecFreeEntity ( msg ) ;
/* De Init parser */
MimeDecDeInitParser ( state ) ;
PASS ;
}
static int MimeDecParseOddLen ( void )
{
char mimemsg [ ] = " TWltZSBkZWNvZGluZyB pcyBzbyBO T1QgZnV uISBJIGNhbm5vdA " ;
uint32_t line_count = 0 ;
MimeDecGetConfig ( ) - > decode_base64 = true ;
MimeDecGetConfig ( ) - > decode_quoted_printable = true ;
MimeDecGetConfig ( ) - > extract_urls = true ;
/* Init parser */
MimeDecParseState * state = MimeDecInitParser ( & line_count , TestDataChunkCallback ) ;
state - > stack - > top - > data - > ctnt_flags | = CTNT_IS_ATTACHMENT ;
const char * str = " From: Sender1 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " To: Recipient1 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " Content-Type: text/plain " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " Content-Transfer-Encoding: base64 " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
str = " " ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) str , strlen ( str ) , 1 , state ) ) ;
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseLine ( ( uint8_t * ) mimemsg , strlen ( mimemsg ) , 1 , state ) ) ;
/* Completed */
FAIL_IF_NOT ( MIME_DEC_OK = = MimeDecParseComplete ( state ) ) ;
MimeDecEntity * msg = state - > msg ;
FAIL_IF_NOT ( msg ) ;
/* filename is not too long */
FAIL_IF ( msg - > anomaly_flags & ANOM_LONG_FILENAME ) ;
MimeDecFreeEntity ( msg ) ;
/* De Init parser */
MimeDecDeInitParser ( state ) ;
PASS ;
}
static int MimeDecParseLongFilename02 ( void )
{
/* contains 40 character filename and 500+ characters following filename */
@ -3404,5 +3619,9 @@ void MimeDecRegisterTests(void)
UtRegisterTest ( " MimeIsIpv6HostTest01 " , MimeIsIpv6HostTest01 ) ;
UtRegisterTest ( " MimeDecParseLongFilename01 " , MimeDecParseLongFilename01 ) ;
UtRegisterTest ( " MimeDecParseLongFilename02 " , MimeDecParseLongFilename02 ) ;
UtRegisterTest ( " MimeDecParseSmallRemInp " , MimeDecParseSmallRemInp ) ;
UtRegisterTest ( " MimeDecParseRemSp " , MimeDecParseRemSp ) ;
UtRegisterTest ( " MimeDecVerySmallInp " , MimeDecVerySmallInp ) ;
UtRegisterTest ( " MimeDecParseOddLen " , MimeDecParseOddLen ) ;
# endif /* UNITTESTS */
}