From a228892f677978b06ffad8b1963a093b9aeb2413 Mon Sep 17 00:00:00 2001 From: zhangxianting Date: Thu, 4 Jul 2024 22:11:52 +0800 Subject: [PATCH] Fix CVE-2024-39929 --- ...-specified-using-multiple-parameters.patch | 159 +++++++++++ ...g3099-MIME-support-RFC-2331-for-name.patch | 266 ++++++++++++++++++ exim.spec | 8 +- 3 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 backport-bug3099-Fix-MIME-parsing-of-filenames-specified-using-multiple-parameters.patch create mode 100644 backport-bug3099-MIME-support-RFC-2331-for-name.patch diff --git a/backport-bug3099-Fix-MIME-parsing-of-filenames-specified-using-multiple-parameters.patch b/backport-bug3099-Fix-MIME-parsing-of-filenames-specified-using-multiple-parameters.patch new file mode 100644 index 0000000..d6d71cf --- /dev/null +++ b/backport-bug3099-Fix-MIME-parsing-of-filenames-specified-using-multiple-parameters.patch @@ -0,0 +1,159 @@ +From 6ce5c70cff8989418e05d01fd2a57703007a6357 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Mon, 1 Jul 2024 19:35:12 +0100 +Subject: [PATCH] Fix MIME parsing of filenames specified using multiple + parameters. Bug 3099 + +--- + doc/ChangeLog | 3 +++ + doc/spec.txt | 10 +++++----- + src/mime.c | 51 +++++++++++++++++++++++++++++---------------------- + 3 files changed, 37 insertions(+), 27 deletions(-) + +diff --git a/doc/ChangeLog b/doc/ChangeLog +index 3e6da91..095bdb4 100644 +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -148,6 +148,9 @@ JH/32 Fix CHUNKING for a second message on a connection when the first was + JH/33 Fis ${srs_encode ...} to handle an empty sender address, now returning + an empty address. Previously the expansion returned an error. + ++JH/34 Bug 3099: fix parsing of MIME filenames split over multiple paramemters. ++ Previously the $mime_filename variable would have an incorrect value. ++ + HS/01 Bug 2855: Handle a v4mapped sender address given us by a frontending + proxy. Previously these were misparsed, leading to paniclog entries. + +diff --git a/doc/spec.txt b/doc/spec.txt +index ddadcaf..9128166 100644 +--- a/doc/spec.txt ++++ b/doc/spec.txt +@@ -32165,13 +32165,13 @@ The right hand side is expanded before use. After expansion, the value can be: + the default path is then used. + + The decode condition normally succeeds. It is only false for syntax errors or +-unusual circumstances such as memory shortages. You can easily decode a file +-with its original, proposed filename using ++errors or unusual circumstances such as memory shortages. + +-decode = $mime_filename ++The variable &$mime_filename$& will have the suggested name for the file. ++Note however that this might contain anything, and is very difficult ++to safely use as all or even part of the filename. + +-However, you should keep in mind that $mime_filename might contain anything. If +-you place files outside of the default path, they are not automatically ++If you place files outside of the default path, they are not + unlinked. + + For RFC822 attachments (these are messages attached to messages, with a +diff --git a/src/mime.c b/src/mime.c +index c192199..e7af413 100644 +--- a/src/mime.c ++++ b/src/mime.c +@@ -586,10 +586,10 @@ while(1) + + while (*p) + { +- DEBUG(D_acl) debug_printf_indent("MIME: considering paramlist '%s'\n", p); ++ DEBUG(D_acl) ++ debug_printf_indent("MIME: considering paramlist '%s'\n", p); + +- if ( !mime_filename +- && strncmpic(CUS"content-disposition:", header, 20) == 0 ++ if ( strncmpic(CUS"content-disposition:", header, 20) == 0 + && strncmpic(CUS"filename*", p, 9) == 0 + ) + { /* RFC 2231 filename */ +@@ -603,11 +603,12 @@ while(1) + + if (q && *q) + { +- uschar * temp_string, * err_msg; ++ uschar * temp_string, * err_msg, * fname = q; + int slen; + + /* build up an un-decoded filename over successive + filename*= parameters (for use when 2047 decode fails) */ ++/*XXX could grow a gstring here */ + + mime_fname_rfc2231 = string_sprintf("%#s%s", + mime_fname_rfc2231, q); +@@ -622,26 +623,32 @@ while(1) + /* look for a ' in the "filename" */ + while(*s != '\'' && *s) s++; /* s is 1st ' or NUL */ + +- if ((size = s-q) > 0) +- mime_filename_charset = string_copyn(q, size); ++ if (*s) /* there was a ' */ ++ { ++ if ((size = s-q) > 0) ++ mime_filename_charset = string_copyn(q, size); + +- if (*(p = s)) p++; +- while(*p == '\'') p++; /* p is after 2nd ' */ ++ if (*(fname = s)) fname++; ++ while(*fname == '\'') fname++; /* fname is after 2nd ' */ ++ } + } +- else +- p = q; + +- DEBUG(D_acl) debug_printf_indent("MIME: charset %s fname '%s'\n", +- mime_filename_charset ? mime_filename_charset : US"", p); ++ DEBUG(D_acl) ++ debug_printf_indent("MIME: charset %s fname '%s'\n", ++ mime_filename_charset ? mime_filename_charset : US"", ++ fname); + +- temp_string = rfc2231_to_2047(p, mime_filename_charset, &slen); +- DEBUG(D_acl) debug_printf_indent("MIME: 2047-name %s\n", temp_string); ++ temp_string = rfc2231_to_2047(fname, mime_filename_charset, ++ &slen); ++ DEBUG(D_acl) ++ debug_printf_indent("MIME: 2047-name %s\n", temp_string); + + temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ', +- NULL, &err_msg); +- DEBUG(D_acl) debug_printf_indent("MIME: plain-name %s\n", temp_string); ++ NULL, &err_msg); ++ DEBUG(D_acl) ++ debug_printf_indent("MIME: plain-name %s\n", temp_string); + +- if (!temp_string || (size = Ustrlen(temp_string)) == slen) ++ if (!temp_string || (size = Ustrlen(temp_string)) == slen) + decoding_failed = TRUE; + else + /* build up a decoded filename over successive +@@ -650,9 +657,9 @@ while(1) + mime_filename = mime_fname = mime_fname + ? string_sprintf("%s%s", mime_fname, temp_string) + : temp_string; +- } +- } +- } ++ } /*!decoding_failed*/ ++ } /*q*/ ++ } /*2231 filename*/ + + else + /* look for interesting parameters */ +@@ -681,7 +688,7 @@ while(1) + + + /* There is something, but not one of our interesting parameters. +- Advance past the next semicolon */ ++ Advance past the next semicolon */ + p = mime_next_semicolon(p); + if (*p) p++; + } /* param scan on line */ +@@ -799,5 +806,5 @@ return rc; + + #endif /*WITH_CONTENT_SCAN*/ + +-/* vi: sw ai sw=2 ++/* vi: aw ai sw=2 + */ +-- +2.33.0 + diff --git a/backport-bug3099-MIME-support-RFC-2331-for-name.patch b/backport-bug3099-MIME-support-RFC-2331-for-name.patch new file mode 100644 index 0000000..b92a499 --- /dev/null +++ b/backport-bug3099-MIME-support-RFC-2331-for-name.patch @@ -0,0 +1,266 @@ +From 1b3209b0577a9327ebb076f3b32b8a159c253f7b Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Tue, 2 Jul 2024 14:41:19 +0100 +Subject: [PATCH] MIME: support RFC 2331 for name=. Bug 3099 + +--- + doc/ChangeLog | 2 + + src/mime.c | 188 ++++++++++++++++++++++++++------------------------ + 2 files changed, 100 insertions(+), 90 deletions(-) + +diff --git a/doc/ChangeLog b/doc/ChangeLog +index 095bdb4..c1f0499 100644 +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -150,6 +150,8 @@ JH/33 Fis ${srs_encode ...} to handle an empty sender address, now returning + + JH/34 Bug 3099: fix parsing of MIME filenames split over multiple paramemters. + Previously the $mime_filename variable would have an incorrect value. ++ While in the code, extend coverage to name= which previously was only ++ supported for single parameters, despite also filling in $mime_filename. + + HS/01 Bug 2855: Handle a v4mapped sender address given us by a frontending + proxy. Previously these were misparsed, leading to paniclog entries. +diff --git a/src/mime.c b/src/mime.c +index e7af413..f36823f 100644 +--- a/src/mime.c ++++ b/src/mime.c +@@ -29,10 +29,10 @@ static int mime_header_list_size = nelem(mime_header_list); + + static mime_parameter mime_parameter_list[] = { + /* name namelen value */ +- { US"name=", 5, &mime_filename }, +- { US"filename=", 9, &mime_filename }, +- { US"charset=", 8, &mime_charset }, +- { US"boundary=", 9, &mime_boundary } ++ { US"name", 4, &mime_filename }, ++ { US"filename", 8, &mime_filename }, ++ { US"charset", 7, &mime_charset }, ++ { US"boundary", 8, &mime_boundary } + }; + + +@@ -576,8 +576,8 @@ while(1) + if (*(p = q)) p++; /* jump past the ; */ + + { +- uschar * mime_fname = NULL; +- uschar * mime_fname_rfc2231 = NULL; ++ gstring * mime_fname = NULL; ++ gstring * mime_fname_rfc2231 = NULL; + uschar * mime_filename_charset = NULL; + BOOL decoding_failed = FALSE; + +@@ -589,90 +589,92 @@ while(1) + DEBUG(D_acl) + debug_printf_indent("MIME: considering paramlist '%s'\n", p); + +- if ( strncmpic(CUS"content-disposition:", header, 20) == 0 +- && strncmpic(CUS"filename*", p, 9) == 0 +- ) +- { /* RFC 2231 filename */ +- uschar * q; +- +- /* find value of the filename */ +- p += 9; +- while(*p != '=' && *p) p++; +- if (*p) p++; /* p is filename or NUL */ +- q = mime_param_val(&p); /* p now trailing ; or NUL */ +- +- if (q && *q) ++ /* look for interesting parameters */ ++ for (mime_parameter * mp = mime_parameter_list; ++ mp < mime_parameter_list + nelem(mime_parameter_list); ++ mp++ ++ ) if (strncmpic(mp->name, p, mp->namelen) == 0) ++ { ++ p += mp->namelen; ++ if (*p == '*') /* RFC 2231 */ + { +- uschar * temp_string, * err_msg, * fname = q; +- int slen; +- +- /* build up an un-decoded filename over successive +- filename*= parameters (for use when 2047 decode fails) */ +-/*XXX could grow a gstring here */ +- +- mime_fname_rfc2231 = string_sprintf("%#s%s", +- mime_fname_rfc2231, q); +- +- if (!decoding_failed) ++ while (isdigit(*++p)) ; /* ignore cont-cnt values */ ++ if (*p == '*') p++; /* step over sep chset mark */ ++ if (*p == '=') + { +- int size; +- if (!mime_filename_charset) ++ uschar * q; ++ p++; /* step over = */ ++ q = mime_param_val(&p); /* p now trailing ; or NUL */ ++ ++ if (q && *q) /* q is the dequoted value */ + { +- uschar * s = q; ++ uschar * err_msg, * fname = q; ++ int slen; + +- /* look for a ' in the "filename" */ +- while(*s != '\'' && *s) s++; /* s is 1st ' or NUL */ ++ /* build up an un-decoded filename over successive ++ filename*= parameters (for use when 2047 decode fails) */ + +- if (*s) /* there was a ' */ ++ mime_fname_rfc2231 = string_cat(mime_fname_rfc2231, q); ++ ++ if (!decoding_failed) + { +- if ((size = s-q) > 0) +- mime_filename_charset = string_copyn(q, size); +- +- if (*(fname = s)) fname++; +- while(*fname == '\'') fname++; /* fname is after 2nd ' */ +- } +- } +- +- DEBUG(D_acl) +- debug_printf_indent("MIME: charset %s fname '%s'\n", +- mime_filename_charset ? mime_filename_charset : US"", +- fname); +- +- temp_string = rfc2231_to_2047(fname, mime_filename_charset, +- &slen); +- DEBUG(D_acl) +- debug_printf_indent("MIME: 2047-name %s\n", temp_string); +- +- temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ', +- NULL, &err_msg); +- DEBUG(D_acl) +- debug_printf_indent("MIME: plain-name %s\n", temp_string); +- +- if (!temp_string || (size = Ustrlen(temp_string)) == slen) +- decoding_failed = TRUE; +- else +- /* build up a decoded filename over successive +- filename*= parameters */ +- +- mime_filename = mime_fname = mime_fname +- ? string_sprintf("%s%s", mime_fname, temp_string) +- : temp_string; +- } /*!decoding_failed*/ +- } /*q*/ +- } /*2231 filename*/ +- +- else +- /* look for interesting parameters */ +- for (mime_parameter * mp = mime_parameter_list; +- mp < mime_parameter_list + nelem(mime_parameter_list); +- mp++ +- ) if (strncmpic(mp->name, p, mp->namelen) == 0) +- { +- uschar * q; +- uschar * dummy_errstr; ++ if (!mime_filename_charset) ++ { /* try for RFC 2231 chset/lang */ ++ uschar * s = q; ++ ++ /* look for a ' in the raw paramval */ ++ while(*s != '\'' && *s) s++; /* s is 1st ' or NUL */ ++ ++ if (*s) /* there was a ' */ ++ { ++ int size; ++ if ((size = s-q) > 0) ++ mime_filename_charset = string_copyn(q, size); ++ ++ if (*(fname = s)) fname++; ++ while(*fname == '\'') fname++; /*fname is after 2nd '*/ ++ } ++ } ++ ++ DEBUG(D_acl) ++ debug_printf_indent("MIME: charset %s fname '%s'\n", ++ mime_filename_charset ? mime_filename_charset : US"", ++ fname); ++ ++ fname = rfc2231_to_2047(fname, mime_filename_charset, ++ &slen); ++ DEBUG(D_acl) ++ debug_printf_indent("MIME: 2047-name %s\n", fname); ++ ++ fname = rfc2047_decode(fname, FALSE, NULL, ' ', ++ NULL, &err_msg); ++ DEBUG(D_acl) debug_printf_indent( ++ "MIME: plain-name %s\n", fname); ++ ++ if (!fname || Ustrlen(fname) == slen) ++ decoding_failed = TRUE; ++ else if (mp->value == &mime_filename) ++ { ++ /* build up a decoded filename over successive ++ filename*= parameters */ ++ ++ mime_fname = string_cat(mime_fname, fname); ++ mime_filename = string_from_gstring(mime_fname); ++ } ++ } /*!decoding_failed*/ ++ } /*q*/ ++ ++ if (*p) p++; /* p is past ; */ ++ goto param_done; /* done matching param names */ ++ } /*2231 param coding extension*/ ++ } ++ else if (*p == '=') ++ { /* non-2231 param */ ++ uschar * q, * dummy_errstr; + + /* grab the value and copy to its expansion variable */ +- p += mp->namelen; ++ ++ if (*p) p++; /* step over = */ + q = mime_param_val(&p); /* p now trailing ; or NUL */ + + *mp->value = q && *q +@@ -683,26 +685,32 @@ while(1) + "MIME: found %s parameter in %s header, value '%s'\n", + mp->name, mh->name, *mp->value); + +- break; /* done matching param names */ ++ if (*p) p++; /* p is past ; */ ++ goto param_done; /* done matching param names */ + } ++ } /* interesting parameters */ + ++ /* There is something, but not one of our interesting parameters. ++ Advance past the next semicolon */ + +- /* There is something, but not one of our interesting parameters. +- Advance past the next semicolon */ +- p = mime_next_semicolon(p); +- if (*p) p++; +- } /* param scan on line */ ++ p = mime_next_semicolon(p); ++ if (*p) p++; ++ param_done: ++ ; /* param scan on line */ ++ } + + if (strncmpic(CUS"content-disposition:", header, 20) == 0) + { +- if (decoding_failed) mime_filename = mime_fname_rfc2231; ++ if (decoding_failed) ++ mime_filename = string_from_gstring(mime_fname_rfc2231); + + DEBUG(D_acl) debug_printf_indent( + "MIME: found %s parameter in %s header, value is '%s'\n", + "filename", mh->name, mime_filename); + } + } +- } ++ break; ++ } /* interesting headers */ + + /* set additional flag variables (easier access) */ + if ( mime_content_type +-- +2.33.0 + diff --git a/exim.spec b/exim.spec index 55bdeec..a62cd5a 100644 --- a/exim.spec +++ b/exim.spec @@ -4,7 +4,7 @@ Summary: The exim mail transfer agent Name: exim Version: 4.96 -Release: 3 +Release: 4 License: GPLv2+ Url: https://www.exim.org/ @@ -45,6 +45,9 @@ Patch4: exim-4.96-opendmarc-1.4-build-fix.patch Patch5: exim-4.96-build-fix.patch Patch6: CVE-2023-51766.patch Patch7: CVE-2022-3559.patch +# CVE-2024-39929 +Patch8: backport-bug3099-Fix-MIME-parsing-of-filenames-specified-using-multiple-parameters.patch +Patch9: backport-bug3099-MIME-support-RFC-2331-for-name.patch Requires: /etc/pki/tls/certs /etc/pki/tls/private Requires: setup @@ -484,6 +487,9 @@ fi %{_sysconfdir}/cron.daily/greylist-tidy.sh %changelog +* Thu Jul 11 2024 zhangxianting - 4.96-4 +- fix CVE-2024-39929 + * Thu Jul 11 2024 technology208 - 4.96-3 - Fix CVE-2022-3559 -- Gitee