New default notice action for emailing network admins.

- When ACTION_EMAIL_ADMIN is applied to a notice,
  the email addresses associated with the address
  are collected from the new local_admins table
  and the email is sent to all discovered email addresses.
- The site.bro script is now in the Site module.
- Some other small cleanup.
This commit is contained in:
Seth Hall 2011-06-25 00:57:02 -04:00
parent 09e242f98f
commit 71d6488637
7 changed files with 97 additions and 35 deletions

View file

@ -16,7 +16,7 @@ export {
## also used by the global function :bro:id:`NOTICE`.
NOTICE,
## This is the notice policy auditing log. It records what the current
## notice policy is at Bro init time..
## notice policy is at Bro init time.
NOTICE_POLICY,
};
@ -38,11 +38,15 @@ export {
ACTION_FILE,
## Indicates that the notice should be alarmed on.
ACTION_ALARM,
## Indicates that the notice should be sent to the configured notice
## contact email address(es).
## Indicates that the notice should be sent to the email address(es)
## configured in the :bro:id:`mail_dest` variable.
ACTION_EMAIL,
## Indicates that the notice should be sent to the configured pager
## email address.
## Indicate that the generated email should be addressed to the
## appropriate addresses as found in the :bro:id:`Site::addr_to_emails`
## variable.
ACTION_EMAIL_ADMIN,
## Indicates that the notice should be sent to the pager email address
## configured in the :bro:id:`mail_page_dest` variable.
ACTION_PAGE,
## Indicates that no more actions should be found after the policy
## item returning this matched.
@ -113,15 +117,15 @@ export {
# This is the :bro:id:`Notice::policy` where the local notice conversion
# policy is set.
const policy: set[Notice::PolicyItem] = {
[$pred(n: Notice::Info) = { return T; },
$result = ACTION_FILE,
$priority = 0],
[$pred(n: Notice::Info) = { return (n$note in ignored_types); },
$result = ACTION_STOP,
$priority = 10],
[$pred(n: Notice::Info) = { return (n$note in emailed_types); },
$result = ACTION_EMAIL,
$priority = 9],
[$pred(n: Notice::Info) = { return T; },
$result = ACTION_FILE,
$priority = 0],
} &redef;
## Local system mail program.

View file

@ -37,8 +37,8 @@ export {
## If the connection is originated locally, this value will be T. If
## it was originated remotely it will be F. In the case that the
## :bro:id:`local_nets` variable is undefined, this field will be left
## empty at all times.
## :bro:id:`Site::local_nets` variable is undefined, this field will
## be left empty at all times.
local_orig: bool &log &optional;
## Indicates the number of bytes missed in content gaps which is
@ -154,8 +154,8 @@ function set_conn(c: connection, eoc: bool)
tmp$uid=c$uid;
tmp$id=id;
tmp$proto=get_port_transport_proto(id$resp_p);
if( |local_nets| > 0 )
tmp$local_orig=is_local_addr(id$orig_h);
if( |Site::local_nets| > 0 )
tmp$local_orig=Site::is_local_addr(id$orig_h);
c$conn = tmp;
}

View file

@ -17,21 +17,21 @@ export {
redef enum Notice::Type += {
## Raised when a non-local name is found to be pointing at a local host.
## This only works appropriately when all of your authoritative DNS
## servers are located in your :bro:id:`local_nets`.
## servers are located in your :bro:id:`Site::local_nets`.
DNS_ExternalName,
};
}
event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) &priority=-3
{
if ( |local_zones| == 0 )
if ( |Site::local_zones| == 0 )
return;
# Check for responses from remote hosts that point at local hosts
# but the name is not considered to be within a "local" zone.
if ( is_local_addr(a) && # referring to a local host
!is_local_addr(c$id$resp_h) && # response from an external nameserver
!is_local_name(ans$query) ) # name isn't in a local zone.
if ( Site::is_local_addr(a) && # referring to a local host
!Site::is_local_addr(c$id$resp_h) && # response from an external nameserver
!Site::is_local_name(ans$query) ) # name isn't in a local zone.
{
NOTICE([$note=DNS_ExternalName,
$msg=fmt("%s is pointing to a local host - %s.", ans$query, a),

View file

@ -339,7 +339,7 @@ event smtp_data(c: connection, is_orig: bool, data: string) &priority=3
local ip = to_addr(text_ip);
if ( ! addr_matches_host(ip, mail_path_capture) &&
ip !in private_address_space )
! Site::is_private_addr(ip) )
{
c$smtp$process_received_from = F;
}

View file

@ -8,8 +8,8 @@ export {
redef enum Notice::Type += {
Login,
PasswordGuessing,
LoginByPasswordGuesser,
Password_Guessing,
Login_By_Password_Guesser,
Login_From_Interesting_Hostname,
Bytecount_Inconsistency,
};
@ -88,12 +88,6 @@ event bro_init()
Log::create_stream(SSH, [$columns=Info, $ev=log_ssh]);
}
# TODO: move this elsewhere
function local_filter(rec: record { id: conn_id; } ): bool
{
return is_local_addr(rec$id$resp_h);
}
function set_session(c: connection)
{
if ( ! c?$ssh )
@ -124,7 +118,7 @@ function check_ssh_connection(c: connection, done: bool)
return;
local status = "failure";
local direction = is_local_addr(c$id$orig_h) ? "to" : "from";
local direction = Site::is_local_addr(c$id$orig_h) ? "to" : "from";
local location: geo_location;
location = (direction == "to") ? lookup_location(c$id$resp_h) : lookup_location(c$id$orig_h);
@ -142,7 +136,7 @@ function check_ssh_connection(c: connection, done: bool)
if ( default_check_threshold(password_rejections[c$id$orig_h]) )
{
add password_guessers[c$id$orig_h];
NOTICE([$note=PasswordGuessing,
NOTICE([$note=Password_Guessing,
$conn=c,
$msg=fmt("SSH password guessing by %s", c$id$orig_h),
$sub=fmt("%d failed logins", password_rejections[c$id$orig_h]$n),
@ -163,7 +157,7 @@ function check_ssh_connection(c: connection, done: bool)
c$id$orig_h !in password_guessers )
{
add password_guessers[c$id$orig_h];
NOTICE([$note=LoginByPasswordGuesser,
NOTICE([$note=Login_By_Password_Guesser,
$conn=c,
$n=password_rejections[c$id$orig_h]$n,
$msg=fmt("Successful SSH login by password guesser %s", c$id$orig_h),
@ -180,7 +174,7 @@ function check_ssh_connection(c: connection, done: bool)
$sub=location$country_code]);
# Check to see if this login came from an interesting hostname
when( local hostname = lookup_addr(c$id$orig_h) )
when ( local hostname = lookup_addr(c$id$orig_h) )
{
if ( interesting_hostnames in hostname )
{
@ -190,6 +184,12 @@ function check_ssh_connection(c: connection, done: bool)
$sub=hostname]);
}
}
if ( location$country_code in watched_countries )
{
}
}
else if ( c$resp$size >= 200000000 )
{

View file

@ -2,7 +2,7 @@
##! and "neighbors", and servers running particular services.
@load utils/pattern
module GLOBAL;
module Site;
export {
## Address space that is considered private and unrouted.
@ -20,6 +20,12 @@ export {
## Networks that are considered "neighbors".
const neighbor_nets: set[subnet] &redef;
## If local network administrators are known and they have responsibility
## for defined address space, then a mapping can be defined here between
## networks for which they have responsibility and a set of email
## addresses.
const local_admins: table[subnet] of set[string] = {} &redef;
## DNS zones that are considered "local".
const local_zones: set[string] &redef;
@ -34,6 +40,10 @@ export {
## the neighbor networks, false if not.
global is_neighbor_addr: function(a: addr): bool;
## Function that returns true if an address corresponds to one of
## the private/unrouted networks, false if not.
global is_private_addr: function(a: addr): bool;
## Function that returns true if a host name is within a local
## DNS zone.
global is_local_name: function(name: string): bool;
@ -42,6 +52,10 @@ export {
## DNS zone.
global is_neighbor_name: function(name: string): bool;
## Function that returns a common separated list of email addresses
## that are considered administrators for the IP address provided as
## an argument.
global get_emails: function(a: addr): string;
}
# Please ignore, this is an interally used variable.
@ -74,6 +88,48 @@ function is_neighbor_name(name: string): bool
return local_dns_neighbor_suffix_regex in name;
}
# This is a hack for doing a for loop.
const one_to_32: vector of count = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32};
# TODO: make this work with IPv6
function find_all_emails(ip: addr): set[string]
{
if ( ip !in local_admins ) return set();
local output_values: set[string] = set();
local tmp_ip: addr;
local i: count;
local emails: string;
for ( i in one_to_32 )
{
tmp_ip = mask_addr(ip, one_to_32[i]);
for ( email in local_admins[tmp_ip] )
{
if ( email != "" )
add output_values[email];
}
}
return output_values;
}
function fmt_email_string(emails: set[string]): string
{
local output="";
for( email in emails )
{
if ( output == "" )
output = email;
else
output = fmt("%s, %s", output, email);
}
return output;
}
function get_emails(a: addr): string
{
return fmt_email_string(find_all_emails(a));
}
event bro_init() &priority=10
{
# Double backslashes are needed due to string parsing.

View file

@ -1,11 +1,13 @@
@load site
type Direction: enum { INBOUND, OUTBOUND, BIDIRECTIONAL, NO_DIRECTION };
function id_matches_direction(id: conn_id, d: Direction): bool
{
if ( d == NO_DIRECTION ) return F;
return ( d == BIDIRECTIONAL ||
(d == OUTBOUND && is_local_addr(id$orig_h)) ||
(d == INBOUND && is_local_addr(id$resp_h)) );
(d == OUTBOUND && Site::is_local_addr(id$orig_h)) ||
(d == INBOUND && Site::is_local_addr(id$resp_h)) );
}
type Host: enum { LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS };
@ -14,6 +16,6 @@ function addr_matches_host(ip: addr, h: Host): bool
if ( h == NO_HOSTS ) return F;
return ( h == ALL_HOSTS ||
(h == LOCAL_HOSTS && is_local_addr(ip)) ||
(h == REMOTE_HOSTS && !is_local_addr(ip)) );
(h == LOCAL_HOSTS && Site::is_local_addr(ip)) ||
(h == REMOTE_HOSTS && !Site::is_local_addr(ip)) );
}