From: Aki Tuomi Date: Thu, 2 Sep 2021 14:10:11 +0000 (+0300) Subject: lib-mail: Limit header length to 1000 bytes X-Git-Tag: 2.3.17~41 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d0e0b22b6b0c18bd2a4e10596814a48613d0330c;p=thirdparty%2Fdovecot%2Fcore.git lib-mail: Limit header length to 1000 bytes --- diff --git a/src/lib-mail/message-header-parser.c b/src/lib-mail/message-header-parser.c index 80210d2269..c5026f1bb7 100644 --- a/src/lib-mail/message-header-parser.c +++ b/src/lib-mail/message-header-parser.c @@ -9,6 +9,9 @@ #include "message-size.h" #include "message-header-parser.h" +/* RFC 5322 2.1.1 and 2.2 */ +#define MESSAGE_HEADER_NAME_MAX_LEN 1000 + struct message_header_parser_ctx { struct message_header_line line; @@ -309,21 +312,34 @@ int message_parse_header_next(struct message_header_parser_ctx *ctx, while (colon_pos > 0 && IS_LWSP(msg[colon_pos-1])) colon_pos--; - str_truncate(ctx->name, 0); - /* use buffer_append() so the name won't be truncated if there - are NULs. */ - buffer_append(ctx->name, msg, colon_pos); - str_append_c(ctx->name, '\0'); - - /* keep middle stored also in ctx->name so it's available - with use_full_value */ - line->middle = msg + colon_pos; - line->middle_len = (size_t)(line->value - line->middle); - str_append_data(ctx->name, line->middle, line->middle_len); - - line->name = str_c(ctx->name); - line->name_len = colon_pos; - line->middle = str_data(ctx->name) + line->name_len + 1; + /* Treat overlong header names as if the full header line was + a value. Callers can usually handle large values better than + large names. */ + if (colon_pos > MESSAGE_HEADER_NAME_MAX_LEN) { + line->name = ""; + line->name_len = 0; + line->middle = uchar_empty_ptr; + line->middle_len = 0; + line->value = msg; + line->value_len = size; + line->full_value_offset = line->name_offset; + } else { + str_truncate(ctx->name, 0); + /* use buffer_append() so the name won't be truncated if there + are NULs. */ + buffer_append(ctx->name, msg, colon_pos); + str_append_c(ctx->name, '\0'); + + /* keep middle stored also in ctx->name so it's available + with use_full_value */ + line->middle = msg + colon_pos; + line->middle_len = (size_t)(line->value - line->middle); + str_append_data(ctx->name, line->middle, line->middle_len); + + line->name = str_c(ctx->name); + line->name_len = colon_pos; + line->middle = str_data(ctx->name) + line->name_len + 1; + } } if (!line->continued) { diff --git a/src/lib-mail/test-message-header-parser.c b/src/lib-mail/test-message-header-parser.c index 58cb7f5577..0b3ffc828a 100644 --- a/src/lib-mail/test-message-header-parser.c +++ b/src/lib-mail/test-message-header-parser.c @@ -2,6 +2,7 @@ #include "lib.h" #include "str.h" +#include "strfuncs.h" #include "unichar.h" #include "istream.h" #include "message-size.h" @@ -245,6 +246,13 @@ static void test_message_header_parser_long_lines(void) { static const char *lf_str = NAME10": 345\n\n"; static const char *crlf_str = NAME10": 345\r\n\r\n"; + static const char *lf_str_vl = NAME1000": Is a long header name\n\n"; + static const char *crlf_str_vl = NAME1000": Is a long header name\r\n\r\n"; + static const char *lf_str_ol = NAME1000 \ + NAME100 ": Is a overlong header name\n\n"; + static const char *crlf_str_ol = NAME1000 \ + NAME100 ": Is a overlong header name\r\n\r\n"; + struct message_size hdr_size, hdr_size2; size_t i, len; @@ -265,6 +273,82 @@ static void test_message_header_parser_long_lines(void) test_assert(hdr_size.virtual_size == hdr_size2.virtual_size); test_assert(hdr_size.physical_size == hdr_size2.physical_size); } + + /* increment these faster, otherwise the test is very slow */ + len = strlen(lf_str_vl); + for (i = 3; i < len; i *= 2) { + test_message_header_parser_long_lines_str(lf_str_vl, i, &hdr_size, &hdr_size2); + test_assert(hdr_size.physical_size == len); + test_assert(hdr_size.virtual_size == len + 2); + test_assert(hdr_size.virtual_size == hdr_size2.virtual_size); + test_assert(hdr_size.physical_size == hdr_size2.physical_size); + } + len = strlen(crlf_str_vl); + for (i = 3; i < len; i *= 2) { + test_message_header_parser_long_lines_str(crlf_str_vl, i, &hdr_size, &hdr_size2); + test_assert(hdr_size.physical_size == len); + test_assert(hdr_size.virtual_size == len); + test_assert(hdr_size.virtual_size == hdr_size2.virtual_size); + test_assert(hdr_size.physical_size == hdr_size2.physical_size); + } + + /* test that parsing overlength lines work so that name & middle are + empty. */ + + struct message_header_line *hdr; + struct message_header_parser_ctx *ctx; + struct istream *input; + + input = test_istream_create(lf_str_ol); + ctx = message_parse_header_init(input, NULL, 0); + + test_assert(message_parse_header_next(ctx, &hdr) > 0 && + *hdr->name == '\0' && hdr->middle == uchar_empty_ptr && + hdr->name_len == 0 && hdr->middle_len == 0 && + hdr->value != NULL && hdr->value_len > 0); + test_assert(message_parse_header_next(ctx, &hdr) > 0 && + hdr->eoh); + message_parse_header_deinit(&ctx); + i_stream_unref(&input); + + input = test_istream_create(crlf_str_ol); + ctx = message_parse_header_init(input, NULL, 0); + test_assert(message_parse_header_next(ctx, &hdr) > 0 && + *hdr->name == '\0' && hdr->middle == uchar_empty_ptr && + hdr->name_len == 0 && hdr->middle_len == 0 && + hdr->value != NULL && hdr->value_len > 0); + test_assert(message_parse_header_next(ctx, &hdr) > 0 && + hdr->eoh); + message_parse_header_deinit(&ctx); + i_stream_unref(&input); + + /* test offset parsing */ + static const char *data = "h1" NAME1000 NAME100 \ + ": value1\r\n" \ + "h2" NAME1000 NAME100 \ + ": value2\r\n" \ + "h3" NAME1000 NAME100 \ + ": value3\r\n\r\n"; + input = test_istream_create(data); + ctx = message_parse_header_init(input, NULL, 0); + test_assert(message_parse_header_next(ctx, &hdr) > 0 && + hdr->full_value[0] == 'h' && + hdr->full_value[1] == '1' && + hdr->full_value_offset == 0); + test_assert(message_parse_header_next(ctx, &hdr) > 0 && + hdr->full_value[0] == 'h' && + hdr->full_value[1] == '2' && + hdr->full_value_offset == 1112); + test_assert(message_parse_header_next(ctx, &hdr) > 0 && + hdr->full_value[0] == 'h' && + hdr->full_value[1] == '3' && + hdr->full_value_offset == 2224); + test_assert(message_parse_header_next(ctx, &hdr) > 0 && + hdr->eoh); + + message_parse_header_deinit(&ctx); + i_stream_unref(&input); + test_end(); } @@ -340,6 +424,35 @@ static void test_message_header_parser_nul(void) test_end(); } +static void test_message_header_parser_extra_crlf_in_name(void) +{ + static const unsigned char str[] = "X-Header\r\n Name: Header Value\n\n"; + struct message_header_parser_ctx *parser; + struct message_header_line *hdr; + struct istream *input; + test_begin("message header parser CRLF in header name"); + + input = test_istream_create_data(str, sizeof(str)-1); + parser = message_parse_header_init(input, NULL, 0); + hdr = NULL; + test_assert(message_parse_header_next(parser, &hdr) > 0 && + *hdr->name == '\0' && hdr->middle == uchar_empty_ptr && + hdr->name_len == 0 && hdr->middle_len == 0 && + hdr->value != NULL && hdr->value_len > 0); + test_assert(message_parse_header_next(parser, &hdr) > 0 && + *hdr->name == '\0' && hdr->middle == uchar_empty_ptr && + hdr->name_len == 0 && hdr->middle_len == 0 && + hdr->value != NULL && hdr->value_len > 0 && + hdr->continued); + test_assert(message_parse_header_next(parser, &hdr) > 0 && + hdr->eoh); + + message_parse_header_deinit(&parser); + i_stream_unref(&input); + + test_end(); +} + int main(void) { static void (*const test_functions[])(void) = { @@ -349,6 +462,7 @@ int main(void) test_message_header_parser_extra_cr_in_eoh, test_message_header_parser_no_eoh, test_message_header_parser_nul, + test_message_header_parser_extra_crlf_in_name, NULL }; return test_run(test_functions);