Stricter validation for @validateUtf8Encoding.

Capture the match in TX:0 when using "capture" action w/@pm operators.
This commit is contained in:
brectanus
2007-07-31 19:04:07 +00:00
parent 5a38dde99b
commit 3e5e2a06b7
2 changed files with 135 additions and 57 deletions

36
CHANGES
View File

@@ -1,9 +1,45 @@
?? ??? 2007 - 2.5.0-trunk ?? ??? 2007 - 2.5.0-trunk
------------------------- -------------------------
* Stricter validation for @validateUtf8Encoding.
* Capture the match in TX:0 when using "capture" action in phrase match
operators.
* Added Cygwin to the list of platforms not supporting the hidden * Added Cygwin to the list of platforms not supporting the hidden
visibility attribute. visibility attribute.
27 July 2007 - 2.1.2
--------------------
* Update included core rules to latest version (1.4.3).
* Enhanced ability to alert/audit failed requests.
* Do not trigger "pause" action for internal requests.
* Fixed issue with requests that use internal requests. These had the
potential to be intercepted incorrectly when other Apache httpd modules
that used internal requests were used with mod_security.
* Added Solaris and Cygwin to the list of platforms not supporting the hidden
visibility attribute.
* Fixed decoding full-width unicode in t:urlDecodeUni.
* Lessen some overhead of debugging messages and calculations.
* Do not try to intercept a request after a failed rule. This fixes the
issue associated with an "Internal Error: Asked to intercept request
but was_intercepted is zero" error message.
* Added SecAuditLog2 directive to allow redundent concurrent audit log
index files. This will allow sending audit data to two consoles, etc.
* Small performance improvement in memory management for rule execution.
21 June 2007 - 2.5.0-dev2 21 June 2007 - 2.5.0-dev2
------------------------- -------------------------

View File

@@ -308,10 +308,14 @@ static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) {
static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
const char *match = NULL; const char *match = NULL;
apr_status_t rc = 0; apr_status_t rc = 0;
int capture;
/* Nothing to read */ /* Nothing to read */
if ((var->value == NULL) || (var->value_len == 0)) return 0; if ((var->value == NULL) || (var->value_len == 0)) return 0;
/* Are we supposed to capture subexpressions? */
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
ACMPT pt = {(ACMP *)rule->op_param_data, NULL}; ACMPT pt = {(ACMP *)rule->op_param_data, NULL};
rc = acmp_process_quick(&pt, &match, var->value, var->value_len); rc = acmp_process_quick(&pt, &match, var->value, var->value_len);
@@ -326,6 +330,33 @@ static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c
*error_msg = apr_psprintf(msr->mp, "Matched phrase \"%s\" at %s.", *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%s\" at %s.",
match_escaped, var->name); match_escaped, var->name);
} }
/* Handle capture as tx.0=match */
if (capture) {
int i;
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
if (s == NULL) return -1;
s->name = "0";
s->value = apr_pstrdup(msr->mp, match);
if (s->value == NULL) return -1;
s->value_len = strlen(s->value);
apr_table_setn(msr->tx_vars, s->name, (void *)s);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Adding phrase match to TXVARS (0): %s",
log_escape_nq_ex(msr->mp, s->value, s->value_len));
}
/* Unset the remaining ones (from previous invocations). */
for(i = rc; i <= 9; i++) {
char buf[2];
apr_snprintf(buf, sizeof(buf), "%i", i);
apr_table_unset(msr->tx_vars, buf);
}
}
return 1; return 1;
} }
return rc; return rc;
@@ -1145,89 +1176,91 @@ static int msre_op_validateUrlEncoding_execute(modsec_rec *msr, msre_rule *rule,
#define UNICODE_ERROR_CHARACTERS_MISSING -1 #define UNICODE_ERROR_CHARACTERS_MISSING -1
#define UNICODE_ERROR_INVALID_ENCODING -2 #define UNICODE_ERROR_INVALID_ENCODING -2
#define UNICODE_ERROR_OVERLONG_CHARACTER -3 #define UNICODE_ERROR_OVERLONG_CHARACTER -3
#define UNICODE_ERROR_RESTRICTED_CHARACTER -4
#define UNICODE_ERROR_DECODING_ERROR -5
/* NOTE: This is over-commented for ease of verification */
static int detect_utf8_character(const char *p_read, unsigned int length) { static int detect_utf8_character(const char *p_read, unsigned int length) {
int unicode_len = 0; int unicode_len = 0;
unsigned int d = 0; unsigned int d = 0;
unsigned char c; unsigned char c;
if (p_read == NULL) return 0; if (p_read == NULL) return UNICODE_ERROR_DECODING_ERROR;
c = *p_read; c = *p_read;
if (c == 0) return 0;
if ((c & 0xE0) == 0xC0) { /* If first byte begins with binary 0 it is single byte encoding */
/* two byte unicode */ if ((c & 0x80) == 0) {
/* single byte unicode (7 bit ASCII equivilent) has no validation */
return 1;
}
/* If first byte begins with binary 110 it is two byte encoding*/
else if ((c & 0xE0) == 0xC0) {
/* check we have at least two bytes */
if (length < 2) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; if (length < 2) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
else /* check second byte starts with binary 10 */
if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else { else {
unicode_len = 2; unicode_len = 2;
/* compute character number */
d = ((c & 0x1F) << 6) | (*(p_read + 1) & 0x3F); d = ((c & 0x1F) << 6) | (*(p_read + 1) & 0x3F);
} }
} }
/* If first byte begins with binary 1110 it is three byte encoding */
else if ((c & 0xF0) == 0xE0) { else if ((c & 0xF0) == 0xE0) {
/* three byte unicode */ /* check we have at least three bytes */
if (length < 3) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; if (length < 3) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
else /* check second byte starts with binary 10 */
if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else /* check third byte starts with binary 10 */
if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else { else {
unicode_len = 3; unicode_len = 3;
/* compute character number */
d = ((c & 0x0F) << 12) | ((*(p_read + 1) & 0x3F) << 6) | (*(p_read + 2) & 0x3F); d = ((c & 0x0F) << 12) | ((*(p_read + 1) & 0x3F) << 6) | (*(p_read + 2) & 0x3F);
} }
} }
/* If first byte begins with binary 11110 it is four byte encoding */
else if ((c & 0xF8) == 0xF0) { else if ((c & 0xF8) == 0xF0) {
/* four byte unicode */ /* restrict characters to UTF-8 range (U+0000 - U+10FFFF)*/
if (c >= 0xF5) {
return UNICODE_ERROR_RESTRICTED_CHARACTER;
}
/* check we have at least four bytes */
if (length < 4) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; if (length < 4) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
else /* check second byte starts with binary 10 */
if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else /* check third byte starts with binary 10 */
if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else /* check forth byte starts with binary 10 */
if (((*(p_read + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; else if (((*(p_read + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else { else {
d = ((c & 0x07) << 18) | ((*(p_read + 1) & 0x3F) << 12) | ((*(p_read + 2) & 0x3F) < 6) | (*(p_read + 3) & 0x3F);
unicode_len = 4; unicode_len = 4;
/* compute character number */
d = ((c & 0x07) << 18) | ((*(p_read + 1) & 0x3F) << 12) | ((*(p_read + 2) & 0x3F) < 6) | (*(p_read + 3) & 0x3F);
} }
} }
else if ((c & 0xFC) == 0xF8) { /* any other first byte is invalid (RFC 3629) */
/* five byte unicode */ else {
if (length < 5) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; return UNICODE_ERROR_INVALID_ENCODING;
else
if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else
if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else
if (((*(p_read + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else
if (((*(p_read + 4)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else {
d = ((c & 0x03) << 24) | ((*(p_read + 1) & 0x3F) << 18) | ((*(p_read + 2) & 0x3F) << 12) | ((*(p_read + 3) & 0x3F) << 6) | (*(p_read + 4) & 0x3F);
unicode_len = 5;
}
}
else if ((c & 0xFE) == 0xFC) {
/* six byte unicode */
if (length < 6) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
else
if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else
if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else
if (((*(p_read + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else
if (((*(p_read + 4)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else
if (((*(p_read + 5)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else {
d = ((c & 0x01) << 30) | ((*(p_read + 1) & 0x3F) << 24) | ((*(p_read + 2) & 0x3F) << 18) | ((*(p_read + 3) & 0x3F) << 12) | ((*(p_read + 4) & 0x3F) << 6) | (*(p_read + 5) & 0x3F);
unicode_len = 6;
}
} }
if ((unicode_len > 1)&&((d & 0x7F) == d)) { /* invalid UTF-8 character number range (RFC 3629) */
unicode_len = UNICODE_ERROR_OVERLONG_CHARACTER; if ((d >= 0xD800) && (d <= 0xDFFF)) {
return UNICODE_ERROR_RESTRICTED_CHARACTER;
}
/* check for overlong */
if ((unicode_len == 4) && (d < 0x010000)) {
/* four byte could be represented with less bytes */
return UNICODE_ERROR_OVERLONG_CHARACTER;
}
else if ((unicode_len == 3) && (d < 0x0800)) {
/* three byte could be represented with less bytes */
return UNICODE_ERROR_OVERLONG_CHARACTER;
}
else if ((unicode_len == 2) && (d < 0x80)) {
/* two byte could be represented with less bytes */
return UNICODE_ERROR_OVERLONG_CHARACTER;
} }
return unicode_len; return unicode_len;
@@ -1239,6 +1272,7 @@ static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule
unsigned int i, bytes_left; unsigned int i, bytes_left;
bytes_left = var->value_len; bytes_left = var->value_len;
for(i = 0; i < var->value_len; i++) { for(i = 0; i < var->value_len; i++) {
int rc = detect_utf8_character(&var->value[i], bytes_left); int rc = detect_utf8_character(&var->value[i], bytes_left);
switch(rc) { switch(rc) {
@@ -1248,18 +1282,26 @@ static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule
return 1; return 1;
break; break;
case UNICODE_ERROR_INVALID_ENCODING : case UNICODE_ERROR_INVALID_ENCODING :
*error_msg = apr_psprintf(msr->mp, "Invalid Unicode encoding: invalid byte value " *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: invalid byte value "
"in character."); "in character.");
return 1; return 1;
break; break;
case UNICODE_ERROR_OVERLONG_CHARACTER : case UNICODE_ERROR_OVERLONG_CHARACTER :
*error_msg = apr_psprintf(msr->mp, "Invalid Unicode encoding: overlong " *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: overlong "
"character detected."); "character detected.");
return 1; return 1;
break; break;
case UNICODE_ERROR_RESTRICTED_CHARACTER :
*error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: use of restricted character");
return 1;
break;
case UNICODE_ERROR_DECODING_ERROR :
*error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding");
return 1;
break;
} }
bytes_left--; bytes_left -= rc;
} }
return 0; return 0;