From 13a09aa488e21ec581047249460425d338afe3c4 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 30 Aug 2011 17:02:07 -0500 Subject: [PATCH] Fixes for GeoIP support (addresses #538). - Missing GeoIP databases now generate warnings/errors that go through the reporter framework instead of hitting GeoIP's internal use of stderr - lookup_location now just queries for country code if the city database was not loaded, which gets rid of invalid database type errors. - lookup_location now leaves missing fields uninitialized in the returned geo_location record value. Updated existing scripts to check for initialized fields in geo_location records before use. - Fixed support for GeoIP's IPv6 API and databases --- cmake/FindLibGeoIP.cmake | 22 +++- config.h.in | 6 ++ .../protocols/smtp/detect-suspicious-orig.bro | 11 +- scripts/policy/protocols/ssh/geo-data.bro | 2 +- src/bro.bif | 101 ++++++++++++------ 5 files changed, 99 insertions(+), 43 deletions(-) diff --git a/cmake/FindLibGeoIP.cmake b/cmake/FindLibGeoIP.cmake index 618dba6463..529886d866 100644 --- a/cmake/FindLibGeoIP.cmake +++ b/cmake/FindLibGeoIP.cmake @@ -13,9 +13,11 @@ # # Variables defined by this module: # -# LIBGEOIP_FOUND System has GeoIP libraries and headers -# LibGeoIP_LIBRARY The GeoIP library -# LibGeoIP_INCLUDE_DIR The location of GeoIP headers +# LIBGEOIP_FOUND System has GeoIP libraries and headers +# LibGeoIP_LIBRARY The GeoIP library +# LibGeoIP_INCLUDE_DIR The location of GeoIP headers +# HAVE_GEOIP_COUNTRY_EDITION_V6 Whether the API support IPv6 country edition +# HAVE_GEOIP_CITY_EDITION_REV0_V6 Whether the API supports IPv6 city edition find_path(LibGeoIP_ROOT_DIR NAMES include/GeoIPCity.h @@ -45,6 +47,20 @@ find_package_handle_standard_args(LibGeoIP DEFAULT_MSG LibGeoIP_INCLUDE_DIR ) +if (LIBGEOIP_FOUND) + include(CheckCXXSourceCompiles) + set(CMAKE_REQUIRED_INCLUDES ${LibGeoIP_INCLUDE_DIR}) + check_cxx_source_compiles(" + #include + int main() { GEOIP_COUNTRY_EDITION_V6; return 0; } + " HAVE_GEOIP_COUNTRY_EDITION_V6) + check_cxx_source_compiles(" + #include + int main() { GEOIP_CITY_EDITION_REV0_V6; return 0; } + " HAVE_GEOIP_CITY_EDITION_REV0_V6) + set(CMAKE_REQUIRED_INCLUDES) +endif () + mark_as_advanced( LibGeoIP_ROOT_DIR LibGeoIP_LIBRARY diff --git a/config.h.in b/config.h.in index 2820875b78..3783a8390d 100644 --- a/config.h.in +++ b/config.h.in @@ -114,6 +114,12 @@ /* GeoIP geographic lookup functionality */ #cmakedefine USE_GEOIP +/* Whether the found GeoIP API supports IPv6 Country Edition */ +#cmakedefine HAVE_GEOIP_COUNTRY_EDITION_V6 + +/* Whether the found GeoIP API supports IPv6 City Edition */ +#cmakedefine HAVE_GEOIP_CITY_EDITION_REV0_V6 + /* Use Google's perftools */ #cmakedefine USE_PERFTOOLS diff --git a/scripts/policy/protocols/smtp/detect-suspicious-orig.bro b/scripts/policy/protocols/smtp/detect-suspicious-orig.bro index 8e85b8db97..4635b17a83 100644 --- a/scripts/policy/protocols/smtp/detect-suspicious-orig.bro +++ b/scripts/policy/protocols/smtp/detect-suspicious-orig.bro @@ -25,11 +25,13 @@ event log_smtp(rec: Info) ip = rec$x_originating_ip; loc = lookup_location(ip); - if ( loc$country_code in suspicious_origination_countries || + if ( (loc?$country_code && + loc$country_code in suspicious_origination_countries) || ip in suspicious_origination_networks ) { NOTICE([$note=Suspicious_Origination, - $msg=fmt("An email originated from %s (%s).", loc$country_code, ip), + $msg=fmt("An email originated from %s (%s).", + loc?$country_code ? loc$country_code : "", ip), $id=rec$id]); } } @@ -38,11 +40,12 @@ event log_smtp(rec: Info) ip = rec$path[|rec$path|-1]; loc = lookup_location(ip); - if ( loc$country_code in suspicious_origination_countries || + if ( (loc?$country_code && + loc$country_code in suspicious_origination_countries) || ip in suspicious_origination_networks ) { NOTICE([$note=Suspicious_Origination, - $msg=fmt("Based up Received headers, email originated from %s (%s).", loc$country_code, ip), + $msg=fmt("Based up Received headers, email originated from %s (%s).", loc?$country_code ? loc$country_code : "", ip), $id=rec$id]); } } diff --git a/scripts/policy/protocols/ssh/geo-data.bro b/scripts/policy/protocols/ssh/geo-data.bro index 4dfd8bbae4..1ac38e5a5b 100644 --- a/scripts/policy/protocols/ssh/geo-data.bro +++ b/scripts/policy/protocols/ssh/geo-data.bro @@ -33,7 +33,7 @@ event SSH::heuristic_successful_login(c: connection) &priority=5 # Add the location data to the SSH record. c$ssh$remote_location = location; - if ( location$country_code in watched_countries ) + if ( location?$country_code && location$country_code in watched_countries ) { NOTICE([$note=Login_From_Watched_Country, $conn=c, diff --git a/src/bro.bif b/src/bro.bif index e4d0f2092b..590f34b56b 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -3019,6 +3019,18 @@ function syslog%(s: string%): any extern "C" { #include } + +static GeoIP* open_geoip_db(GeoIPDBTypes type) + { + GeoIP* geoip = 0; + if ( GeoIP_db_avail(type) ) + geoip = GeoIP_open_type(type, GEOIP_MEMORY_CACHE); + if ( ! geoip ) + reporter->Warning("Failed to open GeoIP database: %s", + GeoIPDBFileName[type]); + return geoip; + } + #endif %%} @@ -3031,82 +3043,103 @@ function lookup_location%(a: addr%) : geo_location static GeoIP* geoip = 0; static GeoIP* geoip_v6 = 0; static bool geoip_initialized = false; + static bool have_city_db = false; + static bool have_cityv6_db = false; GeoIPRecord* gir = 0; + const char* cc = 0; if ( ! geoip_initialized ) { geoip_initialized = true; - geoip = GeoIP_open_type(GEOIP_CITY_EDITION_REV0, - GEOIP_MEMORY_CACHE); + + geoip = open_geoip_db(GEOIP_CITY_EDITION_REV0); + if ( ! geoip ) { - builtin_error("can't initialize GeoIP City database.. trying Country version"); - geoip = GeoIP_open_type(GEOIP_COUNTRY_EDITION, - GEOIP_MEMORY_CACHE); + geoip = open_geoip_db(GEOIP_COUNTRY_EDITION); if ( ! geoip ) - builtin_error("can't initialize GeoIP Country database"); + builtin_error("Can't initialize GeoIP City/Country database"); + else + reporter->Warning("Fell back to GeoIP Country database"); } + else + have_city_db = true; #ifdef BROv6 -#ifdef GEOIP_COUNTRY_EDITION_V6 - geoip_v6 = GeoIP_open_type(GEOIP_COUNTRY_EDITION_V6, - GEOIP_MEMORY_CACHE); - if ( ! geoip_v6 ) - builtin_error("can't initialize the GeoIPv6 Country database"); +#ifdef HAVE_GEOIP_CITY_EDITION_REV0_V6 + geoip_v6 = open_geoip_db(GEOIP_CITY_EDITION_REV0_V6); + if ( geoip_v6 ) have_cityv6_db = true; #endif +#ifdef HAVE_GEOIP_COUNTRY_EDITION_V6 + if ( ! geoip_v6 ) + geoip_v6 = open_geoip_db(GEOIP_COUNTRY_EDITION_V6); +#endif + if ( ! geoip_v6 ) + builtin_error("Can't initialize GeoIPv6 City/Country database"); #endif } #ifdef BROv6 -#ifdef GEOIP_COUNTRY_EDITION_V6 +#ifdef HAVE_GEOIP_COUNTRY_EDITION_V6 if ( geoip_v6 && ! is_v4_addr(a) ) - gir = GeoIP_record_by_ipnum_v6(geoip_v6, geoipv6_t(a)); + { + geoipv6_t ga; + memcpy(&ga, a, 16); + if ( have_cityv6_db ) + gir = GeoIP_record_by_ipnum_v6(geoip_v6, ga); + else + cc = GeoIP_country_code_by_ipnum_v6(geoip_v6, ga); + } else #endif if ( geoip && is_v4_addr(a) ) { uint32 addr = to_v4_addr(a); - gir = GeoIP_record_by_ipnum(geoip, ntohl(addr)); + if ( have_city_db ) + gir = GeoIP_record_by_ipnum(geoip, ntohl(addr)); + else + cc = GeoIP_country_code_by_ipnum(geoip, ntohl(addr)); } #else if ( geoip ) - gir = GeoIP_record_by_ipnum(geoip, ntohl(a)); + { + if ( have_city_db ) + gir = GeoIP_record_by_ipnum(geoip, ntohl(a)); + else + cc = GeoIP_country_code_by_ipnum(geoip, ntohl(a)); + } #endif if ( gir ) { if ( gir->country_code ) location->Assign(0, new StringVal(gir->country_code)); - else - location->Assign(0, new StringVal("")); if ( gir->region ) location->Assign(1, new StringVal(gir->region)); - else - location->Assign(1, new StringVal("")); if ( gir->city ) location->Assign(2, new StringVal(gir->city)); - else - location->Assign(2, new StringVal("")); if ( gir->latitude ) location->Assign(3, new Val(gir->latitude, TYPE_DOUBLE)); - else - location->Assign(3, new Val(0.0, TYPE_DOUBLE)); if ( gir->longitude ) location->Assign(4, new Val(gir->longitude, TYPE_DOUBLE)); - else - location->Assign(4, new Val(0.0, TYPE_DOUBLE)); GeoIPRecord_delete(gir); return location; } + else if (cc) + { + location->Assign(0, new StringVal(cc)); + return location; + } + #else static int missing_geoip_reported = 0; @@ -3120,11 +3153,6 @@ function lookup_location%(a: addr%) : geo_location // We can get here even if we have GeoIP support if we weren't // able to initialize it or it didn't return any information for // the address. - location->Assign(0, new StringVal("")); - location->Assign(1, new StringVal("")); - location->Assign(2, new StringVal("")); - location->Assign(3, new Val(0.0, TYPE_DOUBLE)); - location->Assign(4, new Val(0.0, TYPE_DOUBLE)); return location; %} @@ -3139,10 +3167,9 @@ function lookup_asn%(a: addr%) : count if ( ! geoip_asn_initialized ) { geoip_asn_initialized = true; - geoip_asn = GeoIP_open_type(GEOIP_ASNUM_EDITION, - GEOIP_MEMORY_CACHE); + geoip_asn = open_geoip_db(GEOIP_ASNUM_EDITION); if ( ! geoip_asn ) - builtin_error("can't initialize GeoIP ASNUM database"); + builtin_error("Can't initialize GeoIP ASNUM database"); } if ( geoip_asn ) @@ -3150,9 +3177,13 @@ function lookup_asn%(a: addr%) : count #ifdef BROv6 // IPv6 support showed up in 1.4.5. -#ifdef GEOIP_COUNTRY_EDITION_V6 +#ifdef HAVE_GEOIP_COUNTRY_EDITION_V6 if ( ! is_v4_addr(a) ) - gir = GeoIP_name_by_ipnum_v6(geoip_asn, geoipv6_t(a)); + { + geoipv6_t ga; + memcpy(&ga, a, 16); + gir = GeoIP_name_by_ipnum_v6(geoip_asn, ga); + } else #endif if ( is_v4_addr(a) )