2020#include "msc_util.h"
2121#include "msc_parsers.h"
2222
23+ static const char * mime_charset_special = "!#$%&+-^_`{}~" ;
24+ static const char * attr_char_special = "!#$&+-.^_`~" ;
25+
2326void validate_quotes (modsec_rec * msr , char * data , char quote ) {
2427 assert (msr != NULL );
2528 int i , len ;
@@ -130,7 +133,53 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value)
130133 */
131134
132135 char quote = '\0' ;
133- if ((* p == '"' ) || (* p == '\'' )) {
136+ if (strcmp (name , "filename*" ) == 0 ) {
137+ /* filename*=charset'[optional-language]'filename */
138+ /* Read beyond the charset and the optional language*/
139+ const char * start_of_charset = p ;
140+ while ((* p != '\0' ) && (isalnum (* p ) || (strchr (mime_charset_special , * p )))) {
141+ p ++ ;
142+ }
143+ if ((* p != '\'' ) || (p == start_of_charset )) {
144+ return -16 ; // Must be at least one legit char before ' for start of language
145+ }
146+ msr -> mpd -> mpp -> filename_charset = apr_pstrmemdup (msr -> mp , start_of_charset , (p - start_of_charset ));
147+ if (msr -> txcfg -> debuglog_level >= 9 ) {
148+ msr_log (msr , 9 , "Multipart: Content-Disposition filename* charset: %s" ,
149+ log_escape_nq (msr -> mp , msr -> mpd -> mpp -> filename_charset ));
150+ }
151+ p ++ ;
152+ const char * start_of_language = p ;
153+ while ((* p != '\0' ) && (* p != '\'' )) {
154+ p ++ ;
155+ }
156+ if (* p != '\'' ) {
157+ msr -> mpd -> flag_invalid_quoting = 1 ;
158+ return -17 ; // Single quote for end-of-language not found
159+ }
160+ msr -> mpd -> mpp -> filename_language = apr_pstrmemdup (msr -> mp , start_of_language , (p - start_of_language ));
161+ if (msr -> txcfg -> debuglog_level >= 9 ) {
162+ msr_log (msr , 9 , "Multipart: Content-Disposition filename* language: %s" ,
163+ log_escape_nq (msr -> mp , msr -> mpd -> mpp -> filename_language ));
164+ }
165+ p ++ ;
166+
167+ /* Now read what should be the actual filename */
168+ const char * start_of_filename = p ;
169+ while ((* p != '\0' ) && (* p != ';' )) {
170+ if (* p == '%' ) {
171+ if ((* (p + 1 ) == '\0' ) || (* (p + 2 ) == '\0' ) || (!isxdigit (* (p + 1 ))) || (!isxdigit (* (p + 2 )))) {
172+ return -18 ;
173+ }
174+ p += 3 ;
175+ } else if (isalnum (* p ) || strchr (attr_char_special , * p )) {
176+ p ++ ;
177+ } else {
178+ return -19 ;
179+ }
180+ }
181+ value = apr_pmemdup (msr -> mp , start_of_filename , p - start_of_filename );
182+ } else if ((* p == '"' ) || (* p == '\'' )) {
134183 /* quoted */
135184 quote = * p ; // remember which quote character was used for the value
136185
@@ -191,9 +240,8 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value)
191240
192241 validate_quotes (msr , value , quote );
193242
194- msr -> multipart_name = apr_pstrdup (msr -> mp , value );
195-
196243 if (msr -> mpd -> mpp -> name != NULL ) {
244+ msr -> mpd -> flag_duplicate_part_header = 1 ;
197245 msr_log (msr , 4 , "Multipart: Warning: Duplicate Content-Disposition name: %s" ,
198246 log_escape_nq (msr -> mp , value ));
199247 return -14 ;
@@ -205,23 +253,43 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value)
205253 log_escape_nq (msr -> mp , value ));
206254 }
207255 }
208- else
209- if (strcmp (name , "filename" ) == 0 ) {
210-
211- validate_quotes (msr , value , quote );
212-
213- msr -> multipart_filename = apr_pstrdup (msr -> mp , value );
214-
215- if (msr -> mpd -> mpp -> filename != NULL ) {
256+ else if (strcmp (name , "filename" ) == 0 ) {
257+ // check if the 'filename' (and not the `filename*`) is already added from the header, if yes then return error
258+ if (msr -> mpd -> mpp -> filename != NULL && strlen (msr -> mpd -> mpp -> filename ) != 0 ) {
259+ msr -> mpd -> flag_duplicate_part_header = 1 ;
216260 msr_log (msr , 4 , "Multipart: Warning: Duplicate Content-Disposition filename: %s" ,
217261 log_escape_nq (msr -> mp , value ));
218262 return -15 ;
219263 }
220- msr -> mpd -> mpp -> filename = value ;
264+ else {
265+ validate_quotes (msr , value , quote );
266+
267+ msr -> mpd -> mpp -> filename = apr_pstrdup (msr -> mp , value );
221268
269+ if (msr -> txcfg -> debuglog_level >= 9 ) {
270+ msr_log (msr , 9 , "Multipart: Content-Disposition filename: %s" ,
271+ log_escape_nq (msr -> mp , value ));
272+ }
273+ }
274+ } else if (strcmp (name , "filename*" ) == 0 ) {
275+ // check if the 'filename*' (and not the `filename`) is already added from the header, if yes then return error
276+ if (msr -> mpd -> mpp -> filename_star != NULL && strlen (msr -> mpd -> mpp -> filename_star ) != 0 ) {
277+ msr -> mpd -> flag_duplicate_part_header = 1 ;
278+ msr_log (msr , 4 ,
279+ "Multipart: Warning: Duplicate Content-Disposition filename*: %s." , log_escape_nq (msr -> mp , value ));
280+ return -15 ;
281+ }
282+ int invalid_count = 0 ;
283+ int changed = 0 ;
284+ char * decoded_value = apr_pstrdup (msr -> mp , value );
285+ int decoded_len = urldecode_nonstrict_inplace_ex_plus ((unsigned char * )decoded_value , strlen (decoded_value ), 0 , & invalid_count , & changed );
286+ if (invalid_count > 0 ) {
287+ msr -> mpd -> flag_invalid_part = 1 ;
288+ }
289+ msr -> mpd -> mpp -> filename_star = apr_pstrdup (msr -> mp , decoded_value );
222290 if (msr -> txcfg -> debuglog_level >= 9 ) {
223- msr_log (msr , 9 , "Multipart: Content-Disposition filename: %s" ,
224- log_escape_nq (msr -> mp , value ));
291+ msr_log (msr , 9 , "Multipart: Content-Disposition filename* : %s. " ,
292+ log_escape_nq (msr -> mp , decoded_value ));
225293 }
226294 }
227295 else return -11 ;
@@ -239,7 +307,7 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value)
239307 }
240308 msr -> mpd -> flag_invalid_quoting = 1 ;
241309 }
242- p ++ ;
310+ // p++;
243311 return -12 ;
244312 }
245313 p ++ ; /* move over the semi-colon */
@@ -315,7 +383,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
315383 * values from the C-D header. We need to check for the case where they
316384 * didn't understand C-D but we did.
317385 */
318- if (strstr (header_value , "filename=" ) == NULL ) {
386+ if (strstr (header_value , "filename=" ) == NULL && strstr ( header_value , "filename*=" ) == NULL ) {
319387 * error_msg = apr_psprintf (msr -> mp , "Multipart: Invalid Content-Disposition header (filename)." );
320388 return -1 ;
321389 }
@@ -442,6 +510,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
442510
443511 /* error if the name already exists */
444512 if (apr_table_get (msr -> mpd -> mpp -> headers , header_name ) != NULL ) {
513+ msr -> mpd -> flag_duplicate_part_header = 1 ;
445514 * error_msg = apr_psprintf (msr -> mp , "Multipart: Duplicate part header: %s." ,
446515 log_escape_nq (msr -> mp , header_name ));
447516 return -1 ;
0 commit comments