static int hook_request_late()

in apache2/mod_security2.c [919:1129]


static int hook_request_late(request_rec *r) {
    char *my_error_msg = NULL;
    modsec_rec *msr = NULL;
    int rc;

    /* This function needs to run only once per transaction
     * (i.e. subrequests and redirects are excluded).
     */
    if ((r->main != NULL)||(r->prev != NULL)) {
        return DECLINED;
    }

    /* Find the transaction context and make sure
     * we are supposed to proceed.
     */
    msr = retrieve_tx_context(r);
    if (msr == NULL) {
        /* If we can't find the context that probably means it's
         * a subrequest that was not initiated from the outside.
         */
        return DECLINED;
    }

    /* Has this phase been completed already? */
    if (msr->phase_request_body_complete) {
        msr_log(msr, 1, "Internal Error: Attempted to process the request body more than once.");
        return DECLINED;
    }

    msr->phase_request_body_complete = 1;
    msr->remote_user = r->user;

    /* Get the second configuration context. */
    msr->dcfg2 = (directory_config *)ap_get_module_config(r->per_dir_config,
            &security2_module);

    /* Create a transaction context. */
    msr->txcfg = create_directory_config(msr->mp, NULL);
    if (msr->txcfg == NULL) return DECLINED;
    if (msr->dcfg2 != NULL) {
        msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg2);
        if (msr->txcfg == NULL) return DECLINED;
    }

    /* Update with the explicit user settings. */
    msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->usercfg);

    init_directory_config(msr->txcfg);

    if (msr->txcfg->is_enabled == 0) {
        if (msr->txcfg->debuglog_level >= 4) {
            msr_log(msr, 4, "Processing disabled, skipping (hook request_late).");
        }
        return DECLINED;
    }

#ifndef REQUEST_EARLY
    /* Phase 1 */
    if (msr->txcfg->debuglog_level >= 4) {
        msr_log(msr, 4, "First phase starting (dcfg %pp).", msr->dcfg2);
    }

    /* Process phase REQUEST_HEADERS */
    if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) {
        /* There was a match; see if we need to intercept. */
        rc = perform_interception(msr);
        if (rc != DECLINED) {
            /* Intercepted */
            return rc;
        }
    }
#endif

    /* The rule engine could have been disabled in phase 1. */
    if (msr->txcfg->is_enabled == MODSEC_DISABLED) {
        if (msr->txcfg->debuglog_level >= 4) {
            msr_log(msr, 4, "Skipping phase 2 as the rule engine was disabled by a rule in phase 1.");
        }

        return DECLINED;
    }

    /* Phase 2 */
    if (msr->txcfg->debuglog_level >= 4) {
        msr_log(msr, 4, "Second phase starting (dcfg %pp).", msr->dcfg2);
    }

    /* Check that the request body is not too long, but only
     * if configuration allows for request body access.
     */
    msr->inbound_error = 0;
    if (msr->txcfg->reqbody_access == 1) {
        /* Check request body limit (non-chunked requests only). */
        if (msr->request_content_length > msr->txcfg->reqbody_limit) {

            if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) {
                msr->inbound_error = 1;
                msr_log(msr, 1, "Request body (Content-Length) is larger than the "
                        "configured limit (%ld). Deny with status (%d)", msr->txcfg->reqbody_limit, HTTP_REQUEST_ENTITY_TOO_LARGE);
                return HTTP_REQUEST_ENTITY_TOO_LARGE;
            } else if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){
                msr->inbound_error = 1;
                msr_log(msr, 1, "Request body (Content-Length) 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)){
                msr_log(msr, 1, "Request body (Content-Length) is larger than the "
                        "configured limit (%ld).", msr->txcfg->reqbody_limit);
                msr->inbound_error = 1;
            } else  {
                msr_log(msr, 1, "Request body (Content-Length) is larger than the "
                        "configured limit (%ld).", msr->txcfg->reqbody_limit);
                msr->inbound_error = 1;
            }
        }
    }

    /* Figure out whether to extract multipart files. */
    if ((msr->txcfg->upload_keep_files != KEEP_FILES_OFF) /* user might want to keep them */
            || (msr->txcfg->upload_validates_files)) /* user might want to validate them */
    {
        msr->upload_extract_files = 1;
        msr->upload_remove_files = 1;
    }

    rc = read_request_body(msr, &my_error_msg);
    if (rc < 0 && msr->txcfg->is_enabled == MODSEC_ENABLED) {
        switch(rc) {
            case -1 :
                if (my_error_msg != NULL) {
                    msr_log(msr, 1, "%s", my_error_msg);
                }
                return HTTP_INTERNAL_SERVER_ERROR;
                break;
            case -4 : /* Timeout. */
                if (my_error_msg != NULL) {
                    msr_log(msr, 4, "%s", my_error_msg);
                }
                r->connection->keepalive = AP_CONN_CLOSE;
                return HTTP_REQUEST_TIME_OUT;
                break;
            case -5 : /* Request body limit reached. */
                msr->inbound_error = 1;
                if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT))    {
                    r->connection->keepalive = AP_CONN_CLOSE;
                    if (my_error_msg != NULL) {
                        msr_log(msr, 1, "%s. Deny with code (%d)", my_error_msg, HTTP_REQUEST_ENTITY_TOO_LARGE);
                    }
                    return HTTP_REQUEST_ENTITY_TOO_LARGE;
                } else  {
                    if (my_error_msg != NULL) {
                        msr_log(msr, 1, "%s", my_error_msg);
                    }
                }
                break;
            case -6 : /* EOF when reading request body. */
                if (my_error_msg != NULL) {
                    msr_log(msr, 4, "%s", my_error_msg);
                }
                r->connection->keepalive = AP_CONN_CLOSE;
                return HTTP_BAD_REQUEST;
                break;
            case -7 : /* Partial recieved */
                if (my_error_msg != NULL) {
                    msr_log(msr, 4, "%s", my_error_msg);
                }
                r->connection->keepalive = AP_CONN_CLOSE;
                return HTTP_BAD_REQUEST;
                break;
            default :
                /* allow through */
                break;
        }

        msr->msc_reqbody_error = 1;
        msr->msc_reqbody_error_msg = my_error_msg;
    }

    /* Update the request headers. They might have changed after
     * the body was read (trailers).
     *
     * TODO We might still want to hold onto the original headers
     *      so that we can log them. Keeping them is probably not
     *      going to increase our memory requirements (because all
     *      headers are allocated from the request memory pool
     *      anyway).
     */
    msr->request_headers = apr_table_copy(msr->mp, r->headers_in);

    /* Process phase REQUEST_BODY */
    rc = DECLINED;
    if (modsecurity_process_phase(msr, PHASE_REQUEST_BODY) > 0) {
        rc = perform_interception(msr);
    }

    if(msr->txcfg->stream_inbody_inspection && msr->msc_reqbody_read)    {
        const char *clen = NULL;
        clen = apr_psprintf(msr->mp,"%"APR_SIZE_T_FMT,msr->stream_input_length);
        if(clen)
            apr_table_setn(r->headers_in, "Content-Length",clen);
    }

    /* Remove the compression ability indications the client set,
     * but only if we need to disable backend compression.
     */
    if (msr->txcfg->disable_backend_compression) {
        apr_table_unset(r->headers_in, "Accept-Encoding");
        apr_table_unset(r->headers_in, "TE");
    }

    return rc;
}