Merge 2.5.x changes into trunk.

This commit is contained in:
b1v1r 2009-05-16 07:54:17 +00:00
parent 48981bb7c8
commit 9d26b16e23
9 changed files with 228 additions and 43 deletions

View File

@ -1,6 +1,13 @@
22 Apr 2009 - trunk
-------------------
* Populate GEO:COUNTRY_NAME and GEO:COUNTRY_CONTINENT as documented.
* Handle a newer geo database more gracefully, avoiding a potential crash for
new countries that ModSecurity is not yet aware.
* Allow checking &GEO "@eq 0" for a failed @geoLookup.
* Fixed mlogc global mutex locking issue and added more debugging output.
* Cleaned up build dependencies and configure options.

View File

@ -21,7 +21,7 @@
/* -- Lookup Tables -- */
static const char *geo_country_code[] = {
static const char *geo_country_code[GEO_COUNTRY_LAST + 1] = {
"--",
"AP","EU","AD","AE","AF","AG","AI","AL","AM","AN",
"AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB",
@ -50,7 +50,7 @@ static const char *geo_country_code[] = {
"ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE"
};
static const char *geo_country_code3[] = {
static const char *geo_country_code3[GEO_COUNTRY_LAST + 1] = {
"--",
"AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT",
"AGO","AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB",
@ -79,7 +79,7 @@ static const char *geo_country_code3[] = {
"ZMB","MNE","ZWE","A1","A2","O1","ALA","GGY","IMN","JEY"
};
static const char *geo_country_name[] = {
static const char *geo_country_name[GEO_COUNTRY_LAST + 1] = {
"N/A",
"Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles",
"Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados",
@ -108,7 +108,7 @@ static const char *geo_country_name[] = {
"Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey"
};
static const char *geo_country_continent[] = {
static const char *geo_country_continent[GEO_COUNTRY_LAST + 1] = {
"--",
"AS","EU","EU","AS","AS","SA","SA","EU","AS","SA",
"AF","AN","SA","OC","EU","OC","SA","AS","EU","SA",
@ -315,11 +315,13 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro
/* NOTE: This only works with ipv4 */
if ((rc = apr_sockaddr_info_get(&addr, target, APR_INET, 0, 0, msr->mp)) != APR_SUCCESS) {
*error_msg = apr_psprintf(msr->mp, "Geo lookup of \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
msr_log(msr, 4, "%s", *error_msg);
return 0;
}
if ((rc = apr_sockaddr_ip_get(&targetip, addr)) != APR_SUCCESS) {
*error_msg = apr_psprintf(msr->mp, "Geo lookup of \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
msr_log(msr, 4, "%s", *error_msg);
return 0;
};
@ -360,8 +362,9 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro
if (geo->dbtype == GEO_COUNTRY_DATABASE) {
country = rec_val;
country -= geo->ctry_offset;
if (country <= 0) {
*error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\".", log_escape(msr->mp, target));
if ((country <= 0) || (country > GEO_COUNTRY_LAST)) {
*error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country);
msr_log(msr, 4, "%s", *error_msg);
return 0;
}
@ -383,8 +386,9 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro
rc = apr_file_read_full(geo->db, &cbuf, sizeof(cbuf), &nbytes);
country = cbuf[0];
if (country <= 0) {
*error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\".", log_escape(msr->mp, target));
if ((country <= 0) || (country > GEO_COUNTRY_LAST)) {
*error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country);
msr_log(msr, 4, "%s", *error_msg);
return 0;
}
if (msr->txcfg->debuglog_level >= 9) {
@ -472,7 +476,7 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro
}
*error_msg = apr_psprintf(msr->mp, "Geo lookup of \"%s\" succeeded.", log_escape(msr->mp, target));
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded.", log_escape(msr->mp, target));
return 1;
}

View File

@ -29,6 +29,8 @@
#define GEO_COUNTRY_DATABASE 1
#define GEO_CITY_DATABASE_0 6
#define GEO_CITY_DATABASE_1 2
#define GEO_COUNTRY_LAST 250
typedef struct geo_rec geo_rec;
typedef struct geo_db geo_db;

View File

@ -1206,11 +1206,16 @@ static int msre_op_geoLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var
rc = geo_lookup(msr, &rec, geo_host, error_msg);
if (rc <= 0) {
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed at %s.", log_escape_nq(msr->mp, geo_host), var->name);
if (! *error_msg) {
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed at %s.", log_escape_nq(msr->mp, geo_host), var->name);
}
apr_table_clear(msr->geo_vars);
return rc;
}
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded at %s.",
log_escape_nq(msr->mp, geo_host), var->name);
if (! *error_msg) {
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded at %s.",
log_escape_nq(msr->mp, geo_host), var->name);
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: %s={country_code=%s, country_code3=%s, country_name=%s, country_continent=%s, region=%s, city=%s, postal_code=%s, latitude=%f, longitude=%f, dma_code=%d, area_code=%d}",
@ -1229,63 +1234,77 @@ static int msre_op_geoLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var
}
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "country_code");
s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE");
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, rec.country_code ? rec.country_code : "");
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "country_code3");
s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE3");
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, rec.country_code3 ? rec.country_code3 : "");
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "region");
s->name = apr_pstrdup(msr->mp, "COUNTRY_NAME");
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, rec.country_name ? rec.country_name : "");
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "COUNTRY_CONTINENT");
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, rec.country_continent ? rec.country_continent : "");
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "REGION");
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, rec.region ? rec.region : "");
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "city");
s->name = apr_pstrdup(msr->mp, "CITY");
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, rec.city ? rec.city : "");
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "postal_code");
s->name = apr_pstrdup(msr->mp, "POSTAL_CODE");
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, rec.postal_code ? rec.postal_code : "");
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "latitude");
s->name = apr_pstrdup(msr->mp, "LATITUDE");
s->name_len = strlen(s->name);
s->value = apr_psprintf(msr->mp, "%f", rec.latitude);
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "longitude");
s->name = apr_pstrdup(msr->mp, "LONGITUDE");
s->name_len = strlen(s->name);
s->value = apr_psprintf(msr->mp, "%f", rec.longitude);
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "dma_code");
s->name = apr_pstrdup(msr->mp, "DMA_CODE");
s->name_len = strlen(s->name);
s->value = apr_psprintf(msr->mp, "%d", rec.dma_code);
s->value_len = strlen(s->value);
apr_table_setn(msr->geo_vars, s->name, (void *)s);
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
s->name = apr_pstrdup(msr->mp, "area_code");
s->name = apr_pstrdup(msr->mp, "AREA_CODE");
s->name_len = strlen(s->name);
s->value = apr_psprintf(msr->mp, "%d", rec.area_code);
s->value_len = strlen(s->value);

View File

@ -2282,7 +2282,7 @@ void msre_engine_register_default_variables(msre_engine *engine) {
msre_engine_variable_register(engine,
"GEO",
VAR_LIST,
1, 1,
0, 1,
var_generic_list_validate,
var_geo_generate,
VAR_DONT_CACHE, /* dynamic */

View File

@ -1 +1,44 @@
### Empty
# NOTE: All will return 0 because of lacking DB
{
type => "op",
name => "geoLookup",
param => "",
input => "",
ret => 0,
},
{
type => "op",
name => "geoLookup",
param => "TestCase",
input => "",
ret => 0,
},
# Failed Lookup
{
type => "op",
name => "geoLookup",
param => "",
input => "127.0.0.1",
ret => 0,
},
# Good
{
type => "op",
name => "geoLookup",
param => "",
input => "216.75.21.122",
#ret => 1,
ret => 0,
},
{
type => "op",
name => "geoLookup",
param => "",
input => "www.modsecurity.org",
#ret => 1,
ret => 0,
},

View File

@ -415,13 +415,105 @@
# ),
#},
## ENH: We cannot include this test as we cannot distribute the database.
## Instead we should create a simple test DB of our own.
## GEO
#{
# type => "target",
# comment => "GEO (ip)",
# conf => qq(
# SecRuleEngine On
# SecDebugLog $ENV{DEBUG_LOG}
# SecDebugLogLevel 9
# SecGeoLookupDB GeoLiteCity.dat
# SecRule ARGS:ip "\@geoLookup" "phase:2,log,pass,t:none"
# SecRule GEO:COUNTRY_CODE "\@streq US" "phase:2,log,pass,t:none"
# SecRule GEO:COUNTRY_CODE3 "\@streq USA" "phase:2,log,pass,t:none"
# SecRule GEO:COUNTRY_NAME "\@streq United States" "phase:2,log,pass,t:none"
# # ENH: Not in this database?
# SecRule GEO:COUNTRY_CONTINENT "\@streq NA" "phase:2,log,pass,t:none"
# SecRule GEO:REGION "\@streq CA" "phase:2,log,pass,t:none"
# SecRule GEO:CITY "\@streq San Diego" "phase:2,log,pass,t:none"
# SecRule GEO:POSTAL_CODE "\@streq 92123" "phase:2,log,pass,t:none"
# SecRule GEO:LATITUDE "\@beginsWith 32.8" "phase:2,log,pass,t:none"
# SecRule GEO:LONGITUDE "\@beginsWith 117.1" "phase:2,log,pass,t:none"
# SecRule GEO:DMA_CODE "\@streq 825" "phase:2,log,pass,t:none"
# SecRule GEO:AREA_CODE "\@streq 858" "phase:2,log,pass,t:none"
# ),
# match_log => {
# debug => [ qr/Geo lookup for "216.75.21.122" succeeded.*match "US" at GEO:COUNTRY_CODE.*match "USA" at GEO:COUNTRY_CODE3.*match "United States" at GEO:COUNTRY_NAME.*match "NA" at GEO:COUNTRY_CONTINENT.*match "CA" at GEO:REGION.*match "San Diego" at GEO:CITY.*match "92123" at GEO:POSTAL_CODE.*match "32.8" at GEO:LATITUDE.*match "825" at GEO:DMA_CODE.*match "858" at GEO:AREA_CODE/si, 1 ],
# },
# match_response => {
# status => qr/^200$/,
# },
# request => new HTTP::Request(
# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?ip=216.75.21.122",
# ),
#},
#{
# type => "target",
# comment => "GEO (host)",
# conf => qq(
# SecRuleEngine On
# SecDebugLog $ENV{DEBUG_LOG}
# SecDebugLogLevel 9
# SecGeoLookupDB GeoLiteCity.dat
# SecRule ARGS:host "\@geoLookup" "phase:2,log,pass,t:none"
# SecRule GEO:COUNTRY_CODE "\@streq US" "phase:2,log,pass,t:none"
# SecRule GEO:COUNTRY_CODE3 "\@streq USA" "phase:2,log,pass,t:none"
# SecRule GEO:COUNTRY_NAME "\@streq United States" "phase:2,log,pass,t:none"
# # ENH: Not in this database?
# SecRule GEO:COUNTRY_CONTINENT "\@streq NA" "phase:2,log,pass,t:none"
# SecRule GEO:REGION "\@streq CA" "phase:2,log,pass,t:none"
# SecRule GEO:CITY "\@streq San Diego" "phase:2,log,pass,t:none"
# SecRule GEO:POSTAL_CODE "\@streq 92123" "phase:2,log,pass,t:none"
# SecRule GEO:LATITUDE "\@beginsWith 32.8" "phase:2,log,pass,t:none"
# SecRule GEO:LONGITUDE "\@beginsWith 117.1" "phase:2,log,pass,t:none"
# SecRule GEO:DMA_CODE "\@streq 825" "phase:2,log,pass,t:none"
# SecRule GEO:AREA_CODE "\@streq 858" "phase:2,log,pass,t:none"
# ),
# match_log => {
# debug => [ qr/Using address "\d+\.\d+\.\d+\.\d+".*Geo lookup for "www\.modsecurity\.org" succeeded.*match "US" at GEO:COUNTRY_CODE.*match "USA" at GEO:COUNTRY_CODE3.*match "United States" at GEO:COUNTRY_NAME.*match "NA" at GEO:COUNTRY_CONTINENT.*match "CA" at GEO:REGION.*match "San Diego" at GEO:CITY.*match "92123" at GEO:POSTAL_CODE.*match "32.8" at GEO:LATITUDE.*match "825" at GEO:DMA_CODE.*match "858" at GEO:AREA_CODE/si, 1 ],
# },
# match_response => {
# status => qr/^200$/,
# },
# request => new HTTP::Request(
# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?host=www.modsecurity.org",
# ),
#},
{
type => "target",
comment => "GEO (failed lookup)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecGeoLookupDB GeoLiteCity.dat
SecRule REMOTE_ADDR "\@geoLookup" "pass,nolog"
SecRule \&GEO "\@eq 0" "deny,status:403,msg:'Failed to lookup IP'"
# SecRule ARGS:ip "\@geoLookup" "phase:2,log,pass,t:none"
# SecRule \&GEO "\@eq 0" "phase:2,log,deny,status:403,t:none"
# SecRule ARGS:badip "\@geoLookup" "phase:2,log,pass,t:none"
# SecRule \&GEO "!\@eq 0" "phase:2,log,deny,status:403,t:none"
),
match_log => {
-debug => [ qr/Geo lookup for "127\.0\.0\.1" succeeded/si, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?ip=216.75.21.122&badip=127.0.0.1",
),
},
# TODO: ENV
# TODO: FILES
# TODO: FILES_COMBINED_SIZE
# TODO: FILES_NAMES
# TODO: FILES_SIZES
# TODO: FILES_TMPNAMES
# TODO: GEO
# TODO: HIGHEST_SEVERITY
# TODO: MATCHED_VAR
# TODO: MATCHED_VAR_NAME

View File

@ -16,13 +16,14 @@
output => "16",
ret => 1,
},
{
type => "tfn",
name => "length",
input => ('x' x 8192),
output => "8192",
ret => 1,
},
# ENH: This sometimes fails w/4096 length
#{
# type => "tfn",
# name => "length",
# input => ('x' x 8192),
# output => "8192",
# ret => 1,
#},
### With TAB
{

View File

@ -6,7 +6,7 @@
Manual</title>
<articleinfo>
<releaseinfo>Version 2.6.0-trunk (April 22, 2009)</releaseinfo>
<releaseinfo>Version 2.6.0-trunk (May 15, 2009)</releaseinfo>
<copyright>
<year>2004-2009</year>
@ -2837,7 +2837,7 @@ SecRule <emphasis>ENV:tag</emphasis> "suspicious"</programlisting>
<title><literal moreinfo="none">GEO</literal></title>
<para><literal>GEO</literal> is a collection populated by the <literal
moreinfo="none">@geoLookups</literal> operator. It can be used to match
moreinfo="none">@geoLookup</literal> operator. It can be used to match
geographical fields looked up by an IP address or hostname.</para>
<para>Available since 2.2.0.</para>
@ -2847,7 +2847,7 @@ SecRule <emphasis>ENV:tag</emphasis> "suspicious"</programlisting>
<itemizedlist>
<listitem>
<para><emphasis>COUNTRY_CODE:</emphasis> Two character country code.
EX: US, UK, etc.</para>
EX: US, GB, etc.</para>
</listitem>
<listitem>
@ -2871,24 +2871,28 @@ SecRule <emphasis>ENV:tag</emphasis> "suspicious"</programlisting>
</listitem>
<listitem>
<para><emphasis>CITY:</emphasis> The city name.</para>
<para><emphasis>CITY:</emphasis> The city name if supported by the
database.</para>
</listitem>
<listitem>
<para><emphasis>POSTAL_CODE:</emphasis> The postal code.</para>
<para><emphasis>POSTAL_CODE:</emphasis> The postal code if supported
by the database.</para>
</listitem>
<listitem>
<para><emphasis>LATITUDE:</emphasis> The latitude.</para>
<para><emphasis>LATITUDE:</emphasis> The latitude if supported by
the database.</para>
</listitem>
<listitem>
<para><emphasis>LONGITUDE:</emphasis> The longitude.</para>
<para><emphasis>LONGITUDE:</emphasis> The longitude if supported by
the database.</para>
</listitem>
<listitem>
<para><emphasis>DMA_CODE:</emphasis> The metropolitan area code. (US
only)</para>
<para><emphasis>DMA_CODE:</emphasis> The metropolitan area code if
supported by the database. (US only)</para>
</listitem>
<listitem>
@ -2899,8 +2903,8 @@ SecRule <emphasis>ENV:tag</emphasis> "suspicious"</programlisting>
<para>Example:</para>
<programlisting format="linespecific">SecRule REMOTE_ADDR "<emphasis>@geoLookup</emphasis>" "chain,drop,msg:'Non-UK IP address'"
SecRule GEO:COUNTRY_CODE "!@streq UK"</programlisting>
<programlisting format="linespecific">SecRule REMOTE_ADDR "<emphasis>@geoLookup</emphasis>" "chain,drop,msg:'Non-GB IP address'"
SecRule GEO:COUNTRY_CODE "!@streq GB"</programlisting>
</section>
<section>
@ -5458,6 +5462,19 @@ SecRule ARGS:route "!<emphasis>@endsWith %{REQUEST_ADDR}</emphasis>" t:none,deny
moreinfo="none">SecGeoLookupDb</literal> before this operator can be
used.</para>
<note>
<para>This operator matches and the action is executed on a <emphasis>
successful</emphasis> lookup. For this reason, you probably want to
use the <emphasis>pass,nolog</emphasis> actions. This allows for
<literal moreinfo="none">setvar</literal> and other non-disruptive
actions to be executed on a match. If you wish to block on a failed
lookup, then do something like this (look for an empty GEO
collection):</para>
<programlisting format="linespecific">SecRule REMOTE_ADDR "@geoLookup" "pass,nolog"
SecRule &amp;GEO "@eq 0" "deny,status:403,msg:'Failed to lookup IP'"</programlisting>
</note>
<para>See the <literal moreinfo="none">GEO</literal> variable for an
example and more information on various fields available.</para>
</section>