#include "utils/util.h"
#include "utils/sf_email_attach_decode.h"
+#define MAX_BUF 65535
+
#define UU_DECODE_CHAR(c) (((c) - 0x20) & 0x3f)
-int Email_DecodeState::getCodeDepth(int code_depth, int64_t file_depth)
+int MimeDecode::getCodeDepth(int code_depth, int64_t file_depth)
{
if (file_depth < 0 )
return code_depth;
return (depth + decoded_bytes - decode_bytes_total);
}
-int Email_DecodeState::getDetectionSize(int b64_depth, int qp_depth, int uu_depth, int bitenc_depth)
-{
- int iRet = 0;
-
- switch (decode_type)
- {
- case DECODE_B64:
- iRet = limitDetection(b64_depth, decoded_bytes, b64_state.decode_bytes_read);
- break;
- case DECODE_QP:
- iRet = limitDetection(qp_depth, decoded_bytes, qp_state.decode_bytes_read);
- break;
- case DECODE_UU:
- iRet = limitDetection(uu_depth, decoded_bytes, uu_state.decode_bytes_read);
- break;
- case DECODE_BITENC:
- iRet = limitDetection(bitenc_depth, decoded_bytes, bitenc_state.bytes_read);
- break;
- default:
- break;
- }
-
- return iRet;
-}
-
-inline void Email_DecodeState::ClearPrevEncodeBuf()
+inline void MimeDecode::ClearPrevEncodeBuf()
{
prev_encoded_bytes = 0;
prev_encoded_buf = nullptr;
}
-void Email_DecodeState::ResetBytesRead()
+void MimeDecode::reset_bytes_read()
{
uu_state.begin_found = uu_state.end_found = 0;
ClearPrevEncodeBuf();
bitenc_state.bytes_read = 0;
}
-void Email_DecodeState::ResetDecodedBytes()
+void MimeDecode::reset_decoded_bytes()
{
decodePtr = nullptr;
decoded_bytes = 0;
decode_present = 0;
}
-inline void Email_DecodeState::ResetEmailDecodeState()
+inline void MimeDecode::reset_decode_state()
{
uu_state.begin_found = uu_state.end_found = 0;
- ResetDecodedBytes();
+ reset_decoded_bytes();
ClearPrevEncodeBuf();
}
-void Email_DecodeState::ClearEmailDecodeState()
+void MimeDecode::clear_decode_state()
{
decode_type = DECODE_NONE;
- ResetEmailDecodeState();
+ reset_decode_state();
}
-int Email_DecodeState::Base64Decode(const uint8_t* start, const uint8_t* end)
+DecodeResult MimeDecode::Base64Decode(const uint8_t* start, const uint8_t* end)
{
uint32_t encode_avail = 0, decode_avail = 0;
uint8_t* encode_buf, * decode_buf;
if (encode_avail ==0 || decode_avail ==0 ||
(!encode_buf) || (!decode_buf))
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_EXCEEDED;
}
if (sf_strip_CRLF(start, (end-start), encode_buf + prev_bytes, encode_avail,
&act_encode_size) != 0)
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_FAIL;
}
if (sf_base64decode(encode_buf, act_encode_size, decode_buf, decode_avail, &act_decode_size) !=
0)
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_FAIL;
}
else if (!act_decode_size && !encode_avail)
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_FAIL;
}
return DECODE_SUCCESS;
}
-int Email_DecodeState::QPDecode(const uint8_t* start, const uint8_t* end)
+DecodeResult MimeDecode::QPDecode(const uint8_t* start, const uint8_t* end)
{
uint32_t encode_avail = 0, decode_avail = 0;
uint8_t* encode_buf, * decode_buf;
if (encode_avail ==0 || decode_avail ==0 ||
(!encode_buf) || (!decode_buf))
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_EXCEEDED;
}
if (sf_strip_LWS(start, (end-start), encode_buf + prev_bytes, encode_avail,
&act_encode_size) != 0)
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_FAIL;
}
if (sf_qpdecode((char*)encode_buf, act_encode_size, (char*)decode_buf, decode_avail,
&bytes_read, &act_decode_size) != 0)
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_FAIL;
}
else if (!act_decode_size && !encode_avail)
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_FAIL;
}
return DECODE_SUCCESS;
}
-int Email_DecodeState::UUDecode(const uint8_t* start, const uint8_t* end)
+DecodeResult MimeDecode::UUDecode(const uint8_t* start, const uint8_t* end)
{
uint32_t encode_avail = 0, decode_avail = 0;
uint8_t* encode_buf, * decode_buf;
(!encode_buf) || (!decode_buf))
{
uu_state.begin_found = 0;
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_EXCEEDED;
}
if (SafeMemcpy((encode_buf + prev_bytes), start, act_encode_size, encode_buf, (encode_buf+
encode_avail + prev_bytes)) != SAFEMEM_SUCCESS)
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_FAIL;
}
}
&act_decode_size,
&(uu_state.begin_found), &(uu_state.end_found)) != 0)
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_FAIL;
}
else if (!act_decode_size && !encode_avail)
{
/* Have insufficient data to decode */
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_FAIL;
}
return DECODE_SUCCESS;
}
-int Email_DecodeState::BitEncExtract(const uint8_t* start, const uint8_t* end)
+DecodeResult MimeDecode::BitEncExtract(const uint8_t* start, const uint8_t* end)
{
uint32_t bytes_avail = 0;
uint32_t act_size = 0;
* 2. Stop decoding when we are out of memory */
if (bytes_avail ==0)
{
- ResetEmailDecodeState();
+ reset_decode_state();
return DECODE_EXCEEDED;
}
return DECODE_SUCCESS;
}
-void Email_DecodeState::process_decode_type(const char* start, int length, bool cnt_xf)
+void MimeDecode::process_decode_type(const char* start, int length, bool cnt_xf)
{
const char* tmp = NULL;
}
}
-int Email_DecodeState::EmailDecode(const uint8_t* start, const uint8_t* end)
+DecodeResult MimeDecode::decode_data(const uint8_t* start, const uint8_t* end)
{
- int iRet = DECODE_FAIL;
+ DecodeResult iRet = DECODE_FAIL;
switch (decode_type)
{
return iRet;
}
-int Email_DecodeState::get_decoded_data(uint8_t** buf, uint32_t* size)
+int MimeDecode::get_detection_depth(int b64_depth, int qp_depth, int uu_depth, int bitenc_depth)
+{
+ int iRet = 0;
+
+ switch (decode_type)
+ {
+ case DECODE_B64:
+ iRet = limitDetection(b64_depth, decoded_bytes, b64_state.decode_bytes_read);
+ break;
+ case DECODE_QP:
+ iRet = limitDetection(qp_depth, decoded_bytes, qp_state.decode_bytes_read);
+ break;
+ case DECODE_UU:
+ iRet = limitDetection(uu_depth, decoded_bytes, uu_state.decode_bytes_read);
+ break;
+ case DECODE_BITENC:
+ iRet = limitDetection(bitenc_depth, decoded_bytes, bitenc_state.bytes_read);
+ break;
+ default:
+ break;
+ }
+
+ return iRet;
+}
+
+int MimeDecode::get_decoded_data(uint8_t** buf, uint32_t* size)
{
if (decoded_bytes > 0)
*size = decoded_bytes;
return 0;
}
-DecodeType Email_DecodeState::get_decode_type()
+DecodeType MimeDecode::get_decode_type()
{
return decode_type;
}
-Email_DecodeState::Email_DecodeState(
+MimeDecode::MimeDecode(
int max_depth, int b64_depth, int qp_depth,
int uu_depth, int bitenc_depth, int64_t file_depth)
{
bitenc_state.bytes_read = 0;
}
-Email_DecodeState::~Email_DecodeState()
+MimeDecode::~MimeDecode()
{
if (work_buffer)
free(work_buffer);
#ifndef FILE_MIME_DECODE_H
#define FILE_MIME_DECODE_H
-// Email attachment decoder
+// Email attachment decoder, supports Base64, QP, UU, and Bit7/8
#include <stdlib.h>
#include "main/snort_types.h"
-#define MAX_BUF 65535
-
-// FIXIT-L: Should make this a (scoped?) enum
-#define DECODE_SUCCESS 0
-#define DECODE_EXCEEDED 1 // Decode Complete when we reach the max depths
-#define DECODE_FAIL -1
+typedef enum
+{
+ DECODE_SUCCESS,
+ DECODE_EXCEEDED, // Decode Complete when we reach the max depths
+ DECODE_FAIL
+} DecodeResult;
-// FIXIT-L: Should be a scoped enum
typedef enum
{
DECODE_NONE = 0,
int depth;
};
-// Should be a C++ OOP struct with constructor, etc
-class Email_DecodeState
+class MimeDecode
{
public:
- Email_DecodeState(int max_depth, int b64_depth, int qp_depth,
+ MimeDecode(int max_depth, int b64_depth, int qp_depth,
int uu_depth, int bitenc_depth, int64_t file_depth);
- ~Email_DecodeState();
+ ~MimeDecode();
+
+ // get the decode type from buffer
+ // bool cnt_xf: true if there is transfer encode defined, false otherwise
void process_decode_type(const char* start, int length, bool cnt_xf);
- int EmailDecode(const uint8_t* start, const uint8_t* end);
- int getDetectionSize(int b64_depth, int qp_depth, int uu_depth, int bitenc_depth);
- void ClearEmailDecodeState();
- void ResetDecodedBytes();
- void ResetBytesRead();
+
+ // Main function to decode file data
+ DecodeResult decode_data(const uint8_t* start, const uint8_t* end);
+
+ int get_detection_depth(int b64_depth, int qp_depth, int uu_depth, int bitenc_depth);
+
+ void clear_decode_state();
+ void reset_decoded_bytes();
+ void reset_bytes_read();
int get_decoded_data(uint8_t** buf, uint32_t* size);
DecodeType get_decode_type();
BitEnc_DecodeState bitenc_state;
int getCodeDepth(int code_depth, int64_t file_depth);
inline void ClearPrevEncodeBuf();
-
- inline void ResetEmailDecodeState();
- int Base64Decode(const uint8_t* start, const uint8_t* end);
- int QPDecode(const uint8_t* start, const uint8_t* end);
- int UUDecode(const uint8_t* start, const uint8_t* end);
- int BitEncExtract(const uint8_t* start, const uint8_t* end);
-
+ inline void reset_decode_state();
+ DecodeResult Base64Decode(const uint8_t* start, const uint8_t* end);
+ DecodeResult QPDecode(const uint8_t* start, const uint8_t* end);
+ DecodeResult UUDecode(const uint8_t* start, const uint8_t* end);
+ DecodeResult BitEncExtract(const uint8_t* start, const uint8_t* end);
};
-struct MimeStats
-{
- uint64_t memcap_exceeded;
- uint64_t attachments[DECODE_ALL];
- uint64_t decoded_bytes[DECODE_ALL];
-};
+// Todo: add statistics
+//struct MimeStats
+//{
+// uint64_t memcap_exceeded;
+// uint64_t attachments[DECODE_ALL];
+// uint64_t decoded_bytes[DECODE_ALL];
+//};
#endif
{
if (decode_state == NULL)
{
- decode_state = new Email_DecodeState(
+ decode_state = new MimeDecode(
decode_conf->get_max_depth(), decode_conf->get_b64_depth(),
decode_conf->get_qp_depth(), decode_conf->get_uu_depth(),
decode_conf->get_bitenc_depth(), decode_conf->get_file_depth());
if (decode_state != NULL)
{
- decode_state->ResetBytesRead();
+ decode_state->reset_bytes_read();
decode_state->process_decode_type(data, size, cnt_xf);
state_flags |= MIME_FLAG_EMAIL_ATTACH;
}
if (( attach_start < attach_end ) && decode_state)
{
- if (decode_state->EmailDecode(attach_start, attach_end) < DECODE_SUCCESS )
+ if (decode_state->decode_data(attach_start, attach_end) == DECODE_FAIL )
{
decode_alert(decode_state);
}
data_state = STATE_DATA_INIT;
state_flags = 0;
if (decode_state)
- decode_state->ClearEmailDecodeState();
+ decode_state->clear_decode_state();
}
const uint8_t* MimeSession::process_mime_data_paf(Flow* flow, const uint8_t* start, const uint8_t* end,
if (conf)
{
- int detection_size = decode_state->getDetectionSize(conf->get_b64_depth(),
+ int detection_size = decode_state->get_detection_depth(conf->get_b64_depth(),
conf->get_qp_depth(), conf->get_uu_depth(), conf->get_bitenc_depth());
set_file_data(buffer, (uint16_t)detection_size);
}
log_state->set_file_name_from_log(flow);
}
- decode_state->ResetDecodedBytes();
+ decode_state->reset_decoded_bytes();
}
/* if we got the data end reset state, otherwise we're probably still in the data