mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-14 05:45:59 +03:00
Include data edition, sanitizematched and few fixes
This commit is contained in:
parent
37e8cba181
commit
7f52d86e4b
@ -104,6 +104,10 @@ void *create_directory_config(apr_pool_t *mp, char *path)
|
||||
/* Content injection. */
|
||||
dcfg->content_injection_enabled = NOT_SET;
|
||||
|
||||
/* Stream inspection */
|
||||
dcfg->stream_inbody_inspection = NOT_SET;
|
||||
dcfg->stream_outbody_inspection = NOT_SET;
|
||||
|
||||
/* Geo Lookups */
|
||||
dcfg->geo = NOT_SET_P;
|
||||
|
||||
@ -445,6 +449,12 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child)
|
||||
merged->content_injection_enabled = (child->content_injection_enabled == NOT_SET
|
||||
? parent->content_injection_enabled : child->content_injection_enabled);
|
||||
|
||||
/* Stream inspection */
|
||||
merged->stream_inbody_inspection = (child->stream_inbody_inspection == NOT_SET
|
||||
? parent->stream_inbody_inspection : child->stream_inbody_inspection);
|
||||
merged->stream_outbody_inspection = (child->stream_outbody_inspection == NOT_SET
|
||||
? parent->stream_outbody_inspection : child->stream_outbody_inspection);
|
||||
|
||||
/* Geo Lookup */
|
||||
merged->geo = (child->geo == NOT_SET_P
|
||||
? parent->geo : child->geo);
|
||||
@ -543,6 +553,10 @@ void init_directory_config(directory_config *dcfg)
|
||||
/* Content injection. */
|
||||
if (dcfg->content_injection_enabled == NOT_SET) dcfg->content_injection_enabled = 0;
|
||||
|
||||
/* Stream inspection */
|
||||
if (dcfg->stream_inbody_inspection == NOT_SET) dcfg->stream_inbody_inspection = 0;
|
||||
if (dcfg->stream_outbody_inspection == NOT_SET) dcfg->stream_outbody_inspection = 0;
|
||||
|
||||
/* Geo Lookup */
|
||||
if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL;
|
||||
|
||||
@ -1334,6 +1348,72 @@ static const char *cmd_guardian_log(cmd_parms *cmd, void *_dcfg,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* \brief Add StreamInBodyInspection configuration option
|
||||
*
|
||||
* \param cmd Pointer to configuration data
|
||||
* \param _dcfg Pointer to directory configuration
|
||||
* \param p1 Pointer to configuration option
|
||||
*
|
||||
* \retval NULL On failure
|
||||
* \retval apr_psprintf On Success
|
||||
*/
|
||||
static const char *cmd_stream_inbody_inspection(cmd_parms *cmd, void *_dcfg, int flag)
|
||||
{
|
||||
directory_config *dcfg = (directory_config *)_dcfg;
|
||||
if (dcfg == NULL) return NULL;
|
||||
dcfg->stream_inbody_inspection = flag;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* \brief Add StreamOutBodyInspection configuration option
|
||||
*
|
||||
* \param cmd Pointer to configuration data
|
||||
* \param _dcfg Pointer to directory configuration
|
||||
* \param p1 Pointer to configuration option
|
||||
*
|
||||
* \retval NULL On failure
|
||||
* \retval apr_psprintf On Success
|
||||
*/
|
||||
static const char *cmd_stream_outbody_inspection(cmd_parms *cmd, void *_dcfg, int flag)
|
||||
{
|
||||
directory_config *dcfg = (directory_config *)_dcfg;
|
||||
if (dcfg == NULL) return NULL;
|
||||
dcfg->stream_outbody_inspection = flag;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* \brief Add SecReadStateLimit configuration option
|
||||
*
|
||||
* \param cmd Pointer to configuration data
|
||||
* \param _dcfg Pointer to directory configuration
|
||||
* \param p1 Pointer to configuration option
|
||||
*
|
||||
* \retval NULL On failure
|
||||
* \retval apr_psprintf On Success
|
||||
*/
|
||||
static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg,
|
||||
const char *p1)
|
||||
{
|
||||
directory_config *dcfg = (directory_config *)_dcfg;
|
||||
long int limit;
|
||||
|
||||
if (dcfg == NULL) return NULL;
|
||||
|
||||
limit = strtol(p1, NULL, 10);
|
||||
if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
|
||||
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecReadStateLimit: %s", p1);
|
||||
}
|
||||
|
||||
conn_read_state_limit = limit;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg,
|
||||
const char *p1)
|
||||
{
|
||||
@ -2029,6 +2109,22 @@ const command_rec module_directives[] = {
|
||||
"On or Off"
|
||||
),
|
||||
|
||||
AP_INIT_FLAG (
|
||||
"StreamOutBodyInspection",
|
||||
cmd_stream_outbody_inspection,
|
||||
NULL,
|
||||
CMD_SCOPE_ANY,
|
||||
"On or Off"
|
||||
),
|
||||
|
||||
AP_INIT_FLAG (
|
||||
"StreamInBodyInspection",
|
||||
cmd_stream_inbody_inspection,
|
||||
NULL,
|
||||
CMD_SCOPE_ANY,
|
||||
"On or Off"
|
||||
),
|
||||
|
||||
AP_INIT_TAKE1 (
|
||||
"SecCookieFormat",
|
||||
cmd_cookie_format,
|
||||
@ -2134,6 +2230,14 @@ const command_rec module_directives[] = {
|
||||
"On or Off"
|
||||
),
|
||||
|
||||
AP_INIT_TAKE1 (
|
||||
"SecReadStateLimit",
|
||||
cmd_conn_read_state_limit,
|
||||
NULL,
|
||||
CMD_SCOPE_ANY,
|
||||
"maximum number of threads in READ_BUSY state per ip address"
|
||||
),
|
||||
|
||||
AP_INIT_TAKE1 (
|
||||
"SecRequestBodyInMemoryLimit",
|
||||
cmd_request_body_inmemory_limit,
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "modsecurity.h"
|
||||
#include "apache2.h"
|
||||
|
||||
|
||||
/* -- Input filter -- */
|
||||
|
||||
#if 0
|
||||
@ -89,7 +88,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out,
|
||||
return APR_EGENERAL;
|
||||
}
|
||||
|
||||
if (chunk) {
|
||||
if (chunk && !msr->txcfg->stream_inbody_inspection) {
|
||||
/* Copy the data we received in the chunk */
|
||||
bucket = apr_bucket_heap_create(chunk->data, chunk->length, NULL,
|
||||
f->r->connection->bucket_alloc);
|
||||
@ -117,6 +116,22 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out,
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "Input filter: Forwarded %" APR_SIZE_T_FMT " bytes.", chunk->length);
|
||||
}
|
||||
} else if (msr->stream_input_data != NULL) {
|
||||
|
||||
bucket = apr_bucket_heap_create(msr->stream_input_data, msr->stream_input_length, NULL,
|
||||
f->r->connection->bucket_alloc);
|
||||
|
||||
if (bucket == NULL) return APR_EGENERAL;
|
||||
APR_BRIGADE_INSERT_TAIL(bb_out, bucket);
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "Input stream filter: Forwarded %" APR_SIZE_T_FMT " bytes.", msr->msc_reqbody_disk_chunk->length);
|
||||
}
|
||||
}
|
||||
|
||||
if(msr->txcfg->stream_inbody_inspection && msr->stream_input_data != NULL) {
|
||||
free(msr->stream_input_data);
|
||||
msr->stream_input_data = NULL;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
@ -259,7 +274,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **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->reqbody_length);
|
||||
}
|
||||
|
||||
msr->if_status = IF_STATUS_WANTS_TO_RUN;
|
||||
@ -409,9 +424,9 @@ static apr_status_t send_of_brigade(modsec_rec *msr, ap_filter_t *f) {
|
||||
rc = ap_pass_brigade(f->next, msr->of_brigade);
|
||||
if (rc != APR_SUCCESS) {
|
||||
/* TODO: These need to move to flags in 2.6. For now log them
|
||||
* at level 3 so that they are not confusing users.
|
||||
* at level 4 so that they are not confusing users.
|
||||
*/
|
||||
int log_level = 3;
|
||||
int log_level = 4;
|
||||
|
||||
if (msr->txcfg->debuglog_level >= log_level) {
|
||||
switch(rc) {
|
||||
@ -438,6 +453,34 @@ static apr_status_t send_of_brigade(modsec_rec *msr, ap_filter_t *f) {
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
static void inject_content_to_of_brigade(modsec_rec *msr, ap_filter_t *f) {
|
||||
apr_bucket *b;
|
||||
|
||||
if (msr->txcfg->content_injection_enabled && msr->stream_output_data != NULL) {
|
||||
apr_bucket *bucket_ci = NULL;
|
||||
apr_bucket *bucket_eos = NULL;
|
||||
|
||||
bucket_ci = apr_bucket_heap_create(msr->stream_output_data,
|
||||
msr->stream_output_length, NULL, f->r->connection->bucket_alloc);
|
||||
|
||||
for (b = APR_BRIGADE_FIRST(msr->of_brigade); b != APR_BRIGADE_SENTINEL(msr->of_brigade); b = APR_BUCKET_NEXT(b)) {
|
||||
if(!APR_BUCKET_IS_METADATA(b))
|
||||
apr_bucket_delete(b);
|
||||
}
|
||||
|
||||
APR_BRIGADE_INSERT_HEAD(msr->of_brigade, bucket_ci);
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "Content Injection: Data reinjected bytes [%d]",msr->stream_output_length);
|
||||
}
|
||||
}
|
||||
|
||||
if(msr->stream_output_data != NULL) {
|
||||
free(msr->stream_output_data);
|
||||
msr->stream_output_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -486,6 +529,19 @@ static int flatten_response_body(modsec_rec *msr) {
|
||||
msr->resbody_data[msr->resbody_length] = '\0';
|
||||
msr->resbody_status = RESBODY_STATUS_READ;
|
||||
|
||||
if (msr->txcfg->stream_outbody_inspection) {
|
||||
msr->stream_output_data = (char *)malloc(msr->resbody_length+1);
|
||||
msr->stream_output_length = msr->resbody_length+1;
|
||||
|
||||
if (msr->stream_output_data == NULL) {
|
||||
msr_log(msr, 1, "Output filter: Stream Response body data memory allocation failed. Asked for: %" APR_SIZE_T_FMT,
|
||||
msr->stream_output_length + 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
apr_cpystrn(msr->stream_output_data,msr->resbody_data,msr->stream_output_length);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -801,6 +857,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) {
|
||||
* (full-buffering only).
|
||||
*/
|
||||
if ((msr->of_skipping == 0)&&(!msr->of_partial)) {
|
||||
inject_content_to_of_brigade(msr,f);
|
||||
prepend_content_to_of_brigade(msr, f);
|
||||
|
||||
/* Inject content into response (append & buffering). */
|
||||
|
@ -460,12 +460,12 @@ static apr_status_t module_cleanup(void *data) {
|
||||
*/
|
||||
static const char *modsec_var_log_handler(request_rec *r, char *name) {
|
||||
modsec_rec *msr = NULL;
|
||||
|
||||
|
||||
if (name == NULL) return NULL;
|
||||
|
||||
|
||||
msr = retrieve_tx_context(r);
|
||||
if (msr == NULL) return NULL;
|
||||
|
||||
|
||||
return construct_single_var(msr, name);
|
||||
}
|
||||
|
||||
@ -474,7 +474,7 @@ static const char *modsec_var_log_handler(request_rec *r, char *name) {
|
||||
*/
|
||||
static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) {
|
||||
static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
|
||||
|
||||
|
||||
/* Initialise ModSecurity engine */
|
||||
modsecurity = modsecurity_create(mp, MODSEC_ONLINE);
|
||||
if (modsecurity == NULL) {
|
||||
@ -482,7 +482,7 @@ static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_te
|
||||
"ModSecurity: Failed to initialise engine.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
|
||||
if (log_pfn_register) {
|
||||
log_pfn_register(mp, "M", modsec_var_log_handler, 0);
|
||||
@ -575,7 +575,6 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t
|
||||
ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
|
||||
"%s configured.", MODSEC_MODULE_NAME_FULL);
|
||||
|
||||
/* show libraries informations */
|
||||
version(mp);
|
||||
|
||||
/* If we've changed the server signature make note of the original. */
|
||||
@ -622,13 +621,13 @@ static int hook_request_early(request_rec *r) {
|
||||
/* NOTE This check is not currently needed, but it may be needed in the
|
||||
* future when we add another early phase.
|
||||
*/
|
||||
|
||||
|
||||
/* Are we allowed to continue? */
|
||||
if (msr->txcfg->is_enabled == MODSEC_DISABLED) {
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "Processing disabled, skipping (hook request_early).");
|
||||
}
|
||||
|
||||
|
||||
return DECLINED;
|
||||
}
|
||||
#endif
|
||||
@ -668,7 +667,7 @@ static int hook_request_late(request_rec *r) {
|
||||
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;
|
||||
|
||||
@ -693,15 +692,15 @@ static int hook_request_late(request_rec *r) {
|
||||
if (msr->txcfg->debuglog_level >= 4) {
|
||||
msr_log(msr, 4, "Processing disabled, skipping (hook request_late).");
|
||||
}
|
||||
|
||||
|
||||
return DECLINED;
|
||||
}
|
||||
|
||||
/* Phase 1 */
|
||||
/* 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. */
|
||||
@ -709,18 +708,18 @@ static int hook_request_late(request_rec *r) {
|
||||
if (rc != DECLINED) {
|
||||
/* Intercepted */
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The rule engine could have been disabled in phase 1. */
|
||||
/* 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);
|
||||
@ -802,9 +801,16 @@ static int hook_request_late(request_rec *r) {
|
||||
rc = perform_interception(msr);
|
||||
}
|
||||
|
||||
if(msr->txcfg->stream_inbody_inspection && msr->msc_reqbody_read) {
|
||||
const char *clen = NULL;
|
||||
clen = apr_psprintf(msr->mp,"%d",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");
|
||||
@ -1221,13 +1227,13 @@ static void register_hooks(apr_pool_t *mp) {
|
||||
"mod_ssl.c",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static const char *postconfig_afterme_list[] = {
|
||||
"mod_fcgid.c",
|
||||
"mod_cgid.c",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static const char *postread_beforeme_list[] = {
|
||||
"mod_rpaf.c",
|
||||
"mod_rpaf-2.0.c",
|
||||
@ -1239,18 +1245,16 @@ static void register_hooks(apr_pool_t *mp) {
|
||||
"mod_unique_id.c",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static const char *postread_afterme_list[] = {
|
||||
"mod_log_forensic.c",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static const char *transaction_afterme_list[] = {
|
||||
"mod_log_config.c",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Add the MODSEC_2.x compatibility defines */
|
||||
*(char **)apr_array_push(ap_server_config_defines) = apr_pstrdup(mp, "MODSEC_2.5");
|
||||
@ -1311,8 +1315,10 @@ static void register_hooks(apr_pool_t *mp) {
|
||||
* mod_deflate = -1
|
||||
* mod_headers = 0
|
||||
*/
|
||||
|
||||
ap_register_output_filter("MODSECURITY_OUT", output_filter,
|
||||
NULL, AP_FTYPE_CONTENT_SET - 3);
|
||||
|
||||
}
|
||||
|
||||
/* Defined in apache2_config.c */
|
||||
|
@ -343,7 +343,7 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) {
|
||||
msr_log(msr, 1, "Initialisation: Error occurred while parsing QUERY_STRING arguments.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (invalid_count) {
|
||||
msr->urlencoded_error = 1;
|
||||
}
|
||||
@ -355,6 +355,8 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) {
|
||||
if (msr->request_headers_to_sanitize == NULL) return -1;
|
||||
msr->response_headers_to_sanitize = apr_table_make(msr->mp, 16);
|
||||
if (msr->response_headers_to_sanitize == NULL) return -1;
|
||||
msr->pattern_to_sanitize = apr_table_make(msr->mp, 32);
|
||||
if (msr->pattern_to_sanitize == NULL) return -1;
|
||||
|
||||
/* Initialise cookies */
|
||||
msr->request_cookies = apr_table_make(msr->mp, 16);
|
||||
@ -543,6 +545,7 @@ static apr_status_t modsecurity_process_phase_response_body(modsec_rec *msr) {
|
||||
|
||||
msr->time_phase4 = apr_time_now() - time_before;
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ typedef struct msc_engine msc_engine;
|
||||
typedef struct msc_data_chunk msc_data_chunk;
|
||||
typedef struct msc_arg msc_arg;
|
||||
typedef struct msc_string msc_string;
|
||||
typedef struct msc_parm msc_parm;
|
||||
|
||||
#include "msc_release.h"
|
||||
#include "msc_logging.h"
|
||||
@ -229,6 +230,11 @@ struct modsec_rec {
|
||||
char *resbody_data;
|
||||
unsigned int resbody_contains_html;
|
||||
|
||||
apr_size_t stream_input_length;
|
||||
char *stream_input_data;
|
||||
apr_size_t stream_output_length;
|
||||
char *stream_output_data;
|
||||
|
||||
apr_array_header_t *error_messages;
|
||||
apr_array_header_t *alerts;
|
||||
|
||||
@ -267,6 +273,7 @@ struct modsec_rec {
|
||||
apr_table_t *request_headers_to_sanitize;
|
||||
apr_table_t *response_headers_to_sanitize;
|
||||
apr_table_t *request_cookies;
|
||||
apr_table_t *pattern_to_sanitize;
|
||||
|
||||
unsigned int urlencoded_error;
|
||||
unsigned int inbound_error;
|
||||
@ -473,6 +480,10 @@ struct directory_config {
|
||||
/* Content injection. */
|
||||
int content_injection_enabled;
|
||||
|
||||
/* Stream Inspection */
|
||||
int stream_inbody_inspection;
|
||||
int stream_outbody_inspection;
|
||||
|
||||
/* Geo Lookup */
|
||||
geo_db *geo;
|
||||
|
||||
@ -535,6 +546,11 @@ struct msc_string {
|
||||
unsigned int value_len;
|
||||
};
|
||||
|
||||
struct msc_parm {
|
||||
char *value;
|
||||
unsigned int pad_1;
|
||||
unsigned int pad_2;
|
||||
};
|
||||
|
||||
/* Engine functions */
|
||||
|
||||
|
@ -343,15 +343,41 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro
|
||||
/* NOTE: This is hard-coded for size 3 records */
|
||||
/* Left */
|
||||
if ((ipnum & (1 << level)) == 0) {
|
||||
//rec_val = buf[0] +
|
||||
// (buf[1] << 8) +
|
||||
// (buf[2] << 16);
|
||||
rec_val = (buf[3*0 + 0] << (0*8)) +
|
||||
(buf[3*0 + 1] << (1*8)) +
|
||||
(buf[3*0 + 2] << (2*8));
|
||||
/*j = 3;
|
||||
p = &buf[2*j];
|
||||
x = 0;
|
||||
do {
|
||||
x <<= 8;
|
||||
x += *(--p);
|
||||
} while ( --j );
|
||||
rec_val = x;
|
||||
*/
|
||||
}
|
||||
/* Right */
|
||||
else {
|
||||
//rec_val = buf[3] +
|
||||
// (buf[4] << 8) +
|
||||
// (buf[5] << 16);
|
||||
rec_val = (buf[3*1 + 0] << (0*8)) +
|
||||
(buf[3*1 + 1] << (1*8)) +
|
||||
(buf[3*1 + 2] << (2*8));
|
||||
|
||||
/*j = 3;
|
||||
p = &buf[1*j];
|
||||
x = 0;
|
||||
do {
|
||||
x <<= 8;
|
||||
x += *(--p);
|
||||
} while ( --j );
|
||||
|
||||
rec_val = x;
|
||||
*/
|
||||
}
|
||||
|
||||
/* If we are past the country offset, then we are done */
|
||||
|
@ -326,10 +326,16 @@ static char *create_auditlog_boundary(request_rec *r) {
|
||||
* that have been marked as sensitive.
|
||||
*/
|
||||
static void sanitize_request_line(modsec_rec *msr) {
|
||||
const apr_array_header_t *tarr;
|
||||
const apr_table_entry_t *telts;
|
||||
int i;
|
||||
const apr_array_header_t *tarr = NULL;
|
||||
const apr_table_entry_t *telts = NULL;
|
||||
const apr_array_header_t *tarr_pattern = NULL;
|
||||
const apr_table_entry_t *telts_pattern = NULL;
|
||||
msc_parm *mparm = NULL;
|
||||
int i, k;
|
||||
char *qspos;
|
||||
char *buf = NULL;
|
||||
int sanitized_partial = 0;
|
||||
int sanitize_matched = 0;
|
||||
|
||||
/* Locate the query string. */
|
||||
qspos = strstr(msr->request_line, "?");
|
||||
@ -343,8 +349,9 @@ static void sanitize_request_line(modsec_rec *msr) {
|
||||
msc_arg *arg = (msc_arg *)telts[i].val;
|
||||
/* Only look at the parameters that appeared in the query string. */
|
||||
if (strcmp(arg->origin, "QUERY_STRING") == 0) {
|
||||
char *pat = NULL;
|
||||
char *p;
|
||||
int j;
|
||||
int j, arg_min, arg_max;
|
||||
|
||||
/* Go to the beginning of the parameter. */
|
||||
p = qspos;
|
||||
@ -352,23 +359,65 @@ static void sanitize_request_line(modsec_rec *msr) {
|
||||
while((*p != '\0')&&(j--)) p++;
|
||||
if (*p == '\0') {
|
||||
msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u of QUERY_STRING"
|
||||
"because the request line is too short.",
|
||||
log_escape_ex(msr->mp, arg->name, arg->name_len),
|
||||
arg->value_origin_offset);
|
||||
"because the request line is too short.",
|
||||
log_escape_ex(msr->mp, arg->name, arg->name_len),
|
||||
arg->value_origin_offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Write over the value. */
|
||||
j = arg->value_origin_len;
|
||||
while((*p != '\0')&&(j--)) {
|
||||
*p++ = '*';
|
||||
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
|
||||
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
|
||||
|
||||
sanitized_partial = 0;
|
||||
sanitize_matched = 0;
|
||||
buf = apr_psprintf(msr->mp, "%s",p);
|
||||
for ( k = 0; k < tarr_pattern->nelts; k++) {
|
||||
if(strncmp(telts_pattern[k].key,arg->name,strlen(arg->name)) ==0 ) {
|
||||
mparm = (msc_parm *)telts_pattern[k].val;
|
||||
pat = strstr(buf,mparm->value);
|
||||
if(mparm->pad_1 == -1)
|
||||
sanitize_matched = 1;
|
||||
|
||||
if (pat != NULL) {
|
||||
j = strlen(mparm->value);
|
||||
arg_min = j;
|
||||
arg_max = 1;
|
||||
while((*pat != '\0')&&(j--)) {
|
||||
if(arg_max > mparm->pad_2) {
|
||||
int off = (strlen(mparm->value) - arg_max);
|
||||
int pos = (mparm->pad_1-1);
|
||||
if(off > pos) {
|
||||
*pat = '*';
|
||||
}
|
||||
}
|
||||
arg_max++;
|
||||
arg_min--;
|
||||
*pat++;
|
||||
}
|
||||
}
|
||||
sanitized_partial = 1;
|
||||
}
|
||||
}
|
||||
if (*p == '\0') {
|
||||
msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u (size %d) "
|
||||
"of QUERY_STRING because the request line is too short.",
|
||||
log_escape_ex(msr->mp, arg->name, arg->name_len),
|
||||
arg->value_origin_offset, arg->value_origin_len);
|
||||
|
||||
if(sanitized_partial == 1 && sanitize_matched == 0) {
|
||||
while(*buf != '\0') {
|
||||
*p++ = *buf++;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
/* Write over the value. */
|
||||
j = arg->value_origin_len;
|
||||
while((*p != '\0')&&(j--)) {
|
||||
*p++ = '*';
|
||||
}
|
||||
|
||||
if (*p == '\0') {
|
||||
msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u (size %d) "
|
||||
"of QUERY_STRING because the request line is too short.",
|
||||
log_escape_ex(msr->mp, arg->name, arg->name_len),
|
||||
arg->value_origin_offset, arg->value_origin_len);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -451,7 +500,7 @@ msre_rule *return_chained_rule(const msre_rule *current, modsec_rec *msr) {
|
||||
for (i = 0; i < arr->nelts; i++) {
|
||||
rule = rules[i];
|
||||
if (rule != NULL) {
|
||||
if (strcmp(current->unparsed,rule->unparsed) == 0) {
|
||||
if (strncmp(current->unparsed,rule->unparsed,strlen(rule->unparsed)) == 0) {
|
||||
if (i < arr->nelts -1) {
|
||||
next_rule = rules[i+1];
|
||||
} else {
|
||||
@ -482,7 +531,7 @@ int chained_is_matched(modsec_rec *msr, const msre_rule *next_rule) {
|
||||
|
||||
for (i = 0; i < msr->matched_rules->nelts; i++) {
|
||||
rule = ((msre_rule **)msr->matched_rules->elts)[i];
|
||||
if (rule != NULL && (strcmp(rule->unparsed,next_rule->unparsed) == 0))
|
||||
if (rule != NULL && (strncmp(rule->unparsed,next_rule->unparsed,strlen(next_rule->unparsed)) == 0))
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -495,6 +544,8 @@ int chained_is_matched(modsec_rec *msr, const msre_rule *next_rule) {
|
||||
void sec_audit_logger(modsec_rec *msr) {
|
||||
const apr_array_header_t *arr = NULL;
|
||||
apr_table_entry_t *te = NULL;
|
||||
const apr_array_header_t *tarr_pattern = NULL;
|
||||
const apr_table_entry_t *telts_pattern = NULL;
|
||||
char *str1 = NULL, *str2 = NULL, *text = NULL;
|
||||
const msre_rule *rule = NULL, *next_rule = NULL;
|
||||
apr_size_t nbytes, nbytes_written;
|
||||
@ -504,7 +555,10 @@ void sec_audit_logger(modsec_rec *msr) {
|
||||
int wrote_response_body = 0;
|
||||
char *entry_filename, *entry_basename;
|
||||
apr_status_t rc;
|
||||
int i, limit;
|
||||
int i, limit, k, sanitized_partial, j;
|
||||
char *buf = NULL, *pat = NULL;
|
||||
msc_parm *mparm = NULL;
|
||||
int arg_min, arg_max, sanitize_matched;
|
||||
|
||||
/* the boundary is used by both audit log types */
|
||||
msr->new_auditlog_boundary = create_auditlog_boundary(msr->r);
|
||||
@ -621,12 +675,49 @@ void sec_audit_logger(modsec_rec *msr) {
|
||||
|
||||
arr = apr_table_elts(msr->request_headers);
|
||||
te = (apr_table_entry_t *)arr->elts;
|
||||
|
||||
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
|
||||
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
|
||||
|
||||
for (i = 0; i < arr->nelts; i++) {
|
||||
sanitized_partial = 0;
|
||||
sanitize_matched = 0;
|
||||
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
|
||||
/* Do we need to sanitize this request header? */
|
||||
if (apr_table_get(msr->request_headers_to_sanitize, te[i].key) != NULL) {
|
||||
/* Yes, sanitize it. */
|
||||
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
|
||||
buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2);
|
||||
|
||||
for ( k = 0; k < tarr_pattern->nelts; k++) {
|
||||
if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) {
|
||||
mparm = (msc_parm *)telts_pattern[k].val;
|
||||
if(mparm->pad_1 == -1)
|
||||
sanitize_matched = 1;
|
||||
pat = strstr(buf,mparm->value);
|
||||
if (pat != NULL) {
|
||||
j = strlen(mparm->value);
|
||||
arg_min = j;
|
||||
arg_max = 1;
|
||||
while((*pat != '\0')&&(j--)) {
|
||||
if(arg_max > mparm->pad_2) {
|
||||
int off = strlen(mparm->value) - arg_max;
|
||||
int pos = mparm->pad_1-1;
|
||||
if(off > pos) {
|
||||
*pat = '*';
|
||||
}
|
||||
}
|
||||
arg_max++;
|
||||
arg_min--;
|
||||
*pat++;
|
||||
}
|
||||
sanitized_partial = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sanitized_partial == 1 && sanitize_matched == 0) {
|
||||
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, buf);
|
||||
} else {
|
||||
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
|
||||
}
|
||||
}
|
||||
sec_auditlog_write(msr, text, strlen(text));
|
||||
}
|
||||
@ -820,10 +911,10 @@ void sec_audit_logger(modsec_rec *msr) {
|
||||
if (msr->response_headers_sent) {
|
||||
if (msr->status_line != NULL) {
|
||||
text = apr_psprintf(msr->mp, "%s %s\n", msr->response_protocol,
|
||||
msr->status_line);
|
||||
msr->status_line);
|
||||
} else {
|
||||
text = apr_psprintf(msr->mp, "%s %u\n", msr->response_protocol,
|
||||
msr->response_status);
|
||||
msr->response_status);
|
||||
}
|
||||
sec_auditlog_write(msr, text, strlen(text));
|
||||
|
||||
@ -831,18 +922,57 @@ void sec_audit_logger(modsec_rec *msr) {
|
||||
|
||||
arr = apr_table_elts(msr->response_headers);
|
||||
te = (apr_table_entry_t *)arr->elts;
|
||||
|
||||
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
|
||||
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
|
||||
|
||||
for (i = 0; i < arr->nelts; i++) {
|
||||
sanitized_partial = 0;
|
||||
sanitize_matched = 0;
|
||||
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
|
||||
/* Do we need to sanitize this response header? */
|
||||
if (apr_table_get(msr->response_headers_to_sanitize, te[i].key) != NULL) {
|
||||
/* Yes, sanitize it. */
|
||||
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
|
||||
buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2);
|
||||
|
||||
for ( k = 0; k < tarr_pattern->nelts; k++) {
|
||||
if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) {
|
||||
mparm = (msc_parm *)telts_pattern[k].val;
|
||||
if(mparm->pad_1 == -1)
|
||||
sanitize_matched = 1;
|
||||
pat = strstr(buf,mparm->value);
|
||||
if (pat != NULL) {
|
||||
j = strlen(mparm->value);
|
||||
arg_min = j;
|
||||
arg_max = 1;
|
||||
while((*pat != '\0')&&(j--)) {
|
||||
if(arg_max > mparm->pad_2) {
|
||||
int off = strlen(mparm->value) - arg_max;
|
||||
int pos = mparm->pad_1-1;
|
||||
if(off > pos) {
|
||||
*pat = '*';
|
||||
}
|
||||
}
|
||||
arg_max++;
|
||||
arg_min--;
|
||||
*pat++;
|
||||
}
|
||||
sanitized_partial = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sanitized_partial == 1 && sanitize_matched == 0) {
|
||||
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, buf);
|
||||
} else {
|
||||
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
|
||||
}
|
||||
}
|
||||
sec_auditlog_write(msr, text, strlen(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apr_table_clear(msr->pattern_to_sanitize);
|
||||
|
||||
/* AUDITLOG_PART_RESPONSE_BODY */
|
||||
|
||||
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_RESPONSE_BODY) != NULL) {
|
||||
@ -887,19 +1017,19 @@ void sec_audit_logger(modsec_rec *msr) {
|
||||
text = apr_psprintf(msr->mp, "Apache-Handler: %s\n", msr->r->handler);
|
||||
sec_auditlog_write(msr, text, strlen(text));
|
||||
}
|
||||
|
||||
|
||||
/* Stopwatch; left in for compatibility reasons */
|
||||
text = apr_psprintf(msr->mp, "Stopwatch: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT " (- - -)\n",
|
||||
msr->request_time, (now - msr->request_time));
|
||||
sec_auditlog_write(msr, text, strlen(text));
|
||||
|
||||
sec_auditlog_write(msr, text, strlen(text));
|
||||
|
||||
/* Stopwatch2 */
|
||||
{
|
||||
char *perf_all = format_all_performance_variables(msr, msr->mp);
|
||||
|
||||
|
||||
text = apr_psprintf(msr->mp, "Stopwatch2: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT
|
||||
"; %s\n", msr->request_time, (now - msr->request_time), perf_all);
|
||||
|
||||
|
||||
sec_auditlog_write(msr, text, strlen(text));
|
||||
}
|
||||
|
||||
|
@ -307,7 +307,6 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
|
||||
char *my_error_msg = NULL;
|
||||
msre_reqbody_processor_metadata *metadata =
|
||||
(msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
|
||||
|
||||
|
||||
if (metadata != NULL) {
|
||||
if ( (metadata->process != NULL)
|
||||
@ -384,6 +383,51 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static apr_status_t modsecurity_request_body_to_stream(modsec_rec *msr, char **error_msg) {
|
||||
msc_data_chunk **chunks;
|
||||
char *d;
|
||||
int i, sofar;
|
||||
|
||||
*error_msg = NULL;
|
||||
|
||||
/* Allocate a buffer large enough to hold the request body. */
|
||||
|
||||
if (msr->msc_reqbody_length + 1 == 0) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow the stream buffer: %u",
|
||||
msr->msc_reqbody_length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
msr->stream_input_length = msr->msc_reqbody_length;
|
||||
|
||||
msr->stream_input_data = malloc(msr->stream_input_length + 1);
|
||||
if (msr->stream_input_data== NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body on stream. Asked for %u bytes.",
|
||||
msr->stream_input_length + 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
msr->stream_input_data[msr->stream_input_length] = '\0';
|
||||
|
||||
/* Copy the data we keep in chunks into the new buffer. */
|
||||
|
||||
sofar = 0;
|
||||
d = msr->stream_input_data;
|
||||
chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
|
||||
for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
|
||||
if (sofar + chunks[i]->length <= msr->stream_input_length) {
|
||||
memcpy(d, chunks[i]->data, chunks[i]->length);
|
||||
d += chunks[i]->length;
|
||||
sofar += chunks[i]->length;
|
||||
} else {
|
||||
*error_msg = apr_psprintf(msr->mp, "Internal error, request body buffer overflow.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a bunch of chunks holding a request body with a single large chunk.
|
||||
*/
|
||||
@ -473,7 +517,7 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, cha
|
||||
*error_msg = apr_pstrdup(msr->mp, "Initialisation: Error occurred while parsing BODY arguments.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (invalid_count) {
|
||||
msr->urlencoded_error = 1;
|
||||
}
|
||||
@ -498,12 +542,14 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) {
|
||||
/* Note that we've read the body. */
|
||||
msr->msc_reqbody_read = 1;
|
||||
|
||||
if (msr->txcfg->stream_inbody_inspection)
|
||||
modsecurity_request_body_to_stream(msr, error_msg);
|
||||
|
||||
/* Finalise body processing. */
|
||||
if ((msr->msc_reqbody_processor != NULL) && (msr->msc_reqbody_error == 0)) {
|
||||
char *my_error_msg = NULL;
|
||||
msre_reqbody_processor_metadata *metadata =
|
||||
(msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
|
||||
|
||||
|
||||
if (metadata != NULL) {
|
||||
if ( (metadata->complete != NULL)
|
||||
|
@ -580,6 +580,8 @@ msre_actionset *msre_actionset_create(msre_engine *engine, const char *text,
|
||||
actionset->phase = NOT_SET;
|
||||
actionset->severity = -1;
|
||||
actionset->rule = NOT_SET_P;
|
||||
actionset->arg_max = -1;
|
||||
actionset->arg_min = -1;
|
||||
|
||||
/* Flow */
|
||||
actionset->is_chained = NOT_SET;
|
||||
@ -660,6 +662,8 @@ msre_actionset *msre_actionset_merge(msre_engine *engine, msre_actionset *parent
|
||||
if (child->severity != NOT_SET) merged->severity = child->severity;
|
||||
if (child->phase != NOT_SET) merged->phase = child->phase;
|
||||
if (child->rule != NOT_SET_P) merged->rule = child->rule;
|
||||
if (child->arg_min != NOT_SET) merged->arg_min = child->arg_min;
|
||||
if (child->arg_max != NOT_SET) merged->arg_max = child->arg_max;
|
||||
|
||||
/* Flow */
|
||||
merged->is_chained = child->is_chained;
|
||||
@ -714,6 +718,8 @@ void msre_actionset_set_defaults(msre_actionset *actionset) {
|
||||
if (actionset->phase == NOT_SET) actionset->phase = 2;
|
||||
if (actionset->severity == -1) {} /* leave at -1 */
|
||||
if (actionset->rule == NOT_SET_P) actionset->rule = NULL;
|
||||
if (actionset->arg_max == NOT_SET) actionset->arg_max = -1;
|
||||
if (actionset->arg_min == NOT_SET) actionset->arg_min = -1;
|
||||
|
||||
/* Flow */
|
||||
if (actionset->is_chained == NOT_SET) actionset->is_chained = 0;
|
||||
@ -1839,7 +1845,7 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr,
|
||||
time_before_op = apr_time_now();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
rc = rule->op_metadata->execute(msr, rule, var, &my_error_msg);
|
||||
|
||||
#if defined(PERFORMANCE_MEASUREMENT)
|
||||
|
@ -16,6 +16,7 @@
|
||||
* directly using the email address support@trustwave.com.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MSC_RE_H_
|
||||
#define _MSC_RE_H_
|
||||
|
||||
@ -174,6 +175,9 @@ struct msre_rule {
|
||||
/* Compiled Lua script. */
|
||||
msc_script *script;
|
||||
#endif
|
||||
|
||||
ap_regex_t *sub_regex;
|
||||
char *sub_str;
|
||||
};
|
||||
|
||||
char DSOLOCAL *msre_rule_generate_unparsed(apr_pool_t *pool, const msre_rule *rule, const char *targets, const char *args, const char *actions);
|
||||
@ -284,6 +288,8 @@ struct msre_actionset {
|
||||
int severity;
|
||||
int phase;
|
||||
msre_rule *rule;
|
||||
int arg_min;
|
||||
int arg_max;
|
||||
|
||||
/* Flow */
|
||||
int is_chained;
|
||||
|
@ -18,6 +18,9 @@
|
||||
*/
|
||||
#include "re.h"
|
||||
#include <ctype.h>
|
||||
#include "apr_lib.h"
|
||||
#include "apr_strmatch.h"
|
||||
|
||||
|
||||
/**
|
||||
* Register action with the engine.
|
||||
@ -344,7 +347,6 @@ apr_status_t collection_original_setvar(modsec_rec *msr, const char *col_name, c
|
||||
msr_log(msr, 9, "Original collection variable: %s.%s = \"%s\"", col_name, var_name,
|
||||
log_escape_ex(msr->mp, orig_var->value, orig_var->value_len));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -406,6 +408,34 @@ static apr_status_t msre_action_logdata_init(msre_engine *engine, msre_actionset
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* SanitizeMatchedBytes init */
|
||||
|
||||
static apr_status_t msre_action_sanitizeMatchedBytes_init(msre_engine *engine,
|
||||
msre_actionset *actionset, msre_action *action)
|
||||
{
|
||||
char *parse_parm = NULL;
|
||||
char *ac_param = NULL;
|
||||
char *savedptr = NULL;
|
||||
int arg_min = 0;
|
||||
int arg_max = 0;
|
||||
|
||||
if (action->param != NULL && strlen(action->param) == 3) {
|
||||
|
||||
ac_param = apr_pstrdup(engine->mp, action->param);
|
||||
parse_parm = apr_strtok(ac_param,"/",&savedptr);
|
||||
|
||||
if(apr_isdigit(*parse_parm) && apr_isdigit(*savedptr)) {
|
||||
arg_max = atoi(parse_parm);
|
||||
arg_min = atoi(savedptr);
|
||||
}
|
||||
}
|
||||
|
||||
actionset->arg_min = arg_min;
|
||||
actionset->arg_max = arg_max;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* severity */
|
||||
|
||||
static apr_status_t msre_action_severity_init(msre_engine *engine,
|
||||
@ -1053,7 +1083,9 @@ static apr_status_t msre_action_sanitizeMatched_execute(modsec_rec *msr, apr_poo
|
||||
const char *sargname = NULL;
|
||||
const apr_array_header_t *tarr;
|
||||
const apr_table_entry_t *telts;
|
||||
int i, type = 0;
|
||||
const apr_array_header_t *tarr_pattern;
|
||||
const apr_table_entry_t *telts_pattern;
|
||||
int i, type = 0, k;
|
||||
msc_string *mvar = msr->matched_var;
|
||||
|
||||
if (mvar->name_len == 0) return 0;
|
||||
@ -1090,7 +1122,6 @@ static apr_status_t msre_action_sanitizeMatched_execute(modsec_rec *msr, apr_poo
|
||||
msr_log(msr, 3, "sanitizeMatched: Don't know how to handle variable: %s",
|
||||
mvar->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2293,7 +2324,20 @@ void msre_engine_register_default_actions(msre_engine *engine) {
|
||||
NULL,
|
||||
msre_action_sanitizeArg_execute
|
||||
);
|
||||
|
||||
|
||||
/* sanitiseMatchedBytes */
|
||||
msre_engine_action_register(engine,
|
||||
"sanitizeMatchedBytes",
|
||||
ACTION_NON_DISRUPTIVE,
|
||||
0, 1,
|
||||
NO_PLUS_MINUS,
|
||||
ACTION_CARDINALITY_MANY,
|
||||
ACTION_CGROUP_NONE,
|
||||
NULL,
|
||||
msre_action_sanitizeMatchedBytes_init,
|
||||
msre_action_sanitizeMatched_execute
|
||||
);
|
||||
|
||||
/* sanitizeArg */
|
||||
msre_engine_action_register(engine,
|
||||
"sanitizeArg",
|
||||
@ -2319,7 +2363,7 @@ void msre_engine_register_default_actions(msre_engine *engine) {
|
||||
NULL,
|
||||
msre_action_sanitizeMatched_execute
|
||||
);
|
||||
|
||||
|
||||
/* sanitizeMatched */
|
||||
msre_engine_action_register(engine,
|
||||
"sanitizeMatched",
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "apr_lib.h"
|
||||
#include "apr_strmatch.h"
|
||||
#include "acmp.h"
|
||||
#include <regex.h>
|
||||
|
||||
/**
|
||||
*
|
||||
@ -72,6 +73,162 @@ static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rsub */
|
||||
|
||||
static int msre_op_rsub_param_init(msre_rule *rule, char **error_msg) {
|
||||
const char *errptr = NULL;
|
||||
int erroffset;
|
||||
ap_regex_t *regex;
|
||||
const char *pattern = NULL;
|
||||
const char *line = NULL;
|
||||
char *reg_pattern = NULL;
|
||||
char *replace = NULL;
|
||||
char *flags = NULL;
|
||||
char *data;
|
||||
char delim;
|
||||
int ignore_case = 0;
|
||||
|
||||
line = rule->op_param;
|
||||
|
||||
if (apr_tolower(*line) != 's') {
|
||||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator format, must be s/// pattern",
|
||||
erroffset, errptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = apr_pstrdup(rule->ruleset->mp, line);
|
||||
delim = *++data;
|
||||
if (delim)
|
||||
reg_pattern = ++data;
|
||||
if (reg_pattern) {
|
||||
if (*data != delim) {
|
||||
while (*++data && *data != delim);
|
||||
}
|
||||
if (*data) {
|
||||
*data = '\0';
|
||||
replace = ++data;
|
||||
}
|
||||
}
|
||||
if (replace) {
|
||||
if (*data != delim) {
|
||||
while (*++data && *data != delim);
|
||||
}
|
||||
if (*data) {
|
||||
*data = '\0';
|
||||
flags = ++data;
|
||||
}
|
||||
}
|
||||
|
||||
if (!delim || !reg_pattern || !*reg_pattern || !replace) {
|
||||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator format - must be s/regex/str/[flags]",
|
||||
erroffset, errptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
while (*flags) {
|
||||
delim = apr_tolower(*flags);
|
||||
if (delim == 'i')
|
||||
ignore_case = 1;
|
||||
else
|
||||
*error_msg = apr_psprintf(rule->ruleset->mp, "Regex flag not supported",
|
||||
erroffset, errptr);
|
||||
flags++;
|
||||
}
|
||||
}
|
||||
|
||||
if (error_msg == NULL) return -1;
|
||||
*error_msg = NULL;
|
||||
|
||||
pattern = apr_pstrdup(rule->ruleset->mp, reg_pattern);
|
||||
rule->sub_str = apr_pstrdup(rule->ruleset->mp, replace);
|
||||
|
||||
regex = ap_pregcomp(rule->ruleset->mp, pattern, AP_REG_EXTENDED |
|
||||
(ignore_case ? AP_REG_ICASE : 0));
|
||||
|
||||
rule->sub_regex = regex;
|
||||
return 1; /* OK */
|
||||
}
|
||||
|
||||
static int msre_op_rsub_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||||
ap_regex_t *regex = rule->sub_regex;
|
||||
char *offset = NULL;
|
||||
int sub = 0, so = 0, p_len = 0;
|
||||
char *replace = NULL;
|
||||
char *data = NULL;
|
||||
int size = var->value_len;
|
||||
int output_body = 0, input_body = 0, count = 0;
|
||||
ap_regmatch_t pmatch[AP_MAX_REG_MATCH];
|
||||
|
||||
|
||||
if(strcmp(var->name,"STREAM_OUTPUT_BODY") == 0 ) {
|
||||
output_body = 1;
|
||||
} else if(strcmp(var->name,"STREAM_INPUT_BODY") == 0 ) {
|
||||
input_body = 1;
|
||||
} else {
|
||||
msr_log(msr,9,"Operator rsub only works with STREAM_* variables");
|
||||
return -1;
|
||||
}
|
||||
|
||||
replace = apr_pstrdup(rule->ruleset->mp, rule->sub_str);;
|
||||
data = apr_pcalloc(msr->mp, var->value_len+(AP_MAX_REG_MATCH*strlen(replace))+1);
|
||||
|
||||
if(replace == NULL || data == NULL)
|
||||
return 0;
|
||||
|
||||
memcpy(data,var->value,var->value_len);
|
||||
size += (AP_MAX_REG_MATCH*strlen(replace)+2);
|
||||
|
||||
if (ap_regexec(rule->sub_regex, data ,AP_MAX_REG_MATCH, pmatch, 0)) return 0;
|
||||
|
||||
for (offset = replace; *offset; offset++)
|
||||
if (*offset == '\\' && *(offset + 1) > '0' && *(offset + 1) <= '9') {
|
||||
so = pmatch [*(offset + 1) - 48].rm_so;
|
||||
p_len = pmatch [*(offset + 1) - 48].rm_eo - so;
|
||||
if (so < 0 || strlen (replace) + p_len - 1 > size) return 0;
|
||||
memmove (offset + p_len, offset + 2, strlen (offset) - 1);
|
||||
memmove (offset, data + so, p_len);
|
||||
offset = offset + p_len - 2;
|
||||
}
|
||||
|
||||
sub = pmatch [1].rm_so;
|
||||
|
||||
for (offset = data; !ap_regexec(rule->sub_regex, offset, 1, pmatch, 0); ) {
|
||||
p_len = pmatch [0].rm_eo - pmatch [0].rm_so;
|
||||
count++;
|
||||
offset += pmatch [0].rm_so;
|
||||
if (var->value_len - p_len + strlen(replace) + 1 > size) return 0;
|
||||
memmove (offset + strlen (replace), offset + p_len, strlen (offset) - p_len + 1);
|
||||
memmove (offset, replace, strlen (replace));
|
||||
offset += strlen (replace);
|
||||
if (sub >= 0) break;
|
||||
}
|
||||
|
||||
size -= ((AP_MAX_REG_MATCH - count)*(strlen(replace)) + ((strlen(replace) - p_len)*(count+AP_MAX_REG_MATCH) - (AP_MAX_REG_MATCH+4)));
|
||||
|
||||
if(msr->stream_output_data != NULL && output_body == 1) {
|
||||
msr->stream_output_data = (char *)realloc(msr->stream_output_data,size);
|
||||
msr->stream_output_length = size;
|
||||
if (msr->stream_output_data != NULL) {
|
||||
memset(msr->stream_output_data,0,size);
|
||||
memcpy(msr->stream_output_data,data,size);
|
||||
msr->stream_output_data[msr->stream_output_length] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if(msr->stream_input_data != NULL && input_body == 1) {
|
||||
msr->stream_input_data = (char *)realloc(msr->stream_input_data,size);
|
||||
msr->stream_input_length = size;
|
||||
if (msr->stream_input_data != NULL) {
|
||||
memset(msr->stream_input_data,0,size);
|
||||
memcpy(msr->stream_input_data,data,size);
|
||||
msr->stream_input_data[msr->stream_input_length] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* rx */
|
||||
|
||||
static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) {
|
||||
@ -103,7 +260,12 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c
|
||||
char *my_error_msg = NULL;
|
||||
int ovector[33];
|
||||
int capture = 0;
|
||||
int matched_bytes = 0;
|
||||
int matched = 0;
|
||||
int rc;
|
||||
char *qspos = NULL;
|
||||
const char *parm = NULL;
|
||||
msc_parm *mparm = NULL;
|
||||
|
||||
if (error_msg == NULL) return -1;
|
||||
*error_msg = NULL;
|
||||
@ -127,12 +289,14 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c
|
||||
|
||||
/* Are we supposed to capture subexpressions? */
|
||||
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
|
||||
matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
|
||||
matched = apr_table_get(rule->actionset->actions, "sanitizeMatched") ? 1 : 0;
|
||||
|
||||
/* Show when the regex captures but "capture" is not set */
|
||||
if (msr->txcfg->debuglog_level >= 6) {
|
||||
int capcount = 0;
|
||||
rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount);
|
||||
if (msr->txcfg->debuglog_level >= 6) {
|
||||
if (msr->txcfg->debuglog_level >= 6) {
|
||||
if ((capture == 0) && (capcount > 0)) {
|
||||
msr_log(msr, 6, "Ignoring regex captures since \"capture\" action is not enabled.");
|
||||
}
|
||||
@ -192,16 +356,39 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c
|
||||
for(i = 0; i < rc; i++) {
|
||||
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||||
if (s == NULL) return -1;
|
||||
|
||||
s->name = apr_psprintf(msr->mp, "%d", i);
|
||||
s->name_len = strlen(s->name);
|
||||
s->value = apr_pstrmemdup(msr->mp,
|
||||
target + ovector[2 * i], ovector[2 * i + 1] - ovector[2 * i]);
|
||||
|
||||
s->value_len = (ovector[2 * i + 1] - ovector[2 * i]);
|
||||
if ((s->name == NULL)||(s->value == NULL)) return -1;
|
||||
|
||||
|
||||
apr_table_addn(msr->tx_vars, s->name, (void *)s);
|
||||
|
||||
|
||||
if(((matched == 1) || (matched_bytes == 1)) && (var != NULL) && (var->name != NULL)) {
|
||||
qspos = apr_psprintf(msr->mp, "%s", var->name);
|
||||
parm = strstr(qspos, ":");
|
||||
if (parm != NULL) {
|
||||
parm++;
|
||||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||||
if (mparm == NULL)
|
||||
continue;
|
||||
|
||||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||||
mparm->pad_1 = rule->actionset->arg_min;
|
||||
mparm->pad_2 = rule->actionset->arg_max;
|
||||
apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm);
|
||||
} else {
|
||||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||||
if (mparm == NULL)
|
||||
continue;
|
||||
|
||||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||||
apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm);
|
||||
}
|
||||
}
|
||||
|
||||
if (msr->txcfg->debuglog_level >= 9) {
|
||||
msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
|
||||
log_escape_nq_ex(msr->mp, s->value, s->value_len));
|
||||
@ -2290,6 +2477,13 @@ void msre_engine_register_default_operators(msre_engine *engine) {
|
||||
msre_op_nomatch_execute
|
||||
);
|
||||
|
||||
/* rsub */
|
||||
msre_engine_op_register(engine,
|
||||
"rsub",
|
||||
msre_op_rsub_param_init,
|
||||
msre_op_rsub_execute
|
||||
);
|
||||
|
||||
/* rx */
|
||||
msre_engine_op_register(engine,
|
||||
"rx",
|
||||
|
@ -2149,6 +2149,32 @@ static int var_path_info_generate(modsec_rec *msr, msre_var *var, msre_rule *rul
|
||||
return var_simple_generate(var, vartab, mptmp, value);
|
||||
}
|
||||
|
||||
/* STREAM_OUTPUT_BODY */
|
||||
|
||||
static int var_stream_output_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if (msr->stream_output_data != NULL) {
|
||||
return var_simple_generate_ex(var, vartab, mptmp,
|
||||
msr->stream_output_data, msr->stream_output_length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* STREAM_INPUT_BODY */
|
||||
|
||||
static int var_stream_input_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if (msr->stream_input_data != NULL) {
|
||||
return var_simple_generate_ex(var, vartab, mptmp,
|
||||
msr->stream_input_data, msr->stream_input_length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RESPONSE_BODY */
|
||||
|
||||
static int var_response_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
@ -2890,7 +2916,7 @@ void msre_engine_register_default_variables(msre_engine *engine) {
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
|
||||
/* REQUEST_BODY_LENGTH */
|
||||
msre_engine_variable_register(engine,
|
||||
"REQUEST_BODY_LENGTH",
|
||||
@ -3012,6 +3038,29 @@ void msre_engine_register_default_variables(msre_engine *engine) {
|
||||
PHASE_REQUEST_HEADERS
|
||||
);
|
||||
|
||||
/* STREAM_OUTPUT_BODY */
|
||||
msre_engine_variable_register(engine,
|
||||
"STREAM_OUTPUT_BODY",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_stream_output_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_RESPONSE_BODY
|
||||
);
|
||||
|
||||
/* STREAM_INPUT_BODY */
|
||||
msre_engine_variable_register(engine,
|
||||
"STREAM_INPUT_BODY",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_stream_input_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_FIRST
|
||||
);
|
||||
|
||||
|
||||
/* RESPONSE_BODY */
|
||||
msre_engine_variable_register(engine,
|
||||
"RESPONSE_BODY",
|
||||
|
Loading…
x
Reference in New Issue
Block a user