Skip to content

Commit 6e4acda

Browse files
committed
smtp: handle pipelined replies on owning tx
Track the transaction id for each queued SMTP command so replies can update the transaction that created the command instead of always using the current transaction. Ticket: OISF#8393
1 parent d4f005d commit 6e4acda

2 files changed

Lines changed: 87 additions & 32 deletions

File tree

src/app-layer-smtp.c

Lines changed: 85 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,13 @@ static inline void SMTPSetProgressTC(SMTPTransaction *tx, uint8_t progress)
229229
{
230230
if (tx != NULL && tx->progress_tc < progress) {
231231
tx->progress_tc = progress;
232+
tx->tx_data.updated_tc = true;
232233
}
233234
}
234235

235-
static bool SMTPTransactionIsComplete(const SMTPTransaction *tx)
236+
static bool SMTPTransactionRequestIsComplete(const SMTPTransaction *tx)
236237
{
237-
return tx && tx->progress_ts == SMTP_REQUEST_COMPLETE &&
238-
tx->progress_tc == SMTP_RESPONSE_COMPLETE;
238+
return tx && tx->progress_ts == SMTP_REQUEST_COMPLETE;
239239
}
240240

241241
typedef struct SMTPThreadCtx_ {
@@ -673,7 +673,8 @@ static AppLayerResult SMTPGetLine(Flow *f, StreamSlice *slice, SMTPState *state,
673673
}
674674
}
675675

676-
static int SMTPInsertCommandIntoCommandBuffer(uint8_t command, SMTPState *state)
676+
static int SMTPInsertCommandIntoCommandBuffer(
677+
SMTPState *state, uint8_t command, const SMTPTransaction *tx)
677678
{
678679
SCEnter();
679680
void *ptmp;
@@ -688,12 +689,26 @@ static int SMTPInsertCommandIntoCommandBuffer(uint8_t command, SMTPState *state)
688689
sizeof(uint8_t) * (state->cmds_buffer_len + increment));
689690
if (ptmp == NULL) {
690691
SCFree(state->cmds);
692+
SCFree(state->cmds_tx_ids);
691693
state->cmds = NULL;
694+
state->cmds_tx_ids = NULL;
692695
SCLogDebug("SCRealloc failure");
693696
return -1;
694697
}
695698
state->cmds = ptmp;
696699

700+
ptmp = SCRealloc(
701+
state->cmds_tx_ids, sizeof(uint64_t) * (state->cmds_buffer_len + increment));
702+
if (ptmp == NULL) {
703+
SCFree(state->cmds);
704+
SCFree(state->cmds_tx_ids);
705+
state->cmds = NULL;
706+
state->cmds_tx_ids = NULL;
707+
SCLogDebug("SCRealloc failure");
708+
return -1;
709+
}
710+
state->cmds_tx_ids = ptmp;
711+
697712
state->cmds_buffer_len += increment;
698713
}
699714
if (state->cmds_cnt >= 1 &&
@@ -712,6 +727,7 @@ static int SMTPInsertCommandIntoCommandBuffer(uint8_t command, SMTPState *state)
712727
}
713728

714729
state->cmds[state->cmds_cnt] = command;
730+
state->cmds_tx_ids[state->cmds_cnt] = tx != NULL ? tx->tx_id : UINT64_MAX;
715731
state->cmds_cnt++;
716732

717733
return 0;
@@ -766,29 +782,29 @@ static void SetMimeEvents(SMTPState *state, uint32_t events)
766782
}
767783
}
768784

769-
static inline void SMTPTransactionComplete(SMTPState *state)
785+
static inline void SMTPTransactionComplete(SMTPTransaction *tx)
770786
{
771-
DEBUG_VALIDATE_BUG_ON(state->curr_tx == NULL);
772-
if (state->curr_tx) {
773-
SMTPSetProgressTS(state->curr_tx, SMTP_REQUEST_COMPLETE);
774-
SMTPSetProgressTC(state->curr_tx, SMTP_RESPONSE_COMPLETE);
787+
DEBUG_VALIDATE_BUG_ON(tx == NULL);
788+
if (tx) {
789+
SMTPSetProgressTS(tx, SMTP_REQUEST_COMPLETE);
790+
SMTPSetProgressTC(tx, SMTP_RESPONSE_COMPLETE);
775791
}
776792
}
777793

778-
static inline void SMTPTransactionCompleteTS(SMTPState *state)
794+
static inline void SMTPTransactionCompleteTS(SMTPTransaction *tx)
779795
{
780-
DEBUG_VALIDATE_BUG_ON(state->curr_tx == NULL);
781-
if (state->curr_tx) {
782-
SMTPSetProgressTS(state->curr_tx, SMTP_REQUEST_COMPLETE);
796+
DEBUG_VALIDATE_BUG_ON(tx == NULL);
797+
if (tx) {
798+
SMTPSetProgressTS(tx, SMTP_REQUEST_COMPLETE);
783799
SCLogDebug("marked tx as ts complete");
784800
}
785801
}
786802

787-
static inline void SMTPTransactionCompleteTC(SMTPState *state)
803+
static inline void SMTPTransactionCompleteTC(SMTPTransaction *tx)
788804
{
789-
DEBUG_VALIDATE_BUG_ON(state->curr_tx == NULL);
790-
if (state->curr_tx) {
791-
SMTPSetProgressTC(state->curr_tx, SMTP_RESPONSE_COMPLETE);
805+
DEBUG_VALIDATE_BUG_ON(tx == NULL);
806+
if (tx) {
807+
SMTPSetProgressTC(tx, SMTP_RESPONSE_COMPLETE);
792808
SCLogDebug("marked tx as tc complete");
793809
}
794810
}
@@ -815,7 +831,7 @@ static int SMTPProcessCommandDATA(
815831
* acknowledged with a reply. We insert a dummy command to
816832
* the command buffer to be used by the reply handler to match
817833
* the reply received */
818-
SMTPInsertCommandIntoCommandBuffer(SMTP_COMMAND_DATA_MODE, state);
834+
SMTPInsertCommandIntoCommandBuffer(state, SMTP_COMMAND_DATA_MODE, tx);
819835
if (smtp_config.raw_extraction) {
820836
/* we use this as the signal that message data is complete. */
821837
FileCloseFile(&tx->files_ts, &smtp_config.sbcfg, NULL, 0, 0);
@@ -827,7 +843,7 @@ static int SMTPProcessCommandDATA(
827843
FileFlowToFlags(f, STREAM_TOSERVER));
828844
}
829845
}
830-
SMTPTransactionCompleteTS(state);
846+
SMTPTransactionCompleteTS(tx);
831847
} else if (smtp_config.raw_extraction) {
832848
// message not over, store the line. This is a substitution of
833849
// ProcessDataChunk
@@ -924,8 +940,31 @@ static int SMTPProcessCommandDATA(
924940

925941
static inline bool IsReplyToCommand(const SMTPState *state, const uint8_t cmd)
926942
{
927-
return (state->cmds_idx < state->cmds_buffer_len &&
928-
state->cmds[state->cmds_idx] == cmd);
943+
return (state->cmds_idx < state->cmds_cnt && state->cmds[state->cmds_idx] == cmd);
944+
}
945+
946+
static SMTPTransaction *SMTPStateGetTxById(SMTPState *state, uint64_t tx_id)
947+
{
948+
SMTPTransaction *tx = NULL;
949+
TAILQ_FOREACH (tx, &state->tx_list, next) {
950+
if (tx->tx_id == tx_id) {
951+
return tx;
952+
}
953+
if (tx->tx_id > tx_id) {
954+
break;
955+
}
956+
}
957+
return NULL;
958+
}
959+
960+
static SMTPTransaction *SMTPGetReplyTx(SMTPState *state)
961+
{
962+
if (state->cmds_idx >= state->cmds_cnt) {
963+
return NULL;
964+
}
965+
966+
SMTPTransaction *tx = SMTPStateGetTxById(state, state->cmds_tx_ids[state->cmds_idx]);
967+
return tx != NULL ? tx : state->curr_tx;
929968
}
930969

931970
static int SMTPProcessReply(
@@ -938,8 +977,12 @@ static int SMTPProcessReply(
938977
return 0; // to continue processing further
939978
}
940979

941-
if (state->curr_tx) {
942-
state->curr_tx->tx_data.updated_tc = true;
980+
SMTPTransaction *reply_tx = SMTPGetReplyTx(state);
981+
if (reply_tx == NULL) {
982+
reply_tx = state->curr_tx;
983+
}
984+
if (reply_tx != NULL) {
985+
reply_tx->tx_data.updated_tc = true;
943986
}
944987
/* the reply code has to contain at least 3 bytes, to hold the 3 digit
945988
* reply code */
@@ -1010,16 +1053,16 @@ static int SMTPProcessReply(
10101053
if (!SCAppLayerRequestProtocolTLSUpgrade(f)) {
10111054
SMTPSetEvent(state, SMTP_DECODER_EVENT_FAILED_PROTOCOL_CHANGE);
10121055
}
1013-
if (state->curr_tx) {
1014-
SMTPTransactionComplete(state);
1056+
if (reply_tx) {
1057+
SMTPTransactionComplete(reply_tx);
10151058
}
10161059
} else {
10171060
/* decoder event */
10181061
SMTPSetEvent(state, SMTP_DECODER_EVENT_TLS_REJECTED);
10191062
}
10201063
} else if (IsReplyToCommand(state, SMTP_COMMAND_DATA)) {
10211064
if (reply_code == SMTP_REPLY_354) {
1022-
SMTPSetProgressTC(state->curr_tx, SMTP_RESPONSE_DATA);
1065+
SMTPSetProgressTC(reply_tx, SMTP_RESPONSE_DATA);
10231066
/* Next comes the mail for the DATA command in toserver direction */
10241067
state->parser_state |= SMTP_PARSER_STATE_COMMAND_DATA_MODE;
10251068
} else {
@@ -1031,15 +1074,15 @@ static int SMTPProcessReply(
10311074
SMTPSetEvent(state, SMTP_DECODER_EVENT_DATA_COMMAND_REJECTED);
10321075
}
10331076
} else if (IsReplyToCommand(state, SMTP_COMMAND_BDAT)) {
1034-
SMTPSetProgressTC(state->curr_tx, SMTP_RESPONSE_DATA);
1077+
SMTPSetProgressTC(reply_tx, SMTP_RESPONSE_DATA);
10351078
} else if (IsReplyToCommand(state, SMTP_COMMAND_DATA_MODE)) {
10361079
if (!(state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY)) {
1037-
SMTPTransactionCompleteTC(state);
1080+
SMTPTransactionCompleteTC(reply_tx);
10381081
}
10391082
} else if (IsReplyToCommand(state, SMTP_COMMAND_RSET)) {
1040-
if (reply_code == SMTP_REPLY_250 && state->curr_tx &&
1083+
if (reply_code == SMTP_REPLY_250 && reply_tx &&
10411084
!(state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY)) {
1042-
SMTPTransactionComplete(state);
1085+
SMTPTransactionComplete(reply_tx);
10431086
}
10441087
} else {
10451088
/* we don't care for any other command for now */
@@ -1238,7 +1281,7 @@ static int SMTPProcessRequest(
12381281
return 0;
12391282
}
12401283
if (state->curr_tx == NULL ||
1241-
(SMTPTransactionIsComplete(state->curr_tx) && !NoNewTx(state, line))) {
1284+
(SMTPTransactionRequestIsComplete(state->curr_tx) && !NoNewTx(state, line))) {
12421285
tx = SMTPTransactionCreate(state);
12431286
if (tx == NULL)
12441287
return -1;
@@ -1344,7 +1387,7 @@ static int SMTPProcessRequest(
13441387

13451388
/* Every command is inserted into a command buffer, to be matched
13461389
* against reply(ies) sent by the server */
1347-
if (SMTPInsertCommandIntoCommandBuffer(state->current_command, state) == -1) {
1390+
if (SMTPInsertCommandIntoCommandBuffer(state, state->current_command, tx) == -1) {
13481391
SCReturnInt(-1);
13491392
}
13501393

@@ -1594,6 +1637,12 @@ void *SMTPStateAlloc(void *orig_state, AppProto proto_orig)
15941637
SCFree(smtp_state);
15951638
return NULL;
15961639
}
1640+
smtp_state->cmds_tx_ids = SCMalloc(sizeof(uint64_t) * SMTP_COMMAND_BUFFER_STEPS);
1641+
if (smtp_state->cmds_tx_ids == NULL) {
1642+
SCFree(smtp_state->cmds);
1643+
SCFree(smtp_state);
1644+
return NULL;
1645+
}
15971646
smtp_state->cmds_buffer_len = SMTP_COMMAND_BUFFER_STEPS;
15981647

15991648
TAILQ_INIT(&smtp_state->tx_list);
@@ -1691,6 +1740,9 @@ static void SMTPStateFree(void *p)
16911740
if (smtp_state->cmds != NULL) {
16921741
SCFree(smtp_state->cmds);
16931742
}
1743+
if (smtp_state->cmds_tx_ids != NULL) {
1744+
SCFree(smtp_state->cmds_tx_ids);
1745+
}
16941746

16951747
if (smtp_state->helo) {
16961748
SCFree(smtp_state->helo);
@@ -4341,6 +4393,7 @@ static int SMTPParserTest14(void)
43414393
StreamTcpFreeConfig(true);
43424394
return result;
43434395
}
4396+
43444397
#endif /* UNITTESTS */
43454398

43464399
void SMTPParserRegisterTests(void)

src/app-layer-smtp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ typedef struct SMTPState_ {
153153
* stored command in the buffer to match the reply(ies) with the command */
154154
/** the command buffer */
155155
uint8_t *cmds;
156+
/** tx id for each stored command */
157+
uint64_t *cmds_tx_ids;
156158
/** the buffer length */
157159
uint16_t cmds_buffer_len;
158160
/** no of commands stored in the above buffer */

0 commit comments

Comments
 (0)