mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2026-01-09 17:54:37 +03:00
Backport multipart changes to 2.1.x.
This commit is contained in:
@@ -172,38 +172,62 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value)
|
||||
*
|
||||
*/
|
||||
static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
int rc;
|
||||
int i, rc;
|
||||
|
||||
if (error_msg == NULL) return -1;
|
||||
*error_msg = NULL;
|
||||
|
||||
if ((msr->mpd->buf[0] == '\r')
|
||||
&&(msr->mpd->buf[1] == '\n')
|
||||
&&(msr->mpd->buf[2] == '\0'))
|
||||
/* Check for nul bytes. */
|
||||
for(i = 0; i < (MULTIPART_BUF_SIZE - msr->mpd->bufleft); i++) {
|
||||
if (msr->mpd->buf[i] == '\0') {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Nul byte in part headers.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is this an empty line? */
|
||||
if ( ((msr->mpd->buf[0] == '\r')
|
||||
&&(msr->mpd->buf[1] == '\n')
|
||||
&&(msr->mpd->buf[2] == '\0') )
|
||||
|| ((msr->mpd->buf[0] == '\n')
|
||||
&&(msr->mpd->buf[1] == '\0') ) )
|
||||
{
|
||||
char *header_value;
|
||||
|
||||
/* empty line */
|
||||
/* Empty line. */
|
||||
|
||||
if (msr->mpd->buf[0] == '\n') {
|
||||
msr->mpd->flag_lf_line = 1;
|
||||
}
|
||||
|
||||
header_value = (char *)apr_table_get(msr->mpd->mpp->headers, "Content-Disposition");
|
||||
if (header_value == NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Part is missing the Content-Disposition header");
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Part missing Content-Disposition header.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = multipart_parse_content_disposition(msr, header_value);
|
||||
if (rc < 0) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (%i): %s",
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (%i): %s.",
|
||||
rc, log_escape_nq(msr->mp, header_value));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msr->mpd->mpp->name == NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Part name missing");
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Content-Disposition header missing name field.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (msr->mpd->mpp->filename != NULL) {
|
||||
/* Some parsers use crude methods to extract the name and filename
|
||||
* values from the C-D header. We need to check for the case where they
|
||||
* don't understand and C-D we do.
|
||||
*/
|
||||
if (strstr(header_value, "filename=") == NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (filename).");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msr->mpd->mpp->type = MULTIPART_FILE;
|
||||
} else {
|
||||
msr->mpd->mpp->type = MULTIPART_FORMDATA;
|
||||
@@ -212,19 +236,20 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
msr->mpd->mpp_state = 1;
|
||||
msr->mpd->mpp->last_header_name = NULL;
|
||||
} else {
|
||||
/* header line */
|
||||
/* Header line. */
|
||||
if ((msr->mpd->buf[0] == '\t')||(msr->mpd->buf[0] == ' ')) {
|
||||
char *header_value, *new_value, *data;
|
||||
|
||||
/* header folding, add data to the header we are building */
|
||||
msr->mpd->flag_header_folding = 1;
|
||||
|
||||
if (msr->mpd->mpp->last_header_name == NULL) {
|
||||
/* we are not building a header at this moment */
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: invalid part header (invalid folding)");
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (folding error).");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* locate the beginning of the data */
|
||||
/* locate the beginning of data */
|
||||
data = msr->mpd->buf;
|
||||
while((*data == '\t')||(*data == ' ')) data++;
|
||||
|
||||
@@ -241,7 +266,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
log_escape(msr->mp, data));
|
||||
|
||||
if (strlen(new_value) > 4096) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multpart: invalid part header (too long)");
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Part header too long.");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
@@ -252,7 +277,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
data = msr->mpd->buf;
|
||||
while((*data != ':')&&(*data != '\0')) data++;
|
||||
if (*data == '\0') {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: invalid part header (missing colon): %s",
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (colon missing): %s.",
|
||||
log_escape_nq(msr->mp, msr->mpd->buf));
|
||||
return -1;
|
||||
}
|
||||
@@ -267,7 +292,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
|
||||
/* error if the name already exists */
|
||||
if (apr_table_get(msr->mpd->mpp->headers, header_name) != NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: part header already exists: %s",
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Duplicate part header: %s.",
|
||||
log_escape_nq(msr->mp, header_name));
|
||||
return -1;
|
||||
}
|
||||
@@ -288,22 +313,36 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
*
|
||||
*/
|
||||
static int multipart_process_part_data(modsec_rec *msr, char **error_msg) {
|
||||
char *p = msr->mpd->buf + (MULTIPART_BUF_SIZE - msr->mpd->bufleft) - 2;
|
||||
char *p = msr->mpd->buf + (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
|
||||
char localreserve[2] = { '\0', '\0' }; /* initialized to quiet warning */
|
||||
int bytes_reserved = 0;
|
||||
|
||||
if (error_msg == NULL) return -1;
|
||||
*error_msg = NULL;
|
||||
|
||||
/* preserve the last two bytes for later */
|
||||
if (MULTIPART_BUF_SIZE - msr->mpd->bufleft >= 2) {
|
||||
bytes_reserved = 1;
|
||||
localreserve[0] = *p;
|
||||
localreserve[1] = *(p + 1);
|
||||
msr->mpd->bufleft += 2;
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
/* Preserve some bytes for later. */
|
||||
if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) >= 1)
|
||||
&& (*(p - 1) == '\n') )
|
||||
{
|
||||
if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) >= 2)
|
||||
&& (*(p - 2) == '\r') )
|
||||
{
|
||||
/* Two bytes. */
|
||||
bytes_reserved = 2;
|
||||
localreserve[0] = *(p - 1);
|
||||
localreserve[1] = *(p - 2);
|
||||
msr->mpd->bufleft += 2;
|
||||
*(p - 2) = 0;
|
||||
} else {
|
||||
/* Only one byte. */
|
||||
bytes_reserved = 1;
|
||||
localreserve[0] = *(p - 1);
|
||||
localreserve[1] = 0;
|
||||
msr->mpd->bufleft += 1;
|
||||
*(p - 1) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* add data to the part we are building */
|
||||
if (msr->mpd->mpp->type == MULTIPART_FILE) {
|
||||
|
||||
@@ -318,8 +357,6 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) {
|
||||
if (msr->upload_extract_files) {
|
||||
/* first create a temporary file if we don't have it already */
|
||||
if (msr->mpd->mpp->tmp_file_fd == 0) {
|
||||
// char *filename = multipart_construct_filename(msr);
|
||||
|
||||
/* construct temporary file name */
|
||||
msr->mpd->mpp->tmp_file_name = apr_psprintf(msr->mp, "%s/%s-%s-file-XXXXXX",
|
||||
msr->txcfg->tmp_dir, current_filetime(msr->mp), msr->txid);
|
||||
@@ -337,14 +374,15 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) {
|
||||
}
|
||||
|
||||
/* write the reserve first */
|
||||
if (msr->mpd->reserve[0] == 1) {
|
||||
if (write(msr->mpd->mpp->tmp_file_fd, &msr->mpd->reserve[1], 2) != 2) {
|
||||
if (msr->mpd->reserve[0] != 0) {
|
||||
if (write(msr->mpd->mpp->tmp_file_fd, &msr->mpd->reserve[1], msr->mpd->reserve[0]) != msr->mpd->reserve[0]) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: writing to \"%s\" failed",
|
||||
log_escape(msr->mp, msr->mpd->mpp->tmp_file_name));
|
||||
return -1;
|
||||
}
|
||||
msr->mpd->mpp->tmp_file_size += 2;
|
||||
msr->mpd->mpp->length += 2;
|
||||
|
||||
msr->mpd->mpp->tmp_file_size += msr->mpd->reserve[0];
|
||||
msr->mpd->mpp->length += msr->mpd->reserve[0];
|
||||
}
|
||||
|
||||
/* write data to the file */
|
||||
@@ -360,11 +398,8 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) {
|
||||
msr->mpd->mpp->length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
|
||||
} else {
|
||||
/* just keep track of the file size */
|
||||
if (msr->mpd->reserve[0] == 1) {
|
||||
msr->mpd->mpp->tmp_file_size += 2;
|
||||
}
|
||||
msr->mpd->mpp->tmp_file_size += (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
|
||||
msr->mpd->mpp->length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
|
||||
msr->mpd->mpp->tmp_file_size += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0];
|
||||
msr->mpd->mpp->length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0];
|
||||
}
|
||||
}
|
||||
else if (msr->mpd->mpp->type == MULTIPART_FORMDATA) {
|
||||
@@ -376,12 +411,13 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) {
|
||||
if (msr->mpd->mpp->length == 0) {
|
||||
msr->mpd->mpp->offset = msr->mpd->buf_offset;
|
||||
}
|
||||
|
||||
if (msr->mpd->reserve[0] == 1) {
|
||||
value_part->data = apr_palloc(msr->mp, (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + 2);
|
||||
memcpy(value_part->data, &(msr->mpd->reserve[1]), 2);
|
||||
memcpy(value_part->data + 2, msr->mpd->buf, (MULTIPART_BUF_SIZE - msr->mpd->bufleft));
|
||||
value_part->length = (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + 2;
|
||||
|
||||
if (msr->mpd->reserve[0] != 0) {
|
||||
value_part->data = apr_palloc(msr->mp, (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]);
|
||||
memcpy(value_part->data, &(msr->mpd->reserve[1]), msr->mpd->reserve[0]);
|
||||
memcpy(value_part->data + msr->mpd->reserve[0], msr->mpd->buf, (MULTIPART_BUF_SIZE - msr->mpd->bufleft));
|
||||
|
||||
value_part->length = (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0];
|
||||
msr->mpd->mpp->length += value_part->length;
|
||||
} else {
|
||||
value_part->length = (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
|
||||
@@ -403,14 +439,14 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) {
|
||||
* context so that they don't get lost
|
||||
*/
|
||||
if (bytes_reserved) {
|
||||
msr->mpd->reserve[0] = 1;
|
||||
msr->mpd->reserve[0] = bytes_reserved;
|
||||
msr->mpd->reserve[1] = localreserve[0];
|
||||
msr->mpd->reserve[2] = localreserve[1];
|
||||
msr->mpd->buf_offset += 2;
|
||||
msr->mpd->buf_offset += bytes_reserved;
|
||||
}
|
||||
else {
|
||||
msr->mpd->buf_offset -= msr->mpd->reserve[0];
|
||||
msr->mpd->reserve[0] = 0;
|
||||
msr->mpd->buf_offset -= 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -510,6 +546,12 @@ int multipart_init(modsec_rec *msr, char **error_msg) {
|
||||
msr->mpd = (multipart_data *)apr_pcalloc(msr->mp, sizeof(multipart_data));
|
||||
if (msr->mpd == NULL) return -1;
|
||||
|
||||
msr->mpd->parts = apr_array_make(msr->mp, 10, sizeof(multipart_part *));
|
||||
msr->mpd->bufleft = MULTIPART_BUF_SIZE;
|
||||
msr->mpd->bufptr = msr->mpd->buf;
|
||||
msr->mpd->buf_contains_line = 1;
|
||||
msr->mpd->mpp = NULL;
|
||||
|
||||
if (msr->request_content_type == NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Content-Type header not available.");
|
||||
return -1;
|
||||
@@ -517,19 +559,48 @@ int multipart_init(modsec_rec *msr, char **error_msg) {
|
||||
|
||||
msr->mpd->boundary = strstr(msr->request_content_type, "boundary=");
|
||||
if ((msr->mpd->boundary != NULL)&&(*(msr->mpd->boundary + 9) != 0)) {
|
||||
msr->mpd->boundary = msr->mpd->boundary + 9;
|
||||
char *b = msr->mpd->boundary + 9;
|
||||
int len = strlen(b);
|
||||
|
||||
/* Is the boundary quoted? */
|
||||
if ((len >= 2)&&(*b == '"')&&(*(b + len - 1) == '"')) {
|
||||
/* Quoted. */
|
||||
msr->mpd->boundary = apr_pstrndup(msr->mp, b + 1, len - 2);
|
||||
msr->mpd->flag_boundary_quoted = 1;
|
||||
|
||||
if (strstr(msr->mpd->boundary, "\"") != NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Invalid boundary in C-T (quote).");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
/* Not quoted. */
|
||||
|
||||
/* Test for partial quoting. */
|
||||
if ( (*b == '"')
|
||||
|| ((len >= 2)&&(*(b + len - 1) == '"')) )
|
||||
{
|
||||
*error_msg = apr_psprintf(msr->mp, "Invalid boundary in C-T (quote).");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msr->mpd->boundary = apr_pstrdup(msr->mp, b);
|
||||
msr->mpd->flag_boundary_quoted = 0;
|
||||
}
|
||||
|
||||
msr_log(msr, 9, "Multipart: Boundary%s: %s",
|
||||
(msr->mpd->flag_boundary_quoted ? " (quoted)" : ""),
|
||||
log_escape_nq(msr->mp, msr->mpd->boundary));
|
||||
|
||||
if (strlen(msr->mpd->boundary) == 0) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart boundary in C-T empty.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart Boundary not found or invalid.");
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart boundary in C-T not found or invalid.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msr->mpd->parts = apr_array_make(msr->mp, 10, sizeof(multipart_part *));
|
||||
msr->mpd->bufleft = MULTIPART_BUF_SIZE;
|
||||
msr->mpd->bufptr = msr->mpd->buf;
|
||||
msr->mpd->buf_contains_line = 1;
|
||||
msr->mpd->mpp = NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -540,7 +611,7 @@ int multipart_complete(modsec_rec *msr, char **error_log) {
|
||||
if (msr->mpd == NULL) return 1;
|
||||
|
||||
if ((msr->mpd->seen_data != 0)&&(msr->mpd->is_complete == 0)) {
|
||||
*error_log = apr_psprintf(msr->mp, "Multipart: final boundary missing");
|
||||
*error_log = apr_psprintf(msr->mp, "Multipart: Final boundary missing.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -564,17 +635,19 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf,
|
||||
if (msr->mpd->seen_data == 0) msr->mpd->seen_data = 1;
|
||||
|
||||
if (msr->mpd->is_complete) {
|
||||
msr->mpd->flag_data_before = 1;
|
||||
msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %i bytes)", size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (msr->mpd->bufleft == 0) {
|
||||
msr->mpd->flag_error = 1;
|
||||
*error_msg = apr_psprintf(msr->mp,
|
||||
"Multipart: Internal error in process_chunk: no space left in the buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* here we loop through the data available, byte by byte */
|
||||
/* here we loop through the available data, one byte at a time */
|
||||
while(inleft > 0) {
|
||||
char c = *inptr;
|
||||
int process_buffer = 0;
|
||||
@@ -595,47 +668,85 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf,
|
||||
* or the end of our internal buffer
|
||||
*/
|
||||
if ((c == 0x0a)||(msr->mpd->bufleft == 0)||(process_buffer)) {
|
||||
int processed_as_boundary = 0;
|
||||
|
||||
*(msr->mpd->bufptr) = 0;
|
||||
|
||||
/* boundary preconditions: length of the line greater than
|
||||
* the length of the boundary + the first two characters
|
||||
* are dashes "-"
|
||||
*/
|
||||
if ( msr->mpd->buf_contains_line
|
||||
&& (strlen(msr->mpd->buf) > strlen(msr->mpd->boundary) + 2)
|
||||
&& (((*(msr->mpd->buf) == '-'))&&(*(msr->mpd->buf + 1) == '-'))
|
||||
&& (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) ) {
|
||||
|
||||
char *boundary_end = msr->mpd->buf + 2 + strlen(msr->mpd->boundary);
|
||||
|
||||
if ( (*boundary_end == '\r')
|
||||
&&(*(boundary_end + 1) == '\n')
|
||||
&&(*(boundary_end + 2) == '\0')
|
||||
) {
|
||||
/* simple boundary */
|
||||
if (multipart_process_boundary(msr, 0, error_msg) < 0) return -1;
|
||||
}
|
||||
else
|
||||
if ( (*boundary_end == '-')
|
||||
&&(*(boundary_end + 1) == '-')
|
||||
&&(*(boundary_end + 2) == '\r')
|
||||
&&(*(boundary_end + 3) == '\n')
|
||||
&&(*(boundary_end + 4) == '\0')
|
||||
) {
|
||||
/* final boundary */
|
||||
msr->mpd->is_complete = 1;
|
||||
if (multipart_process_boundary(msr, 1, error_msg) < 0) return -1;
|
||||
}
|
||||
else {
|
||||
/* error */
|
||||
*error_msg = apr_psprintf(msr->mp,
|
||||
"Multipart: Invalid boundary detected: %s",
|
||||
log_escape_nq(msr->mp, msr->mpd->buf));
|
||||
return -1;
|
||||
/* Do we have something that looks like a boundary? */
|
||||
if (msr->mpd->buf_contains_line
|
||||
&& (strlen(msr->mpd->buf) > 3)
|
||||
&& (((*(msr->mpd->buf) == '-'))&&(*(msr->mpd->buf + 1) == '-')) )
|
||||
{
|
||||
/* Does it match our boundary? */
|
||||
if ((strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 2)
|
||||
&& (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) )
|
||||
{
|
||||
char *boundary_end = msr->mpd->buf + 2 + strlen(msr->mpd->boundary);
|
||||
int is_final = 0;
|
||||
|
||||
/* Is this the final boundary? */
|
||||
if ((*boundary_end == '-')&&(*(boundary_end + 1)== '-')) {
|
||||
is_final = 1;
|
||||
boundary_end += 2;
|
||||
|
||||
if (msr->mpd->is_complete != 0) {
|
||||
msr->mpd->flag_error = 1;
|
||||
*error_msg = apr_psprintf(msr->mp,
|
||||
"Multipart: Invalid boundary (final duplicate).");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allow for CRLF and LF line endings. */
|
||||
if ( ( (*boundary_end == '\r')
|
||||
&& (*(boundary_end + 1) == '\n')
|
||||
&& (*(boundary_end + 2) == '\0') )
|
||||
|| ( (*boundary_end == '\n')
|
||||
&& (*(boundary_end + 1) == '\0') ) )
|
||||
{
|
||||
if (*boundary_end == '\n') {
|
||||
msr->mpd->flag_lf_line = 1;
|
||||
}
|
||||
|
||||
if (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) {
|
||||
msr->mpd->flag_error = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_final) {
|
||||
msr->mpd->is_complete = 1;
|
||||
}
|
||||
|
||||
processed_as_boundary = 1;
|
||||
}
|
||||
else {
|
||||
/* error */
|
||||
msr->mpd->flag_error = 1;
|
||||
*error_msg = apr_psprintf(msr->mp,
|
||||
"Multipart: Invalid boundary: %s",
|
||||
log_escape_nq(msr->mp, msr->mpd->buf));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if ( (msr->mpd->flag_boundary_quoted)
|
||||
&& (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 3)
|
||||
&& (((*(msr->mpd->buf) == '-'))&&(*(msr->mpd->buf + 1) == '-'))
|
||||
&& (*(msr->mpd->buf + 2) == '"')
|
||||
&& (strncmp(msr->mpd->buf + 3, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0)
|
||||
) {
|
||||
msr->mpd->flag_error = 1;
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary (quotes).");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msr->mpd->flag_unmatched_boundary = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
/* Process as data if it was not a boundary. */
|
||||
if (processed_as_boundary == 0) {
|
||||
if (msr->mpd->mpp == NULL) {
|
||||
msr->mpd->flag_data_before = 1;
|
||||
msr_log(msr, 4, "Multipart: Ignoring data before first boundary.");
|
||||
} else {
|
||||
if (msr->mpd->mpp_state == 0) {
|
||||
@@ -643,14 +754,22 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf,
|
||||
/* part header lines must be shorter than
|
||||
* MULTIPART_BUF_SIZE bytes
|
||||
*/
|
||||
msr->mpd->flag_error = 1;
|
||||
*error_msg = apr_psprintf(msr->mp,
|
||||
"Multipart: Part header line over %i bytes long",
|
||||
MULTIPART_BUF_SIZE);
|
||||
return -1;
|
||||
}
|
||||
if (multipart_process_part_header(msr, error_msg) < 0) return -1;
|
||||
|
||||
if (multipart_process_part_header(msr, error_msg) < 0) {
|
||||
msr->mpd->flag_error = 1;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (multipart_process_part_data(msr, error_msg) < 0) return -1;
|
||||
if (multipart_process_part_data(msr, error_msg) < 0) {
|
||||
msr->mpd->flag_error = 1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,6 +789,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf,
|
||||
}
|
||||
|
||||
if ((msr->mpd->is_complete)&&(inleft != 0)) {
|
||||
msr->mpd->flag_data_after = 1;
|
||||
msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%i bytes left)", inleft);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -97,6 +97,14 @@ struct multipart_data {
|
||||
|
||||
int seen_data;
|
||||
int is_complete;
|
||||
|
||||
int flag_error;
|
||||
int flag_data_before;
|
||||
int flag_data_after;
|
||||
int flag_header_folding;
|
||||
int flag_boundary_quoted;
|
||||
int flag_lf_line;
|
||||
int flag_unmatched_boundary;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
|
||||
if (multipart_process_chunk(msr, data, length, &my_error_msg) < 0) {
|
||||
msr->msc_reqbody_error = 1;
|
||||
msr->msc_reqbody_error_msg = my_error_msg;
|
||||
msr_log(msr, 4, "%s", my_error_msg);
|
||||
msr_log(msr, 1, "Request body processor error: %s", my_error_msg);
|
||||
}
|
||||
}
|
||||
#ifdef WITH_LIBXML2
|
||||
@@ -249,7 +249,7 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
|
||||
if (xml_process_chunk(msr, data, length, &my_error_msg) < 0) {
|
||||
msr->msc_reqbody_error = 1;
|
||||
msr->msc_reqbody_error_msg = my_error_msg;
|
||||
msr_log(msr, 4, "%s", my_error_msg);
|
||||
msr_log(msr, 1, "Request body processor error: %s", my_error_msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1651,6 +1651,99 @@ static int var_webappid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule
|
||||
return var_simple_generate(var, vartab, mptmp, value);
|
||||
}
|
||||
|
||||
/* MULTIPART_BOUNDARY_QUOTED */
|
||||
|
||||
static int var_multipart_boundary_quoted_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_boundary_quoted != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_DATA_AFTER */
|
||||
|
||||
static int var_multipart_data_after_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_data_after != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_DATA_BEFORE */
|
||||
|
||||
static int var_multipart_data_before_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_data_before != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_HEADER_FOLDING */
|
||||
|
||||
static int var_multipart_header_folding_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_header_folding != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_LF_LINE */
|
||||
|
||||
static int var_multipart_lf_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_lf_line != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_STRICT_ERROR */
|
||||
|
||||
static int var_multipart_strict_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if (msr->mpd != NULL) {
|
||||
/* Respond positive if at least one of the multipart flags is raised. */
|
||||
if ( (msr->mpd->flag_error)
|
||||
||(msr->mpd->flag_boundary_quoted != 0)
|
||||
||(msr->mpd->flag_data_before != 0)
|
||||
||(msr->mpd->flag_data_after != 0)
|
||||
||(msr->mpd->flag_header_folding != 0)
|
||||
||(msr->mpd->flag_lf_line != 0)
|
||||
) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
}
|
||||
}
|
||||
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
|
||||
/* MULTIPART_UNMATCHED_BOUNDARY */
|
||||
|
||||
static int var_multipart_unmatched_boundary_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_unmatched_boundary != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
|
||||
/**
|
||||
@@ -2431,4 +2524,81 @@ void msre_engine_register_default_variables(msre_engine *engine) {
|
||||
VAR_DONT_CACHE,
|
||||
PHASE_RESPONSE_HEADERS
|
||||
);
|
||||
|
||||
/* MULTIPART_BOUNDARY_QUOTED */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_BOUNDARY_QUOTED",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_boundary_quoted_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_DATA_AFTER */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_DATA_AFTER",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_data_after_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_DATA_BEFORE */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_DATA_BEFORE",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_data_before_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_HEADER_FOLDING */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_HEADER_FOLDING",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_header_folding_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_LF_LINE */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_LF_LINE",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_lf_line_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_STRICT_ERROR */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_STRICT_ERROR",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_strict_error_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_UNMATCHED_BOUNDARY */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_UNMATCHED_BOUNDARY",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_unmatched_boundary_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user