Blame apache2/msc_remote_rules.c

Packit Service 384592
/*
Packit Service 384592
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
Packit Service 384592
* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
Packit Service 384592
*
Packit Service 384592
* You may not use this file except in compliance with
Packit Service 384592
* the License.  You may obtain a copy of the License at
Packit Service 384592
*
Packit Service 384592
*     http://www.apache.org/licenses/LICENSE-2.0
Packit Service 384592
*
Packit Service 384592
* If any of the files related to licensing are missing or if you have any
Packit Service 384592
* other questions related to licensing please contact Trustwave Holdings, Inc.
Packit Service 384592
* directly using the email address security@modsecurity.org.
Packit Service 384592
*/
Packit Service 384592
Packit Service 384592
#include "msc_remote_rules.h"
Packit Service 384592
#include "msc_status_engine.h"
Packit Service 384592
Packit Service 384592
#include <apr_thread_pool.h>
Packit Service 384592
Packit Service 384592
#ifdef WITH_CURL
Packit Service 384592
#include <curl/curl.h>
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
#include <apu.h>
Packit Service 384592
Packit Service 384592
#ifdef WITH_REMOTE_RULES
Packit Service 384592
#ifdef WITH_APU_CRYPTO
Packit Service 384592
#include <apr_crypto.h>
Packit Service 384592
#endif
Packit Service 384592
#include <apr_sha1.h>
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
#ifndef AP_MAX_ARGC
Packit Service 384592
#define AP_MAX_ARGC 64
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 * @brief Find command in a list.
Packit Service 384592
 *
Packit Service 384592
 * This is a duplicate of `ap_find_command', which is part of the standalone module.
Packit Service 384592
 * Apache versions does not include the standalone, thus, this is necessary for
Packit Service 384592
 * the Apache versions. Once it is here it may not be necessary to be part of
Packit Service 384592
 * the standalone module, but, for this version both function will co-exist
Packit Service 384592
 * avoiding problems with 3rd parties that are extending the standalone module.
Packit Service 384592
 *
Packit Service 384592
 * @note Prefer this function instead of `ap_finc_command` which is part of the
Packit Service 384592
 *       standalone module.
Packit Service 384592
 *
Packit Service 384592
 * @param parms char pointer, function name.
Packit Service 384592
 * @param cmds pointer to command_rec[].
Packit Service 384592
 * @retval NULL if command was not found.
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
const command_rec *msc_remote_find_command(const char *name, const command_rec *cmds)
Packit Service 384592
{
Packit Service 384592
    while (cmds->name) {
Packit Service 384592
        if (!strcasecmp(name, cmds->name))
Packit Service 384592
            return cmds;
Packit Service 384592
Packit Service 384592
        ++cmds;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    return NULL;
Packit Service 384592
}
Packit Service 384592
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 * @brief Insert a new SecRule to be processed by ModSecurity
Packit Service 384592
 *
Packit Service 384592
 * This is a duplicate of `invoke_cmd', which is part of the standalone module.
Packit Service 384592
 * Apache versions does not include the standalone, thus, this is necessary for
Packit Service 384592
 * the Apache versions. Once it is here it may not be necessary to be part of
Packit Service 384592
 * the standalone module, but, for this version both function will co-exist
Packit Service 384592
 * avoiding problems with 3rd parties that are extending the standalone module.
Packit Service 384592
 *
Packit Service 384592
 * @note Prefer this function instead of `invoke_cmd` which is part of the
Packit Service 384592
 *       standalone module.
Packit Service 384592
 *
Packit Service 384592
 * @param cmd pointer to command_rec structure.
Packit Service 384592
 * @param parms pointer to cmd_params strucutre.
Packit Service 384592
 * @param mconfig pointer to main config structure.
Packit Service 384592
 * @param args SecRule arguments.
Packit Service 384592
 * @retval NULL if everything worked as expected otherwise an error message.
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
const char *msc_remote_invoke_cmd(const command_rec *cmd, cmd_parms *parms,
Packit Service 384592
                              void *mconfig, const char *args)
Packit Service 384592
{
Packit Service 384592
    char *w, *w2, *w3;
Packit Service 384592
    const char *errmsg = NULL;
Packit Service 384592
Packit Service 384592
    if ((parms->override & cmd->req_override) == 0)
Packit Service 384592
        return apr_pstrcat(parms->pool, cmd->name, " not allowed here", NULL);
Packit Service 384592
Packit Service 384592
    parms->info = cmd->cmd_data;
Packit Service 384592
    parms->cmd = cmd;
Packit Service 384592
Packit Service 384592
    switch (cmd->args_how) {
Packit Service 384592
    case RAW_ARGS:
Packit Service 384592
#ifdef RESOLVE_ENV_PER_TOKEN
Packit Service 384592
        args = ap_resolve_env(parms->pool,args);
Packit Service 384592
#endif
Packit Service 384592
        return cmd->AP_RAW_ARGS(parms, mconfig, args);
Packit Service 384592
Packit Service 384592
    case TAKE_ARGV:
Packit Service 384592
        {
Packit Service 384592
            char *argv[AP_MAX_ARGC];
Packit Service 384592
            int argc = 0;
Packit Service 384592
Packit Service 384592
            do {
Packit Service 384592
                w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
                if (*w == '\0' && *args == '\0') {
Packit Service 384592
                    break;
Packit Service 384592
                }
Packit Service 384592
                argv[argc] = w;
Packit Service 384592
                argc++;
Packit Service 384592
            } while (argc < AP_MAX_ARGC && *args != '\0');
Packit Service 384592
Packit Service 384592
            return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv);
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
    case NO_ARGS:
Packit Service 384592
        if (*args != 0)
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name, " takes no arguments",
Packit Service 384592
                               NULL);
Packit Service 384592
Packit Service 384592
        return cmd->AP_NO_ARGS(parms, mconfig);
Packit Service 384592
    case TAKE1:
Packit Service 384592
        w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
Packit Service 384592
        if (*w == '\0' || *args != 0)
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name, " takes one argument",
Packit Service 384592
                               cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
Packit Service 384592
Packit Service 384592
        return cmd->AP_TAKE1(parms, mconfig, w);
Packit Service 384592
    case TAKE2:
Packit Service 384592
        w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
        w2 = ap_getword_conf(parms->pool, &args);
Packit Service 384592
Packit Service 384592
        if (*w == '\0' || *w2 == '\0' || *args != 0)
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name, " takes two arguments",
Packit Service 384592
                               cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
Packit Service 384592
Packit Service 384592
        return cmd->AP_TAKE2(parms, mconfig, w, w2);
Packit Service 384592
    case TAKE12:
Packit Service 384592
        w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
        w2 = ap_getword_conf(parms->pool, &args);
Packit Service 384592
Packit Service 384592
        if (*w == '\0' || *args != 0)
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments",
Packit Service 384592
                               cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
Packit Service 384592
Packit Service 384592
        return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL);
Packit Service 384592
    case TAKE3:
Packit Service 384592
        w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
        w2 = ap_getword_conf(parms->pool, &args);
Packit Service 384592
        w3 = ap_getword_conf(parms->pool, &args);
Packit Service 384592
Packit Service 384592
        if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0)
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name,
Packit Service 384592
                    " takes three arguments",
Packit Service 384592
                    cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
Packit Service 384592
Packit Service 384592
        return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
Packit Service 384592
    case TAKE23:
Packit Service 384592
Packit Service 384592
        w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
        w2 = ap_getword_conf(parms->pool, &args);
Packit Service 384592
        w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
Packit Service 384592
Packit Service 384592
        if (*w == '\0' || *w2 == '\0' || *args != 0)
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name,
Packit Service 384592
                               " takes two or three arguments",
Packit Service 384592
                               cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
Packit Service 384592
Packit Service 384592
        return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
Packit Service 384592
    case TAKE123:
Packit Service 384592
        w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
        w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
Packit Service 384592
        w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
Packit Service 384592
Packit Service 384592
        if (*w == '\0' || *args != 0)
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name,
Packit Service 384592
                               " takes one, two or three arguments",
Packit Service 384592
                               cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
Packit Service 384592
Packit Service 384592
        return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
Packit Service 384592
    case TAKE13:
Packit Service 384592
        w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
        w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
Packit Service 384592
        w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
Packit Service 384592
Packit Service 384592
        if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0)
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name,
Packit Service 384592
                               " takes one or three arguments",
Packit Service 384592
                               cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
Packit Service 384592
Packit Service 384592
        return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
Packit Service 384592
    case ITERATE:
Packit Service 384592
        while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') {
Packit Service 384592
Packit Service 384592
            errmsg = cmd->AP_TAKE1(parms, mconfig, w);
Packit Service 384592
Packit Service 384592
            if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
Packit Service 384592
                return errmsg;
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        return errmsg;
Packit Service 384592
    case ITERATE2:
Packit Service 384592
        w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
Packit Service 384592
        if (*w == '\0' || *args == 0)
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name,
Packit Service 384592
                               " requires at least two arguments",
Packit Service 384592
                               cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
Packit Service 384592
Packit Service 384592
        while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') {
Packit Service 384592
Packit Service 384592
            errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2);
Packit Service 384592
Packit Service 384592
            if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
Packit Service 384592
                return errmsg;
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        return errmsg;
Packit Service 384592
    case FLAG:
Packit Service 384592
        w = ap_getword_conf(parms->pool, &args);
Packit Service 384592
Packit Service 384592
        if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off")))
Packit Service 384592
            return apr_pstrcat(parms->pool, cmd->name, " must be On or Off",
Packit Service 384592
                               NULL);
Packit Service 384592
Packit Service 384592
        return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0);
Packit Service 384592
    default:
Packit Service 384592
        return apr_pstrcat(parms->pool, cmd->name,
Packit Service 384592
                           " is improperly configured internally (server bug)",
Packit Service 384592
                           NULL);
Packit Service 384592
    }
Packit Service 384592
}
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 * @brief Fetch an URL and fill the content into a memory buffer.
Packit Service 384592
 *
Packit Service 384592
 * Fill an msc_curl_memory_buffer_t structure with the content of an given
Packit Service 384592
 * URL.
Packit Service 384592
 *
Packit Service 384592
 * @note While fetching the content, it will present the ModSecurity instance
Packit Service 384592
 *       to the remote server, trough: ModSecurity Unique ID, ModSecurity
Packit Service 384592
 *       status line and also, if given, key that can be used to
Packit Service 384592
 *       authentication. Such data is presented in the following HTTP headers:
Packit Service 384592
 *         - ModSec-status
Packit Service 384592
 *         - ModSec-unique-id
Packit Service 384592
 *         - ModSec-key
Packit Service 384592
 *
Packit Service 384592
 * @warning Cleanup the memory after use it.
Packit Service 384592
 *
Packit Service 384592
 * @param mp pointer to the memory pool.
Packit Service 384592
 * @param uri URI to be fetched.
Packit Service 384592
 * @param key KEY to be present as ModSec-key.
Packit Service 384592
 * @param chunk pointer to an msc_curl_memory_buffer_t struct.
Packit Service 384592
 * @param error_msg pointer an char pointer, filled is something went wrong.
Packit Service 384592
 *
Packit Service 384592
 * @retval n>=0 everything went fine.
Packit Service 384592
 * @retval n<-1 Something wrong happened, further details on error_msg.
Packit Service 384592
 *         n=-2 Download failed, but operation should not be aborted.
Packit Service 384592
 *         n=-3 ModSecurity was not compiled with curl support.
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
int msc_remote_download_content(apr_pool_t *mp, const char *uri, const char *key,
Packit Service 384592
    struct msc_curl_memory_buffer_t *chunk, char **error_msg)
Packit Service 384592
{
Packit Service 384592
#ifdef WITH_CURL
Packit Service 384592
    CURL *curl;
Packit Service 384592
    CURLcode res;
Packit Service 384592
Packit Service 384592
    char id[(APR_SHA1_DIGESTSIZE*2) + 1];
Packit Service 384592
    char *apr_id = NULL;
Packit Service 384592
    char *beacon_str = NULL;
Packit Service 384592
    char *beacon_apr = NULL;
Packit Service 384592
    int beacon_str_len = 0;
Packit Service 384592
    int ret = 0;
Packit Service 384592
Packit Service 384592
    chunk->size = 0;
Packit Service 384592
Packit Service 384592
    memset(id, '\0', sizeof(id));
Packit Service 384592
    if (msc_status_engine_unique_id(id))
Packit Service 384592
    {
Packit Service 384592
        sprintf(id, "no unique id");
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    apr_id = apr_psprintf(mp, "ModSec-unique-id: %s", id);
Packit Service 384592
Packit Service 384592
    curl = curl_easy_init();
Packit Service 384592
Packit Service 384592
    beacon_str_len = msc_beacon_string(NULL, 0);
Packit Service 384592
Packit Service 384592
    beacon_str = malloc(sizeof(char) * beacon_str_len + 1);
Packit Service 384592
    if (beacon_str == NULL)
Packit Service 384592
    {
Packit Service 384592
        beacon_str = "Failed to retrieve beacon string";
Packit Service 384592
        beacon_apr = apr_psprintf(mp, "ModSec-status: %s", beacon_str);
Packit Service 384592
    }
Packit Service 384592
    else
Packit Service 384592
    {
Packit Service 384592
        msc_beacon_string(beacon_str, beacon_str_len);
Packit Service 384592
        beacon_apr = apr_psprintf(mp, "ModSec-status: %s", beacon_str);
Packit Service 384592
        free(beacon_str);
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    if (curl)
Packit Service 384592
    {
Packit Service 384592
        struct curl_slist *headers_chunk = NULL;
Packit Service 384592
#ifdef WIN32
Packit Service 384592
        char *buf = malloc(sizeof(TCHAR) * (2048 + 1));
Packit Service 384592
        char *ptr = NULL;
Packit Service 384592
        DWORD res_len;
Packit Service 384592
#endif
Packit Service 384592
        curl_easy_setopt(curl, CURLOPT_URL, uri);
Packit Service 384592
Packit Service 384592
        headers_chunk = curl_slist_append(headers_chunk, apr_id);
Packit Service 384592
        headers_chunk = curl_slist_append(headers_chunk, beacon_apr);
Packit Service 384592
        if (key != NULL)
Packit Service 384592
        {
Packit Service 384592
            char *header_key = NULL;
Packit Service 384592
            header_key = apr_psprintf(mp, "ModSec-key: %s", key);
Packit Service 384592
            headers_chunk = curl_slist_append(headers_chunk, header_key);
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        /* Make it TLS 1.x only. */
Packit Service 384592
        curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
Packit Service 384592
Packit Service 384592
#ifdef WIN32
Packit Service 384592
        res_len = SearchPathA(NULL, "curl-ca-bundle.crt", NULL, (2048 + 1), buf, &ptr);
Packit Service 384592
        if (res_len > 0) {
Packit Service 384592
            curl_easy_setopt(curl, CURLOPT_CAINFO, strdup(buf));
Packit Service 384592
        }
Packit Service 384592
        free(buf);
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
        /* those are the default options, but lets make sure */
Packit Service 384592
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
Packit Service 384592
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
Packit Service 384592
Packit Service 384592
        /* send all data to this function  */
Packit Service 384592
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, msc_curl_write_memory_cb);
Packit Service 384592
Packit Service 384592
        /* we pass our 'chunk' struct to the callback function */
Packit Service 384592
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk);
Packit Service 384592
Packit Service 384592
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "modesecurity");
Packit Service 384592
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_chunk);
Packit Service 384592
Packit Service 384592
        /* We want Curl to return error in case there is an HTTP error code */
Packit Service 384592
        curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
Packit Service 384592
Packit Service 34f31f
        /* In case we want different timeout than a default one */
Packit Service 34f31f
        if (remote_rules_timeout != NOT_SET){
Packit Service 34f31f
            curl_easy_setopt(curl, CURLOPT_TIMEOUT, remote_rules_timeout);
Packit Service 34f31f
        }
Packit Service 34f31f
Packit Service 384592
        res = curl_easy_perform(curl);
Packit Service 384592
Packit Service 384592
        if (res != CURLE_OK)
Packit Service 384592
        {
Packit Service 384592
            if (remote_rules_fail_action == REMOTE_RULES_WARN_ON_FAIL)
Packit Service 384592
            {
Packit Service 384592
                if (remote_rules_fail_message == NULL)
Packit Service 384592
                {
Packit Service 384592
                    remote_rules_fail_message = "";
Packit Service 384592
                }
Packit Service 384592
Packit Service 384592
                remote_rules_fail_message = apr_psprintf(mp, "%sFailed to " \
Packit Service 384592
                            "download: \"%s\" error: %s. ",
Packit Service 384592
                            remote_rules_fail_message, uri,
Packit Service 384592
                            curl_easy_strerror(res));
Packit Service 384592
Packit Service 384592
                ret = -2;
Packit Service 384592
                goto failed;
Packit Service 384592
            }
Packit Service 384592
            else
Packit Service 384592
            {
Packit Service 384592
                *error_msg = apr_psprintf(mp, "Failed to download: \"%s\" " \
Packit Service 384592
                    "error: %s ",
Packit Service 384592
                    uri, curl_easy_strerror(res));
Packit Service 384592
Packit Service 384592
                ret = -1;
Packit Service 384592
                goto failed;
Packit Service 384592
            }
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        curl_slist_free_all(headers_chunk);
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
failed:
Packit Service 384592
    curl_easy_cleanup(curl);
Packit Service 384592
Packit Service 384592
    return ret;
Packit Service 384592
#else
Packit Service 384592
    return -3;
Packit Service 384592
#endif
Packit Service 384592
}
Packit Service 384592
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 * @brief Setup an apr_crypto_key_t from a given password and salt.
Packit Service 384592
 *
Packit Service 384592
 * apr_crypto_* demands the key to be in a format of an apr_crypto_key_t which
Packit Service 384592
 * is an structure that may be defined depending on the crypto provider, thus,
Packit Service 384592
 * making necessary for us to create this structure using apr internal
Packit Service 384592
 * functions.
Packit Service 384592
 *
Packit Service 384592
 * @warning We trust that the paramenter used in apr, such as the algorithm,
Packit Service 384592
 *          key size and other parameters won't change, if they do it may
Packit Service 384592
 *          break the interoperability with this function with others
Packit Service 384592
 *          implementations, as the key will end up with a different value
Packit Service 384592
 *          than the one expected.
Packit Service 384592
 *
Packit Service 384592
 * @param pool pointer to the memory pool to be used.
Packit Service 384592
 * @param key password to be used while creating the key.
Packit Service 384592
 * @param apr_key pointer to the pointer of an apr_crypto_key_t structure.
Packit Service 384592
 * @param f pointer to apr_crypto_t.
Packit Service 384592
 * @param salt string to be used as salt of the key generation
Packit Service 384592
 * @param error_msg pointer to char pointer, which is filled if something
Packit Service 384592
 *        went wrong.
Packit Service 384592
 *
Packit Service 384592
 * @retval n>=0 everything went fine.
Packit Service 384592
 * @retval n<-1 Something wrong happened, check error_msg for further details.
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
#ifdef WITH_APU_CRYPTO
Packit Service 384592
int msc_remote_enc_key_setup(apr_pool_t *pool,
Packit Service 384592
    const char *key,
Packit Service 384592
    apr_crypto_key_t **apr_key,
Packit Service 384592
    apr_crypto_t *f,
Packit Service 384592
    unsigned char *salt,
Packit Service 384592
    char **error_msg)
Packit Service 384592
{
Packit Service 384592
    apr_size_t key_len = strlen(key);
Packit Service 384592
    apr_size_t salt_len = 16; //FIXME: salt_len should not be hard coded.
Packit Service 384592
Packit Service 384592
    const int do_pad = 1;
Packit Service 384592
    apr_status_t rv;
Packit Service 384592
Packit Service 384592
    rv = apr_crypto_passphrase(
Packit Service 384592
            (apr_crypto_key_t **) apr_key,
Packit Service 384592
            NULL,
Packit Service 384592
            (const char *) key,
Packit Service 384592
            (apr_size_t) key_len,
Packit Service 384592
            (const unsigned char *) salt,
Packit Service 384592
            (apr_size_t) salt_len,
Packit Service 384592
            APR_KEY_AES_256,
Packit Service 384592
            APR_MODE_CBC,
Packit Service 384592
            (const int) do_pad,
Packit Service 384592
            (const int) 4096,
Packit Service 384592
            (const apr_crypto_t *) f,
Packit Service 384592
            (apr_pool_t *) pool);
Packit Service 384592
Packit Service 384592
    if (rv == APR_ENOKEY)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_passphrase: Missing key";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
    else if (rv == APR_EPADDING)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_passphrase: APR_EPADDING";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
    else if (rv == APR_EKEYTYPE)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_passphrase: APR_EKEYTYPE";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
    else if (rv != APR_SUCCESS)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_passphrase: Unknown error";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    return 0;
Packit Service 384592
}
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 * @brief Decrypt an buffer into a memory buffer.
Packit Service 384592
 *
Packit Service 384592
 * Decrypt an msc_curl_memory_buffer_t structure into another
Packit Service 384592
 * msc_curl_memory_buffer_t.
Packit Service 384592
 *
Packit Service 384592
 * Using the key provided, it creates and apr_key and uses it to decript the
Packit Service 384592
 * content provided on chunk. The plain text content is saved under
Packit Service 384592
 * chunk_plain.
Packit Service 384592
 *
Packit Service 384592
 * @warning Cleanup memory after usage.
Packit Service 384592
 *
Packit Service 384592
 * @param pool pointer to the memory pool to be used.
Packit Service 384592
 * @param key pointer to the char array to be used as the key.
Packit Service 384592
 * @param chunk msc_curl_memory_buffer_t that contains the encrypted content.
Packit Service 384592
 * @param plain_text unsigned char which will hold the content of an url
Packit Service 384592
 * @param plain_text_len size of the plain_text buffer
Packit Service 384592
 * @param error_msg pointer to char pointer that is filled if something went
Packit Service 384592
 *        wrong.
Packit Service 384592
 *
Packit Service 384592
 * @retval n>=0 everything went fine.
Packit Service 384592
 * @retval n<-1 Something wrong happened, further details on error_msg.
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
#ifdef WITH_APU_CRYPTO
Packit Service 384592
int msc_remote_decrypt(apr_pool_t *pool,
Packit Service 384592
        const char *key,
Packit Service 384592
        struct msc_curl_memory_buffer_t *chunk,
Packit Service 384592
        unsigned char **plain_text,
Packit Service 384592
        apr_size_t *plain_text_len,
Packit Service 384592
        char **error_msg)
Packit Service 384592
{
Packit Service 384592
    apr_crypto_key_t *apr_key = NULL;
Packit Service 384592
    apr_crypto_t *f = NULL;
Packit Service 384592
    const apr_crypto_driver_t *driver = NULL;
Packit Service 384592
    const apu_err_t *err = NULL;
Packit Service 384592
    apr_status_t rv;
Packit Service 384592
    unsigned char *iv = NULL;
Packit Service 384592
    unsigned char *ciphered_text = NULL;
Packit Service 384592
    unsigned char *salt = NULL;
Packit Service 384592
Packit Service 384592
    apr_crypto_block_t *block = NULL;
Packit Service 384592
    apr_size_t block_size = 0;
Packit Service 384592
    apr_size_t len = 0;
Packit Service 384592
Packit Service 384592
    // FIXME: size should not be hardcoded.
Packit Service 384592
    //        at least size of IV + Salt
Packit Service 384592
    if (chunk->size < 16+16+1)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Failed to download rules from a remote server: " \
Packit Service 384592
            "Unexpected content.";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
    iv = chunk->memory;
Packit Service 384592
    salt = chunk->memory + 16;
Packit Service 384592
    ciphered_text = chunk->memory + (16 + 16);
Packit Service 384592
Packit Service 384592
    rv = apr_crypto_init(pool);
Packit Service 384592
    if (rv != APR_SUCCESS)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error: failed to init crypto";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    rv = apr_crypto_get_driver(&driver, APU_CRYPTO_RECOMMENDED_DRIVER, NULL,
Packit Service 384592
            &err, pool);
Packit Service 384592
Packit Service 384592
    if (rv != APR_SUCCESS || driver == NULL)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_get_driver: Unknown error";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    rv = apr_crypto_make(&f, driver, NULL, pool);
Packit Service 384592
    if (rv != APR_SUCCESS)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_make: Unknown error";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    msc_remote_enc_key_setup(pool, key, &apr_key, f, salt, error_msg);
Packit Service 384592
    if (*error_msg != NULL)
Packit Service 384592
    {
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    rv = apr_crypto_block_decrypt_init(&block, &block_size, iv, apr_key, pool);
Packit Service 384592
    if (rv == APR_ENOKEY)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
Packit Service 384592
            "Missing key";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
    else if (rv == APR_ENOIV)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
Packit Service 384592
            "Missing IV";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
    else if (rv == APR_EKEYTYPE)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
Packit Service 384592
            "Wrong key type";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
    else if (rv == APR_EKEYLENGTH)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
Packit Service 384592
            "Wrong key length";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
    else if (rv != APR_SUCCESS)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
Packit Service 384592
            "Unknown error";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    //FIXME: size should not be hardcoded like that.
Packit Service 384592
    //       32 = iv + salt size.
Packit Service 384592
    rv = apr_crypto_block_decrypt(plain_text, plain_text_len,
Packit Service 384592
        ciphered_text, (apr_size_t) chunk->size - (16 + 16) ,
Packit Service 384592
        block);
Packit Service 384592
Packit Service 384592
    if (rv != APR_SUCCESS)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_block_decrypt: Failed to " \
Packit Service 384592
            "decrypt";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* finalise the decryption */
Packit Service 384592
Packit Service 384592
    rv = apr_crypto_block_decrypt_finish(*plain_text + *plain_text_len, &len,
Packit Service 384592
            block);
Packit Service 384592
    if (rv != APR_SUCCESS)
Packit Service 384592
    {
Packit Service 384592
        *error_msg = "Internal error - apr_crypto_block_decrypt_finish: " \
Packit Service 384592
            "Failed to decrypt";
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    apr_crypto_block_cleanup(block);
Packit Service 384592
    apr_crypto_cleanup(f);
Packit Service 384592
Packit Service 384592
    // Shutdown the apr_crypto seems to be the correct thing to do.
Packit Service 384592
    // However it seems to add instability especially if mod_ssl is enabled.
Packit Service 384592
    // apr_crypto_shutdown(driver);
Packit Service 384592
Packit Service 384592
    return 0;
Packit Service 384592
}
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 * @brief Add SecRules from a given URI.
Packit Service 384592
 *
Packit Service 384592
 * Fetch the URI and using the key provided into params decrypt and install
Packit Service 384592
 * the downloaded set of rules.
Packit Service 384592
 *
Packit Service 384592
 * @warning Cleanup the memory may be necessary.
Packit Service 384592
 *
Packit Service 384592
 * @param orig_parms origin parms used at SecRemoteRule
Packit Service 384592
 * @param remote_rules_server pointer to the filled msc_remote_rules_server
Packit Service 384592
 *          structure.
Packit Service 384592
 * @param error_msg pointer to char pointer that will be filled if something
Packit Service 384592
 *          went wrong.
Packit Service 384592
 *
Packit Service 384592
 *
Packit Service 384592
 * @retval n>=0 everything went fine.
Packit Service 384592
 * @retval n<-1 Something wrong happened, further details on error_msg.
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
int msc_remote_add_rules_from_uri(cmd_parms *orig_parms,
Packit Service 384592
        msc_remote_rules_server *remote_rules_server,
Packit Service 384592
        char **error_msg)
Packit Service 384592
{
Packit Service 384592
Packit Service 384592
#ifdef WITH_REMOTE_RULES
Packit Service 384592
    struct msc_curl_memory_buffer_t downloaded_content;
Packit Service 384592
    unsigned char *plain_text = NULL;
Packit Service 384592
    int len = 0;
Packit Service 384592
    int start = 0;
Packit Service 384592
    int end = 0;
Packit Service 384592
    int added_rules = 0;
Packit Service 384592
    int res = 0;
Packit Service 384592
    apr_size_t plain_text_len = 0;
Packit Service 384592
Packit Service 384592
    apr_pool_t *mp = orig_parms->pool;
Packit Service 384592
Packit Service 384592
    downloaded_content.size = 0;
Packit Service 384592
    downloaded_content.memory = NULL;
Packit Service 384592
Packit Service 384592
    res = msc_remote_download_content(mp, remote_rules_server->uri,
Packit Service 384592
            remote_rules_server->key, &downloaded_content, error_msg);
Packit Service 384592
    if (*error_msg != NULL)
Packit Service 384592
    {
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
    /* error_msg is not filled when the user set SecRemoteRulesFailAction
Packit Service 384592
     * to warn
Packit Service 384592
     */
Packit Service 384592
    if (res != 0)
Packit Service 384592
    {
Packit Service 384592
        return res;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    if (remote_rules_server->crypto == 1)
Packit Service 384592
    {
Packit Service 384592
#ifdef WITH_APU_CRYPTO
Packit Service 384592
        msc_remote_decrypt(mp, remote_rules_server->key, &downloaded_content,
Packit Service 384592
            &plain_text,
Packit Service 384592
            &plain_text_len,
Packit Service 384592
            error_msg);
Packit Service 384592
Packit Service 384592
        if (*error_msg != NULL)
Packit Service 384592
        {
Packit Service 384592
            msc_remote_clean_chunk(&downloaded_content);
Packit Service 384592
            return -1;
Packit Service 384592
        }
Packit Service 384592
#else
Packit Service 384592
        *error_msg = "ModSecurity was not compiled with crypto support.\n";
Packit Service 384592
        msc_remote_clean_chunk(&downloaded_content);
Packit Service 384592
        return -1;
Packit Service 384592
#endif
Packit Service 384592
        msc_remote_clean_chunk(&downloaded_content);
Packit Service 384592
    }
Packit Service 384592
    else
Packit Service 384592
    {
Packit Service 384592
        plain_text = downloaded_content.memory;
Packit Service 384592
        plain_text_len = strlen(plain_text);
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    len = 0;
Packit Service 384592
    plain_text_len = strlen(plain_text);
Packit Service 384592
    while (len < plain_text_len)
Packit Service 384592
    {
Packit Service 384592
        if (plain_text[len]  == '\n')
Packit Service 384592
        {
Packit Service 384592
            const char *rule = NULL;
Packit Service 384592
            int tmp = len;
Packit Service 384592
            char *cmd_name = NULL;
Packit Service 384592
            char *word = NULL;
Packit Service 384592
            const command_rec *cmd;
Packit Service 384592
Packit Service 384592
            ap_directive_t *newdir;
Packit Service 384592
            cmd_parms *parms = apr_pcalloc(mp, sizeof (cmd_parms));
Packit Service 384592
Packit Service 384592
            rule = plain_text + start;
Packit Service 384592
            end = len;
Packit Service 384592
            plain_text[len] = '\0';
Packit Service 384592
 
Packit Service 384592
            memcpy(parms, orig_parms, sizeof(cmd_parms));
Packit Service 384592
Packit Service 384592
            if (*rule == '#' || *rule == '\0')
Packit Service 384592
            {
Packit Service 384592
               goto next;
Packit Service 384592
            }
Packit Service 384592
Packit Service 384592
            cmd_name = ap_getword_conf(mp, &rule;;
Packit Service 384592
            cmd = msc_remote_find_command(cmd_name, security2_module.cmds);
Packit Service 384592
Packit Service 384592
            if (cmd == NULL)
Packit Service 384592
            {
Packit Service 384592
                *error_msg = apr_pstrcat(mp, "Unknown command in config: ",
Packit Service 384592
                        cmd_name, NULL);
Packit Service 384592
                return -1;
Packit Service 384592
            }
Packit Service 384592
Packit Service 384592
            newdir = apr_pcalloc(mp, sizeof(ap_directive_t));
Packit Service 384592
            newdir->filename = "remote server";
Packit Service 384592
            newdir->line_num = -1;
Packit Service 384592
            newdir->directive = cmd_name;
Packit Service 384592
            newdir->args = apr_pstrdup(mp, rule);
Packit Service 384592
            parms->directive = newdir;
Packit Service 384592
Packit Service 384592
#ifdef WIN32
Packit Service 384592
            // some config commands fail in APR when there are file
Packit Service 384592
            // permission issues or other OS-specific problems
Packit Service 384592
            //
Packit Service 384592
            __try
Packit Service 384592
            {
Packit Service 384592
#endif
Packit Service 384592
                *error_msg = (char *) msc_remote_invoke_cmd(cmd, parms,
Packit Service 384592
                        remote_rules_server->context, rule);
Packit Service 384592
                if (*error_msg != NULL)
Packit Service 384592
                {
Packit Service 384592
                    return -1;
Packit Service 384592
                }
Packit Service 384592
Packit Service 384592
                added_rules++;
Packit Service 384592
#ifdef WIN32
Packit Service 384592
            }
Packit Service 384592
            __except(EXCEPTION_EXECUTE_HANDLER)
Packit Service 384592
            {
Packit Service 384592
                *error_msg = "Command failed to execute (check file/folder" \
Packit Service 384592
                    "permissions, syntax, etc.).";
Packit Service 384592
                return -1;
Packit Service 384592
            }
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
next:
Packit Service 384592
            start = end + 1;
Packit Service 384592
        }
Packit Service 384592
        len++;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    remote_rules_server->amount_of_rules = added_rules;
Packit Service 384592
Packit Service 384592
    if (remote_rules_server->crypto != 1)
Packit Service 384592
    {
Packit Service 384592
        msc_remote_clean_chunk(&downloaded_content);
Packit Service 384592
    }
Packit Service 384592
#else
Packit Service 384592
    *error_msg = "SecRemoteRules was not enabled during ModSecurity " \
Packit Service 384592
        "compilation.";
Packit Service 384592
    return -1;
Packit Service 384592
#endif
Packit Service 384592
}
Packit Service 384592
Packit Service 384592
Packit Service 384592
int msc_remote_clean_chunk(struct msc_curl_memory_buffer_t *chunk)
Packit Service 384592
{
Packit Service 384592
    if (chunk->size == 0)
Packit Service 384592
    {
Packit Service 384592
        goto end;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    if (chunk->memory == NULL)
Packit Service 384592
    {
Packit Service 384592
        goto end;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    free(chunk->memory);
Packit Service 384592
    chunk->size = 0;
Packit Service 384592
Packit Service 384592
end:
Packit Service 384592
    return 0;
Packit Service 384592
}
Packit Service 384592