From: Aki Tuomi Date: Mon, 16 Nov 2020 14:20:30 +0000 (+0200) Subject: lib-mail: rfc822-parser - Fix content-type parser to accept only valid values X-Git-Tag: 2.3.14.rc1~301 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e29c697e03a6b16906149b5e7bb0135b165c6a2b;p=thirdparty%2Fdovecot%2Fcore.git lib-mail: rfc822-parser - Fix content-type parser to accept only valid values --- diff --git a/src/lib-mail/rfc822-parser.c b/src/lib-mail/rfc822-parser.c index f87866f442..c8595b4187 100644 --- a/src/lib-mail/rfc822-parser.c +++ b/src/lib-mail/rfc822-parser.c @@ -434,23 +434,39 @@ int rfc822_parse_domain(struct rfc822_parser_context *ctx, string_t *str) int rfc822_parse_content_type(struct rfc822_parser_context *ctx, string_t *str) { + size_t str_pos_0 = str->used; if (rfc822_skip_lwsp(ctx) <= 0) return -1; - /* get main type */ - if (rfc822_parse_mime_token(ctx, str) <= 0) + /* get main type, require at least one byte */ + if (rfc822_parse_mime_token(ctx, str) <= 0 || + str->used == str_pos_0) return -1; /* skip over "/" */ - if (*ctx->data != '/') + if (*ctx->data != '/') { + str_truncate(str, str_pos_0); return -1; + } ctx->data++; - if (rfc822_skip_lwsp(ctx) <= 0) + if (rfc822_skip_lwsp(ctx) <= 0) { + str_truncate(str, str_pos_0); return -1; + } str_append_c(str, '/'); - /* get subtype */ - return rfc822_parse_mime_token(ctx, str); + size_t str_pos = str->used; + /* get subtype, require at least one byte, + and check the next separator to avoid accepting + invalid values. */ + int ret; + if ((ret = rfc822_parse_mime_token(ctx, str)) < 0 || + str->used == str_pos || + (ctx->data != ctx->end && *ctx->data != ';')) { + str_truncate(str, str_pos_0); + return -1; + } + return ret; } int rfc822_parse_content_param(struct rfc822_parser_context *ctx, diff --git a/src/lib-mail/test-rfc822-parser.c b/src/lib-mail/test-rfc822-parser.c index 55018b99aa..e5cf405f69 100644 --- a/src/lib-mail/test-rfc822-parser.c +++ b/src/lib-mail/test-rfc822-parser.c @@ -198,6 +198,98 @@ static void test_rfc822_parse_domain_literal(void) test_end(); } +#undef TEST_STRING +#define TEST_STRING(a) .input = (const unsigned char*)a, .input_len = sizeof(a)-1 + +static void test_rfc822_parse_content_type(void) +{ + const struct { + const unsigned char *input; + size_t input_len; + int ret; + const char *output; + } test_cases[] = { + { TEST_STRING(""), -1, "" }, + { TEST_STRING(";charset=us-ascii"), -1, "" }, + { TEST_STRING(" ;charset=us-ascii"), -1, "" }, + { TEST_STRING("/"), -1, "" }, + { TEST_STRING("/;charset=us-ascii"), -1, "" }, + { TEST_STRING("/ ;charset=us-ascii"), -1, "" }, + { TEST_STRING("text/"), -1, "" }, + { TEST_STRING("text/;charset=us-ascii"), -1, "" }, + { TEST_STRING("text/ ;charset=us-ascii"), -1, "" }, + { TEST_STRING("/plain"), -1, "" }, + { TEST_STRING("/plain;charset=us-ascii"), -1, "" }, + { TEST_STRING("/plain ;charset=us-ascii"), -1, "" }, + { TEST_STRING("text/plain"), 0, "text/plain" }, + { TEST_STRING("text/plain;charset=us-ascii"), 1, "text/plain" }, + { TEST_STRING("text/plain ;charset=us-ascii"), 1, "text/plain" }, + { TEST_STRING("text/plain/format"), -1, "" }, + { TEST_STRING("text/plain/format;charset=us-ascii"), -1, "" }, + { TEST_STRING("text/plain/format ;charset=us-ascii"), -1, "" }, + { TEST_STRING("\xe5\x90\xab\xe9\x87\x8f/\xe7\xa8\xae\xe9\xa1\x9e"), + 0, "\xe5\x90\xab\xe9\x87\x8f/\xe7\xa8\xae\xe9\xa1\x9e" }, + { TEST_STRING("\xe5\x90\xab\xe9\x87\x8f/\xe7\xa8\xae\xe9\xa1\x9e;charset=utf-8"), + 1, "\xe5\x90\xab\xe9\x87\x8f/\xe7\xa8\xae\xe9\xa1\x9e" }, + { TEST_STRING("\xe5\x90\xab\xe9\x87\x8f/\xe7\xa8\xae\xe9\xa1\x9e ;charset=utf-8"), + 1, "\xe5\x90\xab\xe9\x87\x8f/\xe7\xa8\xae\xe9\xa1\x9e" }, + { TEST_STRING("application/ld+json"), 0, "application/ld+json" }, + { TEST_STRING("application/ld+json;charset=us-ascii"), + 1, "application/ld+json" }, + { TEST_STRING("application/ld+json ;charset=us-ascii"), + 1, "application/ld+json" }, + { TEST_STRING("application/x-magic-cap-package-1.0"), + 0, "application/x-magic-cap-package-1.0" }, + { TEST_STRING("application/x-magic-cap-package-1.0;charset=us-ascii"), + 1, "application/x-magic-cap-package-1.0" }, + { TEST_STRING("application/x-magic-cap-package-1.0 ;charset=us-ascii"), + 1, "application/x-magic-cap-package-1.0" }, + { TEST_STRING("application/pro_eng"), 0, "application/pro_eng" }, + { TEST_STRING("application/pro_eng;charset=us-ascii"), + 1, "application/pro_eng" }, + { TEST_STRING("application/pro_eng ;charset=us-ascii"), + 1, "application/pro_eng" }, + { TEST_STRING("application/wordperfect6.1"), + 0, "application/wordperfect6.1" }, + { TEST_STRING("application/wordperfect6.1;charset=us-ascii"), + 1, "application/wordperfect6.1" }, + { TEST_STRING("application/wordperfect6.1 ;charset=us-ascii"), + 1, "application/wordperfect6.1" }, + { TEST_STRING("application/vnd.openxmlformats-officedocument.wordprocessingml.template"), + 0, "application/vnd.openxmlformats-officedocument.wordprocessingml.template" }, + { TEST_STRING("application/vnd.openxmlformats-officedocument.wordprocessingml.template;charset=us-ascii"), + 1, "application/vnd.openxmlformats-officedocument.wordprocessingml.template" }, + { TEST_STRING("application/vnd.openxmlformats-officedocument.wordprocessingml.template ;charset=us-asii"), + 1, "application/vnd.openxmlformats-officedocument.wordprocessingml.template" }, + { TEST_STRING("(hello) text (plain) / (world) plain (eod)"), + 0, "text/plain" }, + { TEST_STRING("(hello) text (plain) / (world) plain (eod);charset=us-ascii"), + 1, "text/plain" }, + { TEST_STRING("(hello) text (plain) / (world) plain (eod); charset=us-ascii"), + 1, "text/plain" }, + { TEST_STRING("message/rfc822\r\n"), 0, "message/rfc822" }, + { TEST_STRING(" \t\r message/rfc822 \t\r\n"), + 0, "message/rfc822" }, + { TEST_STRING(" \t\r message/rfc822 \t ;charset=us-ascii\r\n"), + 1, "message/rfc822" }, + { TEST_STRING(" \t\r message/rfc822 \t ; charset=us-ascii\r\n"), + 1, "message/rfc822" }, + { TEST_STRING("test\0/ty\0pe"), -1, "" }, + }; + + for (size_t i = 0; i < N_ELEMENTS(test_cases); i++) T_BEGIN { + string_t *value = t_str_new(64); + struct rfc822_parser_context parser; + + rfc822_parser_init(&parser, test_cases[i].input, + test_cases[i].input_len, NULL); + test_assert_idx(rfc822_parse_content_type(&parser, value) == + test_cases[i].ret, i); + test_assert_strcmp_idx(test_cases[i].output, str_c(value), i); + rfc822_parser_deinit(&parser); + } T_END; +} + static void test_rfc822_parse_content_param(void) { const char *input = @@ -237,6 +329,7 @@ int main(void) test_rfc822_parse_quoted_string, test_rfc822_parse_dot_atom, test_rfc822_parse_domain_literal, + test_rfc822_parse_content_type, test_rfc822_parse_content_param, NULL };