From 4a6908d3e960b8f9b5d2c67e6135235a08b6f508 Mon Sep 17 00:00:00 2001 From: Anoop Saldanha Date: Sat, 19 Nov 2011 18:12:21 +0530 Subject: [PATCH] fix smtp parser handling fragmented lines + add new unittests to check the same --- src/app-layer-smtp.c | 642 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 563 insertions(+), 79 deletions(-) diff --git a/src/app-layer-smtp.c b/src/app-layer-smtp.c index 80b1a73515..c98fe18511 100644 --- a/src/app-layer-smtp.c +++ b/src/app-layer-smtp.c @@ -122,7 +122,9 @@ static int SMTPGetLine(SMTPState *state) state->ts_current_line_db = 0; SCFree(state->ts_db); state->ts_db = NULL; + state->ts_db_len = 0; state->current_line = NULL; + state->current_line_len = 0; } } @@ -131,11 +133,11 @@ static int SMTPGetLine(SMTPState *state) if (lf_idx == NULL) { /* set decoder event */ if (state->ts_current_line_db == 0) { - state->ts_current_line_db = 1; state->ts_db = SCMalloc(state->input_len); if (state->ts_db == NULL) { return -1; } + state->ts_current_line_db = 1; memcpy(state->ts_db, state->input, state->input_len); state->ts_db_len = state->input_len; } else { @@ -152,58 +154,51 @@ static int SMTPGetLine(SMTPState *state) state->input_len = 0; return -1; + } else { state->ts_current_line_lf_seen = 1; - /* We have CR-LF as the line delimiter */ - if (*(lf_idx - 1) == 0x0d) { - if (state->ts_current_line_db == 1) { - state->ts_db = SCRealloc(state->ts_db, - (state->ts_db_len + - (lf_idx - state->input - 1))); - if (state->ts_db == NULL) { - return -1; - } - memcpy(state->ts_db + state->ts_db_len, - state->input, (lf_idx - state->input - 1)); - state->ts_db_len += (lf_idx - state->input - 1); - - state->current_line = state->ts_db; - state->current_line_len = state->ts_db_len; + if (state->ts_current_line_db == 1) { + state->ts_db = SCRealloc(state->ts_db, + (state->ts_db_len + + (lf_idx + 1 - state->input))); + if (state->ts_db == NULL) { + return -1; + } + memcpy(state->ts_db + state->ts_db_len, + state->input, (lf_idx + 1 - state->input)); + state->ts_db_len += (lf_idx + 1 - state->input); + + if (state->ts_db_len > 1 && + state->ts_db[state->ts_db_len - 2] == 0x0D) { + state->ts_db_len -= 2; + state->current_line_delimiter_len = 2; } else { - state->current_line = state->input; - state->current_line_len = (lf_idx - state->input - 1); + state->ts_db_len -= 1; + state->current_line_delimiter_len = 1; } - state->current_line_delimiter_len = 2; - /* We have just LF as the line delimiter */ + state->current_line = state->ts_db; + state->current_line_len = state->ts_db_len; + } else { - if (state->ts_current_line_db == 1) { - state->ts_db = SCRealloc(state->ts_db, - (state->ts_db_len + - (lf_idx - state->input))); - if (state->ts_db == NULL) { - return -1; - } - memcpy(state->ts_db + state->ts_db_len, - state->input, (lf_idx - state->input)); - state->ts_db_len += (lf_idx - state->input); - - state->current_line = state->ts_db; - state->current_line_len = state->ts_db_len; + state->current_line = state->input; + state->current_line_len = lf_idx - state->input; + + if (state->input != lf_idx && + *(lf_idx - 1) == 0x0D) { + state->current_line_len--; + state->current_line_delimiter_len = 2; } else { - state->current_line = state->input; - state->current_line_len = (lf_idx - state->input); + state->current_line_delimiter_len = 1; } - - state->current_line_delimiter_len = 1; - } /* else */ + } state->input_len -= (lf_idx - state->input) + 1; - state->input = lf_idx + 1; + state->input = (lf_idx + 1); return 0; - } /* else - if (lf_idx == NULL) */ + } /* toclient */ } else { @@ -215,7 +210,9 @@ static int SMTPGetLine(SMTPState *state) state->tc_current_line_db = 0; SCFree(state->tc_db); state->tc_db = NULL; + state->tc_db_len = 0; state->current_line = NULL; + state->current_line_len = 0; } } @@ -224,11 +221,11 @@ static int SMTPGetLine(SMTPState *state) if (lf_idx == NULL) { /* set decoder event */ if (state->tc_current_line_db == 0) { - state->tc_current_line_db = 1; state->tc_db = SCMalloc(state->input_len); if (state->tc_db == NULL) { return -1; } + state->tc_current_line_db = 1; memcpy(state->tc_db, state->input, state->input_len); state->tc_db_len = state->input_len; } else { @@ -245,55 +242,48 @@ static int SMTPGetLine(SMTPState *state) state->input_len = 0; return -1; + } else { state->tc_current_line_lf_seen = 1; - /* We have CR-LF as the line delimiter */ - if (*(lf_idx - 1) == 0x0d) { - if (state->tc_current_line_db == 1) { - state->tc_db = SCRealloc(state->tc_db, - (state->tc_db_len + - (lf_idx - state->input - 1))); - if (state->tc_db == NULL) { - return -1; - } - memcpy(state->tc_db + state->tc_db_len, - state->input, (lf_idx - state->input - 1)); - state->tc_db_len += (lf_idx - state->input - 1); - - state->current_line = state->tc_db; - state->current_line_len = state->tc_db_len; + if (state->tc_current_line_db == 1) { + state->tc_db = SCRealloc(state->tc_db, + (state->tc_db_len + + (lf_idx + 1 - state->input))); + if (state->tc_db == NULL) { + return -1; + } + memcpy(state->tc_db + state->tc_db_len, + state->input, (lf_idx + 1 - state->input)); + state->tc_db_len += (lf_idx + 1 - state->input); + + if (state->tc_db_len > 1 && + state->tc_db[state->tc_db_len - 2] == 0x0D) { + state->tc_db_len -= 2; + state->current_line_delimiter_len = 2; } else { - state->current_line = state->input; - state->current_line_len = (lf_idx - state->input - 1); + state->tc_db_len -= 1; + state->current_line_delimiter_len = 1; } - state->current_line_delimiter_len = 2; - /* We have just LF as the line delimiter */ + state->current_line = state->tc_db; + state->current_line_len = state->tc_db_len; + } else { - if (state->tc_current_line_db == 1) { - state->tc_db = SCRealloc(state->tc_db, - (state->tc_db_len + - (lf_idx - state->input))); - if (state->tc_db == NULL) { - return -1; - } - memcpy(state->tc_db + state->tc_db_len, - state->input, (lf_idx - state->input)); - state->tc_db_len += (lf_idx - state->input); - - state->current_line = state->tc_db; - state->current_line_len = state->tc_db_len; + state->current_line = state->input; + state->current_line_len = lf_idx - state->input; + + if (state->input != lf_idx && + *(lf_idx - 1) == 0x0D) { + state->current_line_len--; + state->current_line_delimiter_len = 2; } else { - state->current_line = state->input; - state->current_line_len = (lf_idx - state->input); + state->current_line_delimiter_len = 1; } - - state->current_line_delimiter_len = 1; - } /* else */ + } state->input_len -= (lf_idx - state->input) + 1; - state->input = lf_idx + 1; + state->input = (lf_idx + 1); return 0; } /* else - if (lf_idx == NULL) */ @@ -2325,6 +2315,494 @@ end: return result; } +/* + * \test Test retrieving lines when frag'ed. + */ +int SMTPParserTest07(void) +{ + int result = 0; + Flow f; + int r = 0; + + const char *request1_str = "EHLO boo.com"; + /* EHLO boo.com */ + uint8_t request1_1[] = { + 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, + }; + int32_t request1_1_len = sizeof(request1_1); + + /* */ + uint8_t request1_2[] = { + 0x0a + }; + int32_t request1_2_len = sizeof(request1_2); + + /* EHLO boo.com */ + uint8_t request2[] = { + 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, + }; + int32_t request2_len = sizeof(request2); + + TcpSession ssn; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request1_1, request1_1_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + SMTPState *smtp_state = f.aldata[AlpGetStateIdx(ALPROTO_SMTP)]; + if (smtp_state == NULL) { + printf("no smtp state: "); + goto end; + } + if (smtp_state->current_line != NULL || + smtp_state->current_line_len != 0 || + smtp_state->ts_current_line_db != 1 || + smtp_state->ts_db == NULL || + smtp_state->ts_db_len != request1_1_len || + memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request1_2, request1_2_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + if (smtp_state->ts_current_line_db != 1 || + smtp_state->ts_db == NULL || + smtp_state->ts_db_len != (int32_t)strlen(request1_str) || + memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 || + smtp_state->current_line != smtp_state->ts_db || + smtp_state->current_line_len != smtp_state->ts_db_len) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request2, request2_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + if (smtp_state->ts_current_line_db != 0 || + smtp_state->ts_db != NULL || + smtp_state->ts_db_len != 0 || + smtp_state->current_line == NULL || + smtp_state->current_line_len != (int32_t)strlen(request1_str) || + memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + result = 1; +end: + FlowL7DataPtrFree(&f); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + return result; +} + +/* + * \test Test retrieving lines when frag'ed. + */ +int SMTPParserTest08(void) +{ + int result = 0; + Flow f; + int r = 0; + + const char *request1_str = "EHLO boo.com"; + /* EHLO boo.com */ + uint8_t request1_1[] = { + 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f, + 0x2e, 0x63, 0x6f, 0x6d, + }; + int32_t request1_1_len = sizeof(request1_1); + + /* */ + uint8_t request1_2[] = { + 0x0d, 0x0a + }; + int32_t request1_2_len = sizeof(request1_2); + + /* EHLO boo.com */ + uint8_t request2[] = { + 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, + }; + int32_t request2_len = sizeof(request2); + + TcpSession ssn; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request1_1, request1_1_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + SMTPState *smtp_state = f.aldata[AlpGetStateIdx(ALPROTO_SMTP)]; + if (smtp_state == NULL) { + printf("no smtp state: "); + goto end; + } + if (smtp_state->current_line != NULL || + smtp_state->current_line_len != 0 || + smtp_state->ts_current_line_db != 1 || + smtp_state->ts_db == NULL || + smtp_state->ts_db_len != request1_1_len || + memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request1_2, request1_2_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + if (smtp_state->ts_current_line_db != 1 || + smtp_state->ts_db == NULL || + smtp_state->ts_db_len != (int32_t)strlen(request1_str) || + memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 || + smtp_state->current_line != smtp_state->ts_db || + smtp_state->current_line_len != smtp_state->ts_db_len) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request2, request2_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + if (smtp_state->ts_current_line_db != 0 || + smtp_state->ts_db != NULL || + smtp_state->ts_db_len != 0 || + smtp_state->current_line == NULL || + smtp_state->current_line_len != (int32_t)strlen(request1_str) || + memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + result = 1; +end: + FlowL7DataPtrFree(&f); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + return result; +} + +/* + * \test Test retrieving lines when frag'ed. + */ +int SMTPParserTest09(void) +{ + int result = 0; + Flow f; + int r = 0; + + const char *request1_str = "EHLO boo.com"; + /* EHLO boo. */ + uint8_t request1_1[] = { + 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f, + 0x2e, + }; + int32_t request1_1_len = sizeof(request1_1); + + /* com */ + uint8_t request1_2[] = { + 0x63, 0x6f, 0x6d, 0x0d, 0x0a + }; + int32_t request1_2_len = sizeof(request1_2); + + /* EHLO boo.com */ + uint8_t request2[] = { + 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, + }; + int32_t request2_len = sizeof(request2); + + TcpSession ssn; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request1_1, request1_1_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + SMTPState *smtp_state = f.aldata[AlpGetStateIdx(ALPROTO_SMTP)]; + if (smtp_state == NULL) { + printf("no smtp state: "); + goto end; + } + if (smtp_state->current_line != NULL || + smtp_state->current_line_len != 0 || + smtp_state->ts_current_line_db != 1 || + smtp_state->ts_db == NULL || + smtp_state->ts_db_len != request1_1_len || + memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request1_2, request1_2_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + if (smtp_state->ts_current_line_db != 1 || + smtp_state->ts_db == NULL || + smtp_state->ts_db_len != (int32_t)strlen(request1_str) || + memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 || + smtp_state->current_line != smtp_state->ts_db || + smtp_state->current_line_len != smtp_state->ts_db_len) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request2, request2_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + if (smtp_state->ts_current_line_db != 0 || + smtp_state->ts_db != NULL || + smtp_state->ts_db_len != 0 || + smtp_state->current_line == NULL || + smtp_state->current_line_len != (int32_t)strlen(request1_str) || + memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + result = 1; +end: + FlowL7DataPtrFree(&f); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + return result; +} + +/* + * \test Test retrieving lines when frag'ed. + */ +int SMTPParserTest10(void) +{ + int result = 0; + Flow f; + int r = 0; + + const char *request1_str = ""; + /* EHLO boo. */ + uint8_t request1_1[] = { + 0x0d, + }; + int32_t request1_1_len = sizeof(request1_1); + + /* com */ + uint8_t request1_2[] = { + 0x0a, + }; + int32_t request1_2_len = sizeof(request1_2); + + const char *request2_str = "EHLO boo.com"; + /* EHLO boo.com */ + uint8_t request2[] = { + 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, + }; + int32_t request2_len = sizeof(request2); + + TcpSession ssn; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request1_1, request1_1_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + SMTPState *smtp_state = f.aldata[AlpGetStateIdx(ALPROTO_SMTP)]; + if (smtp_state == NULL) { + printf("no smtp state: "); + goto end; + } + if (smtp_state->current_line != NULL || + smtp_state->current_line_len != 0 || + smtp_state->ts_current_line_db != 1 || + smtp_state->ts_db == NULL || + smtp_state->ts_db_len != request1_1_len || + memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request1_2, request1_2_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + if (smtp_state->ts_current_line_db != 1 || + smtp_state->ts_db == NULL || + smtp_state->ts_db_len != (int32_t)strlen(request1_str) || + memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 || + smtp_state->current_line != smtp_state->ts_db || + smtp_state->current_line_len != smtp_state->ts_db_len) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request2, request2_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + if (smtp_state->ts_current_line_db != 0 || + smtp_state->ts_db != NULL || + smtp_state->ts_db_len != 0 || + smtp_state->current_line == NULL || + smtp_state->current_line_len != (int32_t)strlen(request2_str) || + memcmp(smtp_state->current_line, request2_str, strlen(request2_str)) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + result = 1; +end: + FlowL7DataPtrFree(&f); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + return result; +} + +/* + * \test Test retrieving lines when frag'ed. + */ +int SMTPParserTest11(void) +{ + int result = 0; + Flow f; + int r = 0; + + const char *request1_str = ""; + /* EHLO boo. */ + uint8_t request1[] = { + 0x0a, + }; + int32_t request1_len = sizeof(request1); + + const char *request2_str = "EHLO boo.com"; + /* EHLO boo.com */ + uint8_t request2[] = { + 0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, + }; + int32_t request2_len = sizeof(request2); + + TcpSession ssn; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request1, request1_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + SMTPState *smtp_state = f.aldata[AlpGetStateIdx(ALPROTO_SMTP)]; + if (smtp_state == NULL) { + printf("no smtp state: "); + goto end; + } + if (smtp_state->current_line == NULL || + smtp_state->current_line_len != 0 || + smtp_state->ts_current_line_db == 1 || + smtp_state->ts_db != NULL || + smtp_state->ts_db_len != 0 || + memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_SMTP, STREAM_TOSERVER, + request2, request2_len); + if (r != 0) { + printf("smtp check returned %" PRId32 ", expected 0: ", r); + goto end; + } + if (smtp_state->ts_current_line_db != 0 || + smtp_state->ts_db != NULL || + smtp_state->ts_db_len != 0 || + smtp_state->current_line == NULL || + smtp_state->current_line_len != (int32_t)strlen(request2_str) || + memcmp(smtp_state->current_line, request2_str, strlen(request2_str)) != 0) { + printf("smtp parser in inconsistent state\n"); + goto end; + } + + result = 1; +end: + FlowL7DataPtrFree(&f); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + return result; +} + void SMTPParserRegisterTests(void) { UtRegisterTest("SMTPParserTest01", SMTPParserTest01, 1); @@ -2333,5 +2811,11 @@ void SMTPParserRegisterTests(void) UtRegisterTest("SMTPParserTest04", SMTPParserTest04, 1); UtRegisterTest("SMTPParserTest05", SMTPParserTest05, 1); UtRegisterTest("SMTPParserTest06", SMTPParserTest06, 1); + UtRegisterTest("SMTPParserTest07", SMTPParserTest07, 1); + UtRegisterTest("SMTPParserTest08", SMTPParserTest08, 1); + UtRegisterTest("SMTPParserTest09", SMTPParserTest09, 1); + UtRegisterTest("SMTPParserTest10", SMTPParserTest10, 1); + UtRegisterTest("SMTPParserTest11", SMTPParserTest11, 1); + return; }