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/doc-docbook/spec.xfpt | 14 +++---- doc/doc-txt/ChangeLog | 3 ++ src/src/mime.c | 51 +++++++++++++----------- src/src/string.c | 1 + test/log/4000 | 3 ++ test/mail/4000.userx | 70 +++++++++++++++++++++++++++++++++ test/scripts/4000-scanning/4000 | 31 +++++++++++++++ test/stdout/4000 | 12 ++++++ 8 files changed, 156 insertions(+), 29 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index cfdf0ca1a..514ec24d0 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -35231,13 +35231,13 @@ If the string does not start with a slash, it is used as the filename, and the default path is then used. .endlist 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 -.code -decode = $mime_filename -.endd -However, you should keep in mind that &$mime_filename$& might contain -anything. If you place files outside of the default path, they are not +errors or unusual circumstances such as memory shortages. +.new +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. +.wen +If you place files outside of the default path, they are not automatically unlinked. For RFC822 attachments (these are messages attached to messages, with a diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index daa62ad0e..acb779605 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -164,6 +164,9 @@ JH/33 Bug 2994: A subdir dsearch lookup should permit a directory name that star JH/34 Fix delivery ordering for 2-phase queue run combined with queue_run_in_order. +JH/35 Bug 3099: fix parsing of MIME filenames split over multiple paramemters. + Previously the $mime_filename variable would have an incorrect value. + Exim version 4.97 ----------------- diff --git a/src/src/mime.c b/src/src/mime.c index 975ddca85..5f9e1ade7 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -587,10 +587,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 */ @@ -604,11 +604,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); @@ -623,26 +624,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 @@ -651,9 +658,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 */ @@ -682,7 +689,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 */ @@ -800,5 +807,5 @@ return rc; #endif /*WITH_CONTENT_SCAN*/ -/* vi: sw ai sw=2 +/* vi: aw ai sw=2 */ diff --git a/src/src/string.c b/src/src/string.c index 113c05754..aa768d03c 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1342,6 +1342,7 @@ Field width: decimal digits, or * Precision: dot, followed by decimal digits or * Length modifiers: h L l ll z Conversion specifiers: n d o u x X p f e E g G % c s S T W V Y D M +Alternate-form: %#s is silent about a null string Returns the possibly-new (if copy for growth or taint-handling was needed) string, not nul-terminated. diff --git a/test/log/4000 b/test/log/4000 index 4aed68039..912a32a1a 100644 --- a/test/log/4000 +++ b/test/log/4000 @@ -20,3 +20,6 @@ 1999-03-02 09:44:33 10HmbE-000000005vi-0000 => userx R=r1 T=t1 1999-03-02 09:44:33 10HmbE-000000005vi-0000 Completed 1999-03-02 09:44:33 10HmaX-000000005vi-0000 U=CALLER F= rejected during MIME ACL checks: this is a deny from the mime acl +1999-03-02 09:44:33 10HmbF-000000005vi-0000 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="Bug 3099 (2)" +1999-03-02 09:44:33 10HmbF-000000005vi-0000 => userx R=r1 T=t1 +1999-03-02 09:44:33 10HmbF-000000005vi-0000 Completed diff --git a/test/mail/4000.userx b/test/mail/4000.userx index 569813066..242be6627 100644 --- a/test/mail/4000.userx +++ b/test/mail/4000.userx @@ -406,3 +406,73 @@ foobar --T4sUOijqQbZv57TR-- +From CALLER@myhost.test.ex Tue Mar 02 09:44:33 1999 +Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmbF-000000005vi-0000 + for userx@test.ex; + Tue, 2 Mar 1999 09:44:33 +0000 +From: localpart@test.example +To: localpart@test.example +Subject: Bug 3099 (2) +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="----=_MIME_BOUNDARY_000_695039" +Message-Id: +Sender: CALLER_NAME +Date: Tue, 2 Mar 1999 09:44:33 +0000 +X-0-content-type: multipart/mixed +X-0-filename: +X-0-charset: +X-0-boundary: ----=_MIME_BOUNDARY_000_695039 +X-0-content-disposition: +X-0-content-transfer-encoding: +X-0-content-id: +X-0-content-description: +X-0-is-multipart: 1 +X-0-is-coverletter: 1 +X-0-is-rfc822: 0 +X-0-decode-filename: TESTSUITE/spool/scan/10HmbF-000000005vi-0000/10HmbF-000000005vi-0000-00000 +X-0-content-size: 1 +X-1-content-type: text/plain +X-1-filename: +X-1-charset: +X-1-boundary: +X-1-content-disposition: +X-1-content-transfer-encoding: +X-1-content-id: +X-1-content-description: +X-1-is-multipart: 0 +X-1-is-coverletter: 1 +X-1-is-rfc822: 0 +X-1-decode-filename: TESTSUITE/spool/scan/10HmbF-000000005vi-0000/10HmbF-000000005vi-0000-00001 +X-1-content-size: 1 +X-2-content-type: application/octet-stream +X-2-filename: example3.exe +X-2-charset: +X-2-boundary: +X-2-content-disposition: attachment +X-2-content-transfer-encoding: base64 +X-2-content-id: +X-2-content-description: +X-2-is-multipart: 0 +X-2-is-coverletter: 0 +X-2-is-rfc822: 0 +X-2-decode-filename: TESTSUITE/spool/scan/10HmbF-000000005vi-0000/10HmbF-000000005vi-0000-00002 +X-2-content-size: 1 + +------=_MIME_BOUNDARY_000_695039 +Content-Type: text/plain + +This is a test mailing +------=_MIME_BOUNDARY_000_695039 +Content-Type: application/octet-stream +Content-Disposition: attachment; + filename*0*="example3"; + filename*1*=".exe" +Content-Transfer-Encoding: BASE64 + +QmVpc3BpZWwK + +------=_MIME_BOUNDARY_000_695039-- + diff --git a/test/scripts/4000-scanning/4000 b/test/scripts/4000-scanning/4000 index 623c5420f..bb2835ed3 100644 --- a/test/scripts/4000-scanning/4000 +++ b/test/scripts/4000-scanning/4000 @@ -272,3 +272,34 @@ foobar . quit **** +# +# +# Filename using parameter value continuation (RFC 2231 sec. 3) +exim -odi -bs +ehlo test.ex +mail from:<> +rcpt to: +data +From: localpart@test.example +To: localpart@test.example +Subject: Bug 3099 (2) +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="----=_MIME_BOUNDARY_000_695039" + +------=_MIME_BOUNDARY_000_695039 +Content-Type: text/plain + +This is a test mailing +------=_MIME_BOUNDARY_000_695039 +Content-Type: application/octet-stream +Content-Disposition: attachment; + filename*0*="example3"; + filename*1*=".exe" +Content-Transfer-Encoding: BASE64 + +QmVpc3BpZWwK + +------=_MIME_BOUNDARY_000_695039-- +. +quit +**** diff --git a/test/stdout/4000 b/test/stdout/4000 index b9098d554..ec6593646 100644 --- a/test/stdout/4000 +++ b/test/stdout/4000 @@ -94,3 +94,15 @@ 354 Enter message, ending with "." on a line by itself 550 this is a deny from the mime acl 221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-LIMITS MAILMAX=1000 RCPTMAX=50000 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmbF-000000005vi-0000 +221 myhost.test.ex closing connection -- 2.30.2