smtp/mime: fix null ptr deref on bad traffic

Due to missing error handling, a bad mime message could put the
mime parser in an error state, without the SMTP layer taking this
into account. So the SMTP layer would continue to pass data to the
mime parser, even though it was in an error state.

When the parser would be fed a very long line while in this state,
it would try to set an error flag in the state. However, due to
the error state, this setting of the flag would dereference a null
pointer.

This patch fixes this issue by updating the mime parser to check
the state it is in when receiving new input. It will refuse to
process futher data while in the error state. It will also return
a new error code to indicate to the SMTP layer that the parser
was in an error state.
pull/3541/head
Victor Julien 7 years ago
parent fa2ce043cf
commit 36e84b929c

@ -799,31 +799,10 @@ static int SMTPProcessCommandBDAT(SMTPState *state, Flow *f,
SCReturnInt(0);
}
static int SMTPProcessCommandDATA(SMTPState *state, Flow *f,
AppLayerParserState *pstate)
static void SetMimeEvents(SMTPState *state)
{
SCEnter();
if (!(state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
/* looks like are still waiting for a confirmination from the server */
return 0;
}
if (state->current_line_len == 1 && state->current_line[0] == '.') {
state->parser_state &= ~SMTP_PARSER_STATE_COMMAND_DATA_MODE;
/* kinda like a hack. The mail sent in DATA mode, would be
* acknowledged with a reply. We insert a dummy command to
* the command buffer to be used by the reply handler to match
* the reply received */
SMTPInsertCommandIntoCommandBuffer(SMTP_COMMAND_DATA_MODE, state, f);
if (smtp_config.decode_mime && state->curr_tx->mime_state != NULL) {
/* Complete parsing task */
int ret = MimeDecParseComplete(state->curr_tx->mime_state);
if (ret != MIME_DEC_OK) {
SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_PARSE_FAILED);
SCLogDebug("MimeDecParseComplete() function failed");
if (state->curr_tx->mime_state->msg == NULL) {
return;
}
/* Generate decoder events */
@ -853,6 +832,40 @@ static int SMTPProcessCommandDATA(SMTPState *state, Flow *f,
SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_BOUNDARY_TOO_LONG);
}
}
/**
* \retval 0 ok
* \retval -1 error
*/
static int SMTPProcessCommandDATA(SMTPState *state, Flow *f,
AppLayerParserState *pstate)
{
SCEnter();
if (!(state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
/* looks like are still waiting for a confirmination from the server */
return 0;
}
if (state->current_line_len == 1 && state->current_line[0] == '.') {
state->parser_state &= ~SMTP_PARSER_STATE_COMMAND_DATA_MODE;
/* kinda like a hack. The mail sent in DATA mode, would be
* acknowledged with a reply. We insert a dummy command to
* the command buffer to be used by the reply handler to match
* the reply received */
SMTPInsertCommandIntoCommandBuffer(SMTP_COMMAND_DATA_MODE, state, f);
if (smtp_config.decode_mime && state->curr_tx->mime_state != NULL) {
/* Complete parsing task */
int ret = MimeDecParseComplete(state->curr_tx->mime_state);
if (ret != MIME_DEC_OK) {
SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_PARSE_FAILED);
SCLogDebug("MimeDecParseComplete() function failed");
}
/* Generate decoder events */
SetMimeEvents(state);
}
state->curr_tx->done = 1;
SCLogDebug("marked tx as done");
}
@ -861,12 +874,20 @@ static int SMTPProcessCommandDATA(SMTPState *state, Flow *f,
if (state->current_command == SMTP_COMMAND_DATA &&
(state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
if (smtp_config.decode_mime && state->curr_tx->mime_state) {
if (smtp_config.decode_mime && state->curr_tx->mime_state != NULL) {
int ret = MimeDecParseLine((const uint8_t *) state->current_line,
state->current_line_len, state->current_line_delimiter_len,
state->curr_tx->mime_state);
if (ret != MIME_DEC_OK) {
if (ret != MIME_DEC_ERR_STATE) {
/* Generate decoder events */
SetMimeEvents(state);
SCLogDebug("MimeDecParseLine() function returned an error code: %d", ret);
SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_PARSE_FAILED);
}
/* keep the parser in its error state so we can log that,
* the parser will reject new data */
}
}
}

@ -2330,6 +2330,11 @@ static int ProcessMimeEntity(const uint8_t *buf, uint32_t len,
SCLogDebug("START FLAG: %s", StateFlags[state->state_flag]);
if (state->state_flag == PARSE_ERROR) {
SCLogDebug("START FLAG: PARSE_ERROR, bail");
return MIME_DEC_ERR_STATE;
}
/* Track long line */
if (len > MAX_LINE_LEN) {
state->stack->top->data->anomaly_flags |= ANOM_LONG_LINE;
@ -2477,6 +2482,11 @@ int MimeDecParseComplete(MimeDecParseState *state)
SCLogDebug("Parsing flagged as completed");
if (state->state_flag == PARSE_ERROR) {
SCLogDebug("parser in error state: PARSE_ERROR");
return MIME_DEC_ERR_STATE;
}
/* Store the header value */
ret = StoreMimeHeader(state);
if (ret != MIME_DEC_OK) {

@ -83,7 +83,8 @@ typedef enum MimeDecRetCode {
MIME_DEC_MORE = 1,
MIME_DEC_ERR_DATA = -1,
MIME_DEC_ERR_MEM = -2,
MIME_DEC_ERR_PARSE = -3
MIME_DEC_ERR_PARSE = -3,
MIME_DEC_ERR_STATE = -4, /**< parser in error state */
} MimeDecRetCode;
/**

Loading…
Cancel
Save