apr_status_t read_request_body()

in apache2/apache2_io.c [169:348]


apr_status_t read_request_body(modsec_rec *msr, char **error_msg) {
    request_rec *r = msr->r;
    unsigned int finished_reading;
    apr_bucket_brigade *bb_in;
    apr_bucket *bucket;

    if (error_msg == NULL) return -1;
    *error_msg = NULL;

    if (msr->reqbody_should_exist != 1) {
        if (msr->txcfg->debuglog_level >= 4) {
            msr_log(msr, 4, "Input filter: This request does not have a body.");
        }
        return 0;
    }

    if (msr->txcfg->reqbody_access != 1) {
        if (msr->txcfg->debuglog_level >= 4) {
            msr_log(msr, 4, "Input filter: Request body access not enabled.");
        }
        return 0;
    }

    if (msr->txcfg->debuglog_level >= 4) {
        msr_log(msr, 4, "Input filter: Reading request body.");
    }
    if (modsecurity_request_body_start(msr, error_msg) < 0) {
        return -1;
    }

    finished_reading = 0;
    msr->if_seen_eos = 0;
    bb_in = apr_brigade_create(msr->mp, r->connection->bucket_alloc);
    if (bb_in == NULL) return -1;
    do {
        apr_status_t rc;

        rc = ap_get_brigade(r->input_filters, bb_in, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
        if (rc != APR_SUCCESS) {
            /* NOTE Apache returns AP_FILTER_ERROR here when the request is
             *      too large and APR_EGENERAL when the client disconnects.
             */
            switch(rc) {
                case APR_INCOMPLETE :
                    *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc));
                    return -7;
                case APR_EOF :
                    *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc));
                    return -6;
                case APR_TIMEUP :
                    *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc));
                    return -4;
                case AP_FILTER_ERROR :
                    *error_msg = apr_psprintf(msr->mp, "Error reading request body: HTTP Error 413 - Request entity too large. (Most likely.)");
                    return -3;
                case APR_EGENERAL :
                    *error_msg = apr_psprintf(msr->mp, "Error reading request body: Client went away.");
                    return -2;
                default :
                    *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc));
                    return -1;
            }
        }

        /* Loop through the buckets in the brigade in order
         * to extract the size of the data available.
         */
        for(bucket = APR_BRIGADE_FIRST(bb_in);
                bucket != APR_BRIGADE_SENTINEL(bb_in);
                bucket = APR_BUCKET_NEXT(bucket))
        {
            const char *buf;
            apr_size_t buflen;

            rc = apr_bucket_read(bucket, &buf, &buflen, APR_BLOCK_READ);
            if (rc != APR_SUCCESS) {
                *error_msg = apr_psprintf(msr->mp, "Failed reading input / bucket (%d): %s", rc, get_apr_error(msr->mp, rc));
                return -1;
            }

            if (msr->txcfg->debuglog_level >= 9) {
                msr_log(msr, 9, "Input filter: Bucket type %s contains %" APR_SIZE_T_FMT " bytes.",
                        bucket->type->name, buflen);
            }

            /* Check request body limit (should only trigger on chunked requests). */
            if (msr->reqbody_length + buflen > (apr_size_t)msr->txcfg->reqbody_limit) {
                if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) {
                    *error_msg = apr_psprintf(msr->mp, "Request body is larger than the "
                            "configured limit (%ld).", msr->txcfg->reqbody_limit);
                    return -5;
                } else if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)) {

                    *error_msg = apr_psprintf(msr->mp, "Request body is larger than the "
                            "configured limit (%ld).", msr->txcfg->reqbody_limit);

                } else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){

                    *error_msg = apr_psprintf(msr->mp, "Request body is larger than the "
                            "configured limit (%ld).", msr->txcfg->reqbody_limit);

                } else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)){

                    *error_msg = apr_psprintf(msr->mp, "Request body is larger than the "
                            "configured limit (%ld).", msr->txcfg->reqbody_limit);

                } else  {

                    *error_msg = apr_psprintf(msr->mp, "Request body is larger than the "
                            "configured limit (%ld).", msr->txcfg->reqbody_limit);

                    return -5;
                }
            }

            if (msr->txcfg->stream_inbody_inspection == 1)   {
#ifndef MSC_LARGE_STREAM_INPUT
                msr->stream_input_length+=buflen;
                modsecurity_request_body_to_stream(msr, buf, buflen, error_msg);
#else
                if (modsecurity_request_body_to_stream(msr, buf, buflen, error_msg) < 0) {
                    return -1;
                }
#endif
            }

            msr->reqbody_length += buflen;

            if (buflen != 0) {
                int rcbs = modsecurity_request_body_store(msr, buf, buflen, error_msg);

                if (msr->reqbody_length > (apr_size_t)msr->txcfg->reqbody_limit && msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL) {
                    finished_reading = 1;
                }

                if (rcbs < 0) {
                    if (rcbs == -5) {
                        if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) {
                            *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the "
                                    "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit);
                            return -5;
                        } else if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)) {
                            *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the "
                                    "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit);
                        } else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)) {
                            *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the "
                                    "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit);
                        } else {
                            *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the "
                                    "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit);
                            return -5;
                        }
                    }

                    if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT))
                        return -1;
                }

            }

            if (APR_BUCKET_IS_EOS(bucket)) {
                finished_reading = 1;
                msr->if_seen_eos = 1;
            }
        }

        apr_brigade_cleanup(bb_in);
    } while(!finished_reading);

    apr_status_t rcbe = modsecurity_request_body_end(msr, error_msg);

    if (msr->txcfg->debuglog_level >= 4) {
        msr_log(msr, 4, "Input filter: Completed receiving request body (length %" APR_SIZE_T_FMT ").",
                msr->reqbody_length);
    }

    msr->if_status = IF_STATUS_WANTS_TO_RUN;

    return rcbe;
}