in apache2/msc_multipart.c [81:296]
static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value) {
char *p = NULL, *t = NULL;
/* accept only what we understand */
if (strncmp(c_d_value, "form-data", 9) != 0) {
return -1;
}
/* see if there are any other parts to parse */
p = c_d_value + 9;
while((*p == '\t') || (*p == ' ')) p++;
if (*p == '\0') return 1; /* this is OK */
if (*p != ';') return -2;
p++;
uint8_t filename_present = FALSE;
uint8_t filename_ext_present = FALSE;
/* parse the appended parts */
while(*p != '\0') {
char *name = NULL, *value = NULL, *start = NULL;
/* go over the whitespace */
while((*p == '\t') || (*p == ' ')) p++;
if (*p == '\0') return -3;
start = p;
while((*p != '\0') && (*p != '=') && (*p != '\t') && (*p != ' ')) p++;
if (*p == '\0') return -4;
name = apr_pstrmemdup(msr->mp, start, (p - start));
while((*p == '\t') || (*p == ' ')) p++;
if (*p == '\0') return -5;
if (*p != '=') return -13;
p++;
while((*p == '\t') || (*p == ' ')) p++;
if (*p == '\0') return -6;
/* Accept both quotes as some backends will accept them, but
* technically "'" is invalid and so flag_invalid_quoting is
* set so the user can deal with it in the rules if they so wish.
*/
if ((*p == '"') || (*p == '\'')) {
/* quoted */
char quote = *p;
if (quote == '\'') {
msr->mpd->flag_invalid_quoting = 1;
}
p++;
if (*p == '\0') return -7;
start = p;
value = apr_pstrdup(msr->mp, p);
t = value;
while(*p != '\0') {
if (*p == '\\') {
if (*(p + 1) == '\0') {
/* improper escaping */
return -8;
}
/* only quote and \ can be escaped */
if ((*(p + 1) == quote) || (*(p + 1) == '\\')) {
p++;
}
else {
/* improper escaping */
/* We allow for now because IE sends
* improperly escaped content and there's
* nothing we can do about it.
*
* return -9;
*/
}
}
else if (*p == quote) {
*t = '\0';
break;
}
*(t++) = *(p++);
}
if (*p == '\0') return -10;
p++; /* go over the quote at the end */
} else {
/* not quoted */
start = p;
while((*p != '\0') && (is_token_char(*p))) p++;
value = apr_pstrmemdup(msr->mp, start, (p - start));
}
/* evaluate part */
if (strcmp(name, "name") == 0) {
validate_quotes(msr, value);
msr->multipart_name = apr_pstrdup(msr->mp, value);
if (msr->mpd->mpp->name != NULL) {
msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition name: %s",
log_escape_nq(msr->mp, value));
return -14;
}
msr->mpd->mpp->name = value;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Content-Disposition name: %s",
log_escape_nq(msr->mp, value));
}
}
else if ((strcmp(name, "filename") == 0) || (strcmp(name, "filename*") == 0))
{
char *decoded_filename = NULL;
if (strcmp(name, "filename*") == 0)
{
// We allow only one instance of `filename*` attribute to be present in the Content-Disposition header.
if (filename_ext_present)
{
msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition filename*: %s",
log_escape_nq(msr->mp, decoded_filename));
return -17;
}
filename_ext_present = TRUE;
// Make sure to turn of INVALID quoting since RFC 5987 expects quotes in the filename format.
msr->mpd->flag_invalid_quoting = 0;
decoded_filename = rfc5987_decode(msr->mp, value);
if (!decoded_filename)
{
msr_log(msr, 4, "Multipart: Could not decode extended filename parameter in RFC 5987 format: %s",
log_escape_nq(msr->mp, value));
return -16;
}
msr->multipart_filename = decoded_filename;
// The `filename*` RCF 5987 encoded filename always overrides the `filename` parameter in content-disposition header.
msr->mpd->mpp->filename = apr_pstrdup(msr->mp, decoded_filename);
// Re-run the validation check on the filename. We shouldn't be seeing quotes in the UTF-8 formatted filename either.
validate_quotes(msr, msr->mpd->mpp->filename);
}
else
{
// We allow only one instance of `filename` attribute to be present in the Content-Disposition header.
if (filename_present)
{
// Duplicate `filename` attributes are not allowed.
msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition filename: %s",
log_escape_nq(msr->mp, decoded_filename));
return -15;
}
filename_present = TRUE;
// Process the `filename` attribute in the content-disposition header only if `filename*` does not exist.
if (!filename_ext_present)
{
// "name == 'filename'"
decoded_filename = value;
validate_quotes(msr, value);
msr->multipart_filename = apr_pstrdup(msr->mp, decoded_filename);
msr->mpd->mpp->filename = decoded_filename;
}
}
if (msr->txcfg->debuglog_level >= 9)
{
msr_log(msr, 9, "Multipart: Content-Disposition filename: %s",
log_escape_nq(msr->mp, decoded_filename));
}
}
else return -11;
if (*p != '\0') {
while((*p == '\t') || (*p == ' ')) p++;
/* the next character must be a zero or a semi-colon */
if (*p == '\0') return 1; /* this is OK */
if (*p != ';') {
p--;
if(*p == '\'' || *p == '\"') {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Invalid quoting detected: %s length %zu bytes",
log_escape_nq(msr->mp, p), strlen(p));
}
msr->mpd->flag_invalid_quoting = 1;
}
p++;
return -12;
}
p++; /* move over the semi-colon */
}
/* loop will stop when (*p == '\0') */
}
return 1;
}