mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-14 05:45:59 +03:00
Initial support to load rules from a remote server
New directive `SecRemoteRules' was added. It allows the user to load a set of rules from a given HTTP server.
This commit is contained in:
parent
899ee0c365
commit
9b836b652a
@ -21,6 +21,7 @@ mod_security2_la_SOURCES = acmp.c \
|
||||
msc_parsers.c \
|
||||
msc_pcre.c \
|
||||
msc_release.c \
|
||||
msc_remote_rules.c \
|
||||
msc_reqbody.c \
|
||||
msc_tree.c \
|
||||
msc_unicode.c \
|
||||
|
@ -58,6 +58,7 @@ OBJS = mod_security2.obj apache2_config.obj apache2_io.obj apache2_util.obj \
|
||||
msc_reqbody.obj msc_geo.obj msc_gsb.obj msc_crypt.obj msc_tree.obj msc_unicode.obj acmp.obj msc_lua.obj \
|
||||
msc_release.obj \
|
||||
msc_status_engine.obj \
|
||||
msc_remote_rules.obj \
|
||||
msc_json.obj \
|
||||
libinjection/libinjection_html5.obj \
|
||||
libinjection/libinjection_sqli.obj \
|
||||
|
@ -2213,6 +2213,50 @@ static const char *cmd_rule_engine(cmd_parms *cmd, void *_dcfg, const char *p1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *cmd_remote_rules(cmd_parms *cmd, void *_dcfg, const char *p1,
|
||||
const char *p2)
|
||||
{
|
||||
char *error_msg = NULL;
|
||||
directory_config *dcfg = (directory_config *)_dcfg;
|
||||
if (dcfg == NULL) return NULL;
|
||||
|
||||
// FIXME: make it https only.
|
||||
// if (strncasecmp(p1, "https", 5) != 0) {
|
||||
if (strncasecmp(p2, "http", 4) != 0) {
|
||||
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
|
||||
" %s, expected an HTTPS address.", p2);
|
||||
}
|
||||
|
||||
// FIXME: Should we handle more then one server at once?
|
||||
if (remote_rules_server != NULL)
|
||||
{
|
||||
return apr_psprintf(cmd->pool, "ModSecurity: " \
|
||||
"SecRemoteRules cannot be used more than once.");
|
||||
}
|
||||
|
||||
remote_rules_server = apr_pcalloc(cmd->pool, sizeof(msc_remote_rules_server));
|
||||
if (remote_rules_server == NULL)
|
||||
{
|
||||
return apr_psprintf(cmd->pool, "ModSecurity: " \
|
||||
"SecRemoteRules: Internal failure. Not enougth memory.");
|
||||
}
|
||||
|
||||
remote_rules_server->context = dcfg;
|
||||
remote_rules_server->context_label = apr_pstrdup(cmd->pool, "Unkwon context");
|
||||
remote_rules_server->key = p1;
|
||||
remote_rules_server->uri = p2;
|
||||
remote_rules_server->amount_of_rules = 0;
|
||||
|
||||
msc_remote_add_rules_from_uri(cmd, remote_rules_server, &error_msg);
|
||||
if (error_msg != NULL)
|
||||
{
|
||||
return error_msg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char *cmd_status_engine(cmd_parms *cmd, void *_dcfg, const char *p1)
|
||||
{
|
||||
if (strcasecmp(p1, "on") == 0) {
|
||||
@ -3500,6 +3544,14 @@ const command_rec module_directives[] = {
|
||||
"On or Off"
|
||||
),
|
||||
|
||||
AP_INIT_TAKE2 (
|
||||
"SecRemoteRules",
|
||||
cmd_remote_rules,
|
||||
NULL,
|
||||
CMD_SCOPE_ANY,
|
||||
"key and URI to the remote rules"
|
||||
),
|
||||
|
||||
AP_INIT_TAKE1 (
|
||||
"SecXmlExternalEntity",
|
||||
cmd_xml_external_entity,
|
||||
|
@ -33,6 +33,8 @@
|
||||
|
||||
#include "apr_version.h"
|
||||
|
||||
#include "msc_remote_rules.h"
|
||||
|
||||
#if defined(WITH_LUA)
|
||||
#include "msc_lua.h"
|
||||
#endif
|
||||
@ -66,6 +68,8 @@ unsigned long int DSOLOCAL msc_pcre_match_limit = 0;
|
||||
|
||||
unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0;
|
||||
|
||||
msc_remote_rules_server DSOLOCAL *remote_rules_server = NULL;
|
||||
|
||||
int DSOLOCAL status_engine_state = STATUS_ENGINE_DISABLED;
|
||||
|
||||
int DSOLOCAL conn_limits_filter_state = MODSEC_DISABLED;
|
||||
@ -752,6 +756,24 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t
|
||||
"SecStatusEngine to On.");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (remote_rules_server != NULL)
|
||||
{
|
||||
if (remote_rules_server->amount_of_rules == 1)
|
||||
{
|
||||
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
|
||||
"ModSecurity: Loaded %d rule from: '%s'.",
|
||||
remote_rules_server->amount_of_rules,
|
||||
remote_rules_server->uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
|
||||
"ModSecurity: Loaded %d rule from: '%s'.",
|
||||
remote_rules_server->amount_of_rules,
|
||||
remote_rules_server->uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srand((unsigned int)(time(NULL) * getpid()));
|
||||
|
@ -536,7 +536,7 @@ static apr_status_t modsecurity_process_phase_request_body(modsec_rec *msr) {
|
||||
apr_time_t time_before;
|
||||
apr_status_t rc = 0;
|
||||
|
||||
|
||||
|
||||
if ((msr->allow_scope == ACTION_ALLOW_REQUEST)||(msr->allow_scope == ACTION_ALLOW)) {
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "Skipping phase REQUEST_BODY (allow used).");
|
||||
@ -626,7 +626,7 @@ static apr_status_t modsecurity_process_phase_response_body(modsec_rec *msr) {
|
||||
*/
|
||||
static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) {
|
||||
apr_time_t time_before, time_after;
|
||||
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "Starting phase LOGGING.");
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ typedef struct msc_arg msc_arg;
|
||||
typedef struct msc_string msc_string;
|
||||
typedef struct msc_parm msc_parm;
|
||||
|
||||
#include "msc_remote_rules.h"
|
||||
#include "msc_release.h"
|
||||
#include "msc_logging.h"
|
||||
#include "msc_multipart.h"
|
||||
@ -144,6 +145,8 @@ extern DSOLOCAL unsigned long int msc_pcre_match_limit;
|
||||
|
||||
extern DSOLOCAL unsigned long int msc_pcre_match_limit_recursion;
|
||||
|
||||
extern DSOLOCAL msc_remote_rules_server *remote_rules_server;
|
||||
|
||||
extern DSOLOCAL int status_engine_state;
|
||||
|
||||
extern DSOLOCAL int conn_limits_filter_state;
|
||||
@ -619,6 +622,14 @@ struct directory_config {
|
||||
|
||||
/* xml */
|
||||
int xml_external_entity;
|
||||
|
||||
/* This will be used whenever ModSecurity will be ready
|
||||
* to ask the server for newer rules.
|
||||
*/
|
||||
#if 0
|
||||
msc_remote_rules_server *remote_rules;
|
||||
int remote_timeout;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct error_message_t {
|
||||
|
697
apache2/msc_remote_rules.c
Normal file
697
apache2/msc_remote_rules.c
Normal file
@ -0,0 +1,697 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* If any of the files related to licensing are missing or if you have any
|
||||
* other questions related to licensing please contact Trustwave Holdings, Inc.
|
||||
* directly using the email address security@modsecurity.org.
|
||||
*/
|
||||
|
||||
#include "msc_remote_rules.h"
|
||||
#include "msc_status_engine.h"
|
||||
|
||||
#include <apr_thread_pool.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <apu.h>
|
||||
#include <apr_crypto.h>
|
||||
#include <apr_sha1.h>
|
||||
|
||||
#ifndef AP_MAX_ARGC
|
||||
#define AP_MAX_ARGC 64
|
||||
#endif
|
||||
|
||||
#ifndef APU_HAVE_CRYPTO
|
||||
#error Missing apu crypto module
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Insert a new SecRule to be processed by ModSecurity
|
||||
*
|
||||
* This is a duplicate of `invoke_cmd', which is part of the standalone module.
|
||||
* Apache versions does not include the standalone, thus, this is necessary for
|
||||
* the Apache versions. Once it is here it may not be necessary to be part of
|
||||
* the standalone module, but, for this version both function will co-exist
|
||||
* avoiding problems with 3rd parties that are extending the standalone module.
|
||||
*
|
||||
* @note Prefer this function instead of `invoke_cmd` which is part of the
|
||||
* standalone module.
|
||||
*
|
||||
* @param cmd pointer to command_rec structure.
|
||||
* @param parms pointer to cmd_params strucutre.
|
||||
* @param mconfig pointer to main config structure.
|
||||
* @param args SecRule arguments.
|
||||
* @retval NULL if everything worked as expected otherwise an error message.
|
||||
*
|
||||
*/
|
||||
const char *msc_remote_invoke_cmd(const command_rec *cmd, cmd_parms *parms,
|
||||
void *mconfig, const char *args)
|
||||
{
|
||||
char *w, *w2, *w3;
|
||||
const char *errmsg = NULL;
|
||||
|
||||
if ((parms->override & cmd->req_override) == 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name, " not allowed here", NULL);
|
||||
|
||||
parms->info = cmd->cmd_data;
|
||||
parms->cmd = cmd;
|
||||
|
||||
switch (cmd->args_how) {
|
||||
case RAW_ARGS:
|
||||
#ifdef RESOLVE_ENV_PER_TOKEN
|
||||
args = ap_resolve_env(parms->pool,args);
|
||||
#endif
|
||||
return cmd->AP_RAW_ARGS(parms, mconfig, args);
|
||||
|
||||
case TAKE_ARGV:
|
||||
{
|
||||
char *argv[AP_MAX_ARGC];
|
||||
int argc = 0;
|
||||
|
||||
do {
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
if (*w == '\0' && *args == '\0') {
|
||||
break;
|
||||
}
|
||||
argv[argc] = w;
|
||||
argc++;
|
||||
} while (argc < AP_MAX_ARGC && *args != '\0');
|
||||
|
||||
return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv);
|
||||
}
|
||||
|
||||
case NO_ARGS:
|
||||
if (*args != 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name, " takes no arguments",
|
||||
NULL);
|
||||
|
||||
return cmd->AP_NO_ARGS(parms, mconfig);
|
||||
case TAKE1:
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
|
||||
if (*w == '\0' || *args != 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name, " takes one argument",
|
||||
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
|
||||
|
||||
return cmd->AP_TAKE1(parms, mconfig, w);
|
||||
case TAKE2:
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
w2 = ap_getword_conf(parms->pool, &args);
|
||||
|
||||
if (*w == '\0' || *w2 == '\0' || *args != 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name, " takes two arguments",
|
||||
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
|
||||
|
||||
return cmd->AP_TAKE2(parms, mconfig, w, w2);
|
||||
case TAKE12:
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
w2 = ap_getword_conf(parms->pool, &args);
|
||||
|
||||
if (*w == '\0' || *args != 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments",
|
||||
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
|
||||
|
||||
return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL);
|
||||
case TAKE3:
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
w2 = ap_getword_conf(parms->pool, &args);
|
||||
w3 = ap_getword_conf(parms->pool, &args);
|
||||
|
||||
if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name,
|
||||
" takes three arguments",
|
||||
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
|
||||
|
||||
return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
|
||||
case TAKE23:
|
||||
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
w2 = ap_getword_conf(parms->pool, &args);
|
||||
w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
|
||||
|
||||
if (*w == '\0' || *w2 == '\0' || *args != 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name,
|
||||
" takes two or three arguments",
|
||||
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
|
||||
|
||||
return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
|
||||
case TAKE123:
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
|
||||
w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
|
||||
|
||||
if (*w == '\0' || *args != 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name,
|
||||
" takes one, two or three arguments",
|
||||
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
|
||||
|
||||
return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
|
||||
case TAKE13:
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
|
||||
w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
|
||||
|
||||
if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name,
|
||||
" takes one or three arguments",
|
||||
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
|
||||
|
||||
return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
|
||||
case ITERATE:
|
||||
while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') {
|
||||
|
||||
errmsg = cmd->AP_TAKE1(parms, mconfig, w);
|
||||
|
||||
if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
return errmsg;
|
||||
case ITERATE2:
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
|
||||
if (*w == '\0' || *args == 0)
|
||||
return apr_pstrcat(parms->pool, cmd->name,
|
||||
" requires at least two arguments",
|
||||
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
|
||||
|
||||
while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') {
|
||||
|
||||
errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2);
|
||||
|
||||
if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
return errmsg;
|
||||
case FLAG:
|
||||
w = ap_getword_conf(parms->pool, &args);
|
||||
|
||||
if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off")))
|
||||
return apr_pstrcat(parms->pool, cmd->name, " must be On or Off",
|
||||
NULL);
|
||||
|
||||
return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0);
|
||||
default:
|
||||
return apr_pstrcat(parms->pool, cmd->name,
|
||||
" is improperly configured internally (server bug)",
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Fetch an URL and fill the content into a memory buffer.
|
||||
*
|
||||
* Fill an msc_curl_memory_buffer_t structure with the content of an given
|
||||
* URL.
|
||||
*
|
||||
* @note While fetching the content, it will present the ModSecurity instance
|
||||
* to the remote server, trough: ModSecurity Unique ID, ModSecurity
|
||||
* status line and also, if given, key that can be used to
|
||||
* authentication. Such data is presented in the following HTTP headers:
|
||||
* - ModSec-status
|
||||
* - ModSec-unique-id
|
||||
* - ModSec-key
|
||||
*
|
||||
* @warning Cleanup the memory after use it.
|
||||
*
|
||||
* @param mp pointer to the memory pool.
|
||||
* @param uri URI to be fetched.
|
||||
* @param key KEY to be present as ModSec-key.
|
||||
* @param chunk pointer to an msc_curl_memory_buffer_t struct.
|
||||
* @param error_msg pointer an char pointer, filled is something went wrong.
|
||||
*
|
||||
* @retval n>=0 everything went fine.
|
||||
* @retval n<-1 Something wrong happened, further details on error_msg.
|
||||
*
|
||||
*/
|
||||
int msc_remote_grab_content(apr_pool_t *mp, const char *uri, const char *key,
|
||||
struct msc_curl_memory_buffer_t *chunk, char **error_msg)
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
|
||||
char id[(APR_SHA1_DIGESTSIZE*2) + 1];
|
||||
char *apr_id = NULL;
|
||||
char *beacon_str = NULL;
|
||||
char *beacon_apr = NULL;
|
||||
char *header_key = NULL;
|
||||
int beacon_str_len = 0;
|
||||
|
||||
memset(id, '\0', sizeof(id));
|
||||
if (msc_status_engine_unique_id(id))
|
||||
{
|
||||
sprintf(id, "no unique id");
|
||||
}
|
||||
|
||||
apr_id = apr_psprintf(mp, "ModSec-unique-id: %s", id);
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
curl = curl_easy_init();
|
||||
|
||||
beacon_str_len = msc_beacon_string(NULL, 0);
|
||||
|
||||
beacon_str = malloc(sizeof(char) * beacon_str_len + 1);
|
||||
if (beacon_str == NULL)
|
||||
{
|
||||
beacon_str = "Failed to retrieve beacon string";
|
||||
beacon_apr = apr_psprintf(mp, "ModSec-status: %s", beacon_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
msc_beacon_string(beacon_str, beacon_str_len);
|
||||
beacon_apr = apr_psprintf(mp, "ModSec-status: %s", beacon_str);
|
||||
free(beacon_str);
|
||||
}
|
||||
|
||||
if (key != NULL)
|
||||
{
|
||||
header_key = apr_psprintf(mp, "ModSec-key: %s", key);
|
||||
}
|
||||
|
||||
if (curl)
|
||||
{
|
||||
struct curl_slist *headers_chunk = NULL;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, remote_rules_server->uri);
|
||||
|
||||
headers_chunk = curl_slist_append(headers_chunk, apr_id);
|
||||
headers_chunk = curl_slist_append(headers_chunk, beacon_apr);
|
||||
if (key != NULL)
|
||||
{
|
||||
headers_chunk = curl_slist_append(headers_chunk, header_key);
|
||||
}
|
||||
|
||||
/* send all data to this function */
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, msc_curl_write_memory_cb);
|
||||
|
||||
/* we pass our 'chunk' struct to the callback function */
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk);
|
||||
|
||||
/* some servers don't like requests that are made without a user-agent
|
||||
field, so we provide one */
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "modesecurity");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_chunk);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if (res != CURLE_OK)
|
||||
{
|
||||
*error_msg = apr_psprintf(mp, "Failed to fetch \"%s\" error: %s ",
|
||||
remote_rules_server->uri, curl_easy_strerror(res));
|
||||
return -1;
|
||||
}
|
||||
|
||||
curl_slist_free_all(headers_chunk);
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
curl_global_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup an apr_crypto_key_t from a given password and salt.
|
||||
*
|
||||
* apr_crypto_* demands the key to be in a format of an apr_crypto_key_t which
|
||||
* is an structure that may be defined depending on the crypto provider, thus,
|
||||
* making necessary for us to create this structure using apr internal
|
||||
* functions.
|
||||
*
|
||||
* @warning We trust that the paramenter used in apr, such as the algorithm,
|
||||
* key size and other parameters won't change, if they do it may
|
||||
* break the interoperability with this function with others
|
||||
* implementations, as the key will end up with a different value
|
||||
* than the one expected.
|
||||
*
|
||||
* @param pool pointer to the memory pool to be used.
|
||||
* @param key password to be used while creating the key.
|
||||
* @param apr_key pointer to the pointer of an apr_crypto_key_t structure.
|
||||
* @param f pointer to apr_crypto_t.
|
||||
* @param salt string to be used as salt of the key generation
|
||||
* @param error_msg pointer to char pointer, which is filled if something
|
||||
* went wrong.
|
||||
*
|
||||
* @retval n>=0 everything went fine.
|
||||
* @retval n<-1 Something wrong happened, check error_msg for further details.
|
||||
*
|
||||
*/
|
||||
int msc_remote_enc_key_setup(apr_pool_t *pool,
|
||||
const char *key,
|
||||
apr_crypto_key_t **apr_key,
|
||||
apr_crypto_t *f,
|
||||
unsigned char *salt,
|
||||
char **error_msg)
|
||||
{
|
||||
apr_size_t key_len = strlen(key);
|
||||
apr_size_t salt_len = 16; //FIXME: salt_len should not be hard coded.
|
||||
|
||||
const int do_pad = 1;
|
||||
apr_status_t rv;
|
||||
|
||||
rv = apr_crypto_passphrase(
|
||||
(apr_crypto_key_t **) apr_key,
|
||||
NULL,
|
||||
(const char *) key,
|
||||
(apr_size_t) key_len,
|
||||
(const unsigned char *) salt,
|
||||
(apr_size_t) salt_len,
|
||||
APR_KEY_AES_256,
|
||||
APR_MODE_CBC,
|
||||
(const int) do_pad,
|
||||
(const int) 4096,
|
||||
(const apr_crypto_t *) f,
|
||||
(apr_pool_t *) pool);
|
||||
|
||||
if (rv == APR_ENOKEY)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_passphrase: Missing key";
|
||||
return -1;
|
||||
}
|
||||
else if (rv == APR_EPADDING)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_passphrase: APR_EPADDING";
|
||||
return -1;
|
||||
}
|
||||
else if (rv == APR_EKEYTYPE)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_passphrase: APR_EKEYTYPE";
|
||||
return -1;
|
||||
}
|
||||
else if (rv == APR_EKEYTYPE)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_passphrase: APR_EKEYTYPE";
|
||||
return -1;
|
||||
}
|
||||
else if (rv != APR_SUCCESS)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_passphrase: Unknown error";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decrypt an buffer into a memory buffer.
|
||||
*
|
||||
* Decrypt an msc_curl_memory_buffer_t structure into another
|
||||
* msc_curl_memory_buffer_t.
|
||||
*
|
||||
* Using the key provided, it creates and apr_key and uses it to decript the
|
||||
* content provided on chunk. The plain text content is saved under
|
||||
* chunk_plain.
|
||||
*
|
||||
* @warning Cleanup memory after usage.
|
||||
*
|
||||
* @param pool pointer to the memory pool to be used.
|
||||
* @param key pointer to the char array to be used as the key.
|
||||
* @param chunk msc_curl_memory_buffer_t that contains the encrypted content.
|
||||
* @param plain_text unsigned char which will hold the content of an url
|
||||
* @param plain_text_len size of the plain_text buffer
|
||||
* @param error_msg pointer to char pointer that is filled if something went
|
||||
* wrong.
|
||||
*
|
||||
* @retval n>=0 everything went fine.
|
||||
* @retval n<-1 Something wrong happened, further details on error_msg.
|
||||
*
|
||||
*/
|
||||
int msc_remote_decrypt(apr_pool_t *pool,
|
||||
const char *key,
|
||||
struct msc_curl_memory_buffer_t *chunk,
|
||||
unsigned char **plain_text,
|
||||
apr_size_t *plain_text_len,
|
||||
char **error_msg)
|
||||
{
|
||||
apr_crypto_key_t *apr_key = NULL;
|
||||
apr_crypto_t *f = NULL;
|
||||
const apr_crypto_driver_t *driver = NULL;
|
||||
const apu_err_t *err = NULL;
|
||||
apr_status_t rv;
|
||||
unsigned char *iv = NULL;
|
||||
unsigned char *ciphered_text = NULL;
|
||||
unsigned char *salt = NULL;
|
||||
|
||||
apr_crypto_block_t *block = NULL;
|
||||
apr_size_t block_size = 0;
|
||||
apr_size_t len = 0;
|
||||
|
||||
// FIXME: size should not be hardcoded.
|
||||
// at least size of IV + Salt
|
||||
if (chunk->size < 16+16+1)
|
||||
{
|
||||
*error_msg = "Unexpected content.";
|
||||
return -1;
|
||||
}
|
||||
iv = chunk->memory;
|
||||
salt = chunk->memory + 16;
|
||||
ciphered_text = chunk->memory + (16 + 16);
|
||||
|
||||
rv = apr_crypto_init(pool);
|
||||
if (rv != APR_SUCCESS)
|
||||
{
|
||||
*error_msg = "Internal error: failed to init crypto";
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef APU_CRYPTO_RECOMMENDED_DRIVER
|
||||
rv = apr_crypto_get_driver(&driver, "openssl", NULL, &err, pool);
|
||||
#else
|
||||
rv = apr_crypto_get_driver(&driver, APU_CRYPTO_RECOMMENDED_DRIVER, NULL,
|
||||
&err, pool);
|
||||
#endif
|
||||
if (rv != APR_SUCCESS || driver == NULL)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_get_driver: Unknown error";
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = apr_crypto_make(&f, driver, NULL, pool);
|
||||
if (rv != APR_SUCCESS)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_make: Unknown error";
|
||||
return -1;
|
||||
}
|
||||
|
||||
msc_remote_enc_key_setup(pool, key, &apr_key, f, salt, error_msg);
|
||||
if (*error_msg != NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = apr_crypto_block_decrypt_init(&block, &block_size, iv, apr_key, pool);
|
||||
if (rv == APR_ENOKEY)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
|
||||
"Missing key";
|
||||
return -1;
|
||||
}
|
||||
else if (rv == APR_ENOIV)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
|
||||
"Missing IV";
|
||||
return -1;
|
||||
}
|
||||
else if (rv == APR_EKEYTYPE)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
|
||||
"Wrong key type";
|
||||
return -1;
|
||||
}
|
||||
else if (rv == APR_EKEYLENGTH)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
|
||||
"Wrong key length";
|
||||
return -1;
|
||||
}
|
||||
else if (rv != APR_SUCCESS)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
|
||||
"Unknown error";
|
||||
return -1;
|
||||
}
|
||||
|
||||
//FIXME: size should not be hardcoded like that.
|
||||
// 32 = iv + salt size.
|
||||
rv = apr_crypto_block_decrypt(plain_text, plain_text_len,
|
||||
ciphered_text, (apr_size_t) chunk->size - (16 + 16) ,
|
||||
block);
|
||||
|
||||
if (rv != APR_SUCCESS)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_block_decrypt: Failed to " \
|
||||
"decrypt";
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* finalise the decryption */
|
||||
|
||||
rv = apr_crypto_block_decrypt_finish(*plain_text + *plain_text_len, &len,
|
||||
block);
|
||||
if (rv != APR_SUCCESS)
|
||||
{
|
||||
*error_msg = "Internal error - apr_crypto_block_decrypt_finish: " \
|
||||
"Failed to decrypt";
|
||||
return -1;
|
||||
}
|
||||
|
||||
apr_crypto_block_cleanup(block);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add SecRules from a given URI.
|
||||
*
|
||||
* Fetch the URI and using the key provided into params decrypt and install
|
||||
* the downloaded set of rules.
|
||||
*
|
||||
* @warning Cleanup the memory may be necessary.
|
||||
*
|
||||
* @param orig_parms origin parms used at SecRemoteRule
|
||||
* @param remote_rules_server pointer to the filled msc_remote_rules_server
|
||||
* structure.
|
||||
* @param error_msg pointer to char pointer that will be filled if something
|
||||
* went wrong.
|
||||
*
|
||||
*
|
||||
* @retval n>=0 everything went fine.
|
||||
* @retval n<-1 Something wrong happened, further details on error_msg.
|
||||
*
|
||||
*/
|
||||
int msc_remote_add_rules_from_uri(cmd_parms *orig_parms,
|
||||
msc_remote_rules_server *remote_rules_server,
|
||||
char **error_msg)
|
||||
{
|
||||
struct msc_curl_memory_buffer_t chunk_encrypted;
|
||||
unsigned char *plain_text = NULL;
|
||||
int len = 0;
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
int added_rules = 0;
|
||||
apr_size_t plain_text_len = 0;
|
||||
|
||||
apr_pool_t *mp = orig_parms->pool;
|
||||
|
||||
chunk_encrypted.size = 0;
|
||||
chunk_encrypted.memory = NULL;
|
||||
|
||||
msc_remote_grab_content(mp, remote_rules_server->uri,
|
||||
remote_rules_server->key, &chunk_encrypted, error_msg);
|
||||
if (*error_msg != NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
msc_remote_decrypt(mp, remote_rules_server->key, &chunk_encrypted,
|
||||
&plain_text,
|
||||
&plain_text_len,
|
||||
error_msg);
|
||||
if (*error_msg != NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
msc_remote_clean_chunk(&chunk_encrypted);
|
||||
|
||||
len = 0;
|
||||
plain_text_len = strlen(plain_text);
|
||||
while (len < plain_text_len)
|
||||
{
|
||||
if (plain_text[len] == '\n')
|
||||
{
|
||||
const char *rule = NULL;
|
||||
int tmp = len;
|
||||
char *cmd_name;
|
||||
char *word;
|
||||
const command_rec *cmd;
|
||||
ap_directive_t *newdir;
|
||||
cmd_parms *parms = apr_pcalloc(mp, sizeof (cmd_parms));
|
||||
|
||||
rule = plain_text + start;
|
||||
end = len;
|
||||
plain_text[len] = '\0';
|
||||
|
||||
memcpy(parms, orig_parms, sizeof(cmd_parms));
|
||||
|
||||
if (*rule == '#' || *rule == '\0')
|
||||
{
|
||||
goto next;
|
||||
}
|
||||
|
||||
cmd_name = ap_getword_conf(mp, &rule);
|
||||
cmd = ap_find_command(cmd_name, security2_module.cmds);
|
||||
|
||||
if (cmd == NULL)
|
||||
{
|
||||
*error_msg = apr_pstrcat(mp, "Unknown command in config: ",
|
||||
cmd_name, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
newdir = apr_pcalloc(mp, sizeof(ap_directive_t));
|
||||
newdir->filename = "remote server";
|
||||
newdir->line_num = -1;
|
||||
newdir->directive = cmd_name;
|
||||
newdir->args = apr_pstrdup(mp, rule);
|
||||
parms->directive = newdir;
|
||||
|
||||
#ifdef WIN32
|
||||
// some config commands fail in APR when there are file
|
||||
// permission issues or other OS-specific problems
|
||||
//
|
||||
__try
|
||||
{
|
||||
#endif
|
||||
*error_msg = (char *) msc_remote_invoke_cmd(cmd, parms,
|
||||
remote_rules_server->context, rule);
|
||||
if (*error_msg != NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
added_rules++;
|
||||
#ifdef WIN32
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
error_msg = "Command failed to execute (check file/folder" \
|
||||
"permissions, syntax, etc.).";
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
next:
|
||||
start = end + 1;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
remote_rules_server->amount_of_rules = added_rules;
|
||||
}
|
||||
|
||||
|
||||
int msc_remote_clean_chunk(struct msc_curl_memory_buffer_t *chunk)
|
||||
{
|
||||
if (chunk->size <= 0)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (chunk->memory == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
free(chunk->memory);
|
||||
chunk->size = 0;
|
||||
|
||||
end:
|
||||
return 0;
|
||||
}
|
||||
|
67
apache2/msc_remote_rules.h
Normal file
67
apache2/msc_remote_rules.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* If any of the files related to licensing are missing or if you have any
|
||||
* other questions related to licensing please contact Trustwave Holdings, Inc.
|
||||
* directly using the email address security@modsecurity.org.
|
||||
*/
|
||||
|
||||
#ifndef MSC_REMOTE_RULES_H
|
||||
#define MSC_REMOTE_RULES_H
|
||||
|
||||
#include <apr_general.h>
|
||||
#include <apr_optional.h>
|
||||
#include <apr_thread_pool.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <apr_sha1.h>
|
||||
#include <apr_crypto.h>
|
||||
#include "http_core.h"
|
||||
|
||||
typedef struct msc_remote_rules_server msc_remote_rules_server;
|
||||
struct msc_curl_memory_buffer_t;
|
||||
|
||||
#include "modsecurity.h"
|
||||
|
||||
struct msc_remote_rules_server {
|
||||
directory_config *context;
|
||||
const char *context_label;
|
||||
const char *uri;
|
||||
const char *key;
|
||||
int amount_of_rules;
|
||||
};
|
||||
|
||||
const char *msc_remote_invoke_cmd(const command_rec *cmd, cmd_parms *parms,
|
||||
void *mconfig, const char *args);
|
||||
|
||||
int msc_remote_grab_content(apr_pool_t *mp, const char *uri, const char *key,
|
||||
struct msc_curl_memory_buffer_t *chunk, char **error_msg);
|
||||
|
||||
int msc_remote_enc_key_setup(apr_pool_t *pool,
|
||||
const char *key,
|
||||
apr_crypto_key_t **apr_key,
|
||||
apr_crypto_t *f,
|
||||
unsigned char *salt,
|
||||
char **error_msg);
|
||||
|
||||
int msc_remote_decrypt(apr_pool_t *pool,
|
||||
const char *key,
|
||||
struct msc_curl_memory_buffer_t *chunk,
|
||||
unsigned char **plain_text,
|
||||
apr_size_t *plain_text_len,
|
||||
char **error_msg);
|
||||
|
||||
int msc_remote_add_rules_from_uri(cmd_parms *orig_parms,
|
||||
msc_remote_rules_server *remote_rules_server,
|
||||
char **error_msg);
|
||||
|
||||
int msc_remote_clean_chunk(struct msc_curl_memory_buffer_t *chunk);
|
||||
|
||||
#endif
|
||||
|
@ -2634,12 +2634,12 @@ int ip_tree_from_uri(TreeRoot **rtree, char *uri,
|
||||
int beacon_str_len = 0;
|
||||
char *beacon_apr = NULL;
|
||||
struct msc_curl_memory_buffer_t chunk;
|
||||
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
|
||||
chunk.size = 0; /* no data at this point */
|
||||
char *word = NULL;
|
||||
char *brkt = NULL;
|
||||
char *sep = "\n";
|
||||
|
||||
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
|
||||
chunk.size = 0; /* no data at this point */
|
||||
|
||||
|
||||
if (create_radix_tree(mp, rtree, error_msg))
|
||||
@ -2834,7 +2834,17 @@ size_t msc_curl_write_memory_cb(void *contents, size_t size,
|
||||
size_t realsize = size * nmemb;
|
||||
struct msc_curl_memory_buffer_t *mem = (struct msc_curl_memory_buffer_t *)userp;
|
||||
|
||||
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
|
||||
if (mem->size == 0)
|
||||
{
|
||||
mem->memory = malloc(realsize + 1);
|
||||
memset(mem->memory, '\0', sizeof(realsize + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
|
||||
memset(mem->memory + mem->size, '\0', sizeof(realsize + 1));
|
||||
}
|
||||
|
||||
if(mem->memory == NULL) {
|
||||
/* out of memory! */
|
||||
return 0;
|
||||
@ -2847,3 +2857,31 @@ size_t msc_curl_write_memory_cb(void *contents, size_t size,
|
||||
return realsize;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
char* strtok_r(
|
||||
char *str,
|
||||
const char *delim,
|
||||
char **nextp)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
if (str == NULL)
|
||||
{
|
||||
str = *nextp;
|
||||
}
|
||||
str += strspn(str, delim);
|
||||
if (*str == '\0')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
ret = str;
|
||||
str += strcspn(str, delim);
|
||||
if (*str)
|
||||
{
|
||||
*str++ = '\0';
|
||||
}
|
||||
*nextp = str;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -170,4 +170,8 @@ struct msc_curl_memory_buffer_t
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
char *strtok_r(char *str, const char *delim, char **nextp);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -73,8 +73,9 @@ OBJS1 = mod_security2.obj apache2_config.obj apache2_io.obj apache2_util.obj \
|
||||
msc_reqbody.obj msc_geo.obj msc_gsb.obj msc_unicode.obj acmp.obj msc_lua.obj \
|
||||
msc_release.obj msc_crypt.obj msc_tree.obj \
|
||||
msc_status_engine.obj \
|
||||
msc_json.obj
|
||||
|
||||
msc_json.obj \
|
||||
msc_remote_rules.obj
|
||||
|
||||
OBJS2 = api.obj buckets.obj config.obj filters.obj hooks.obj regex.obj server.obj
|
||||
OBJS3 = main.obj moduleconfig.obj mymodule.obj
|
||||
OBJS4 = libinjection_html5.obj \
|
||||
|
@ -22,6 +22,7 @@ standalone_la_SOURCES = ../apache2/acmp.c \
|
||||
../apache2/msc_parsers.c \
|
||||
../apache2/msc_pcre.c \
|
||||
../apache2/msc_release.c \
|
||||
../apache2/msc_remote_rules.c \
|
||||
../apache2/msc_reqbody.c \
|
||||
../apache2/msc_tree.c \
|
||||
../apache2/msc_unicode.c \
|
||||
|
Loading…
x
Reference in New Issue
Block a user