diff --git a/policy/frameworks/notice/base.bro b/policy/frameworks/notice/base.bro index f6191806f8..611ec0f07a 100644 --- a/policy/frameworks/notice/base.bro +++ b/policy/frameworks/notice/base.bro @@ -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. diff --git a/policy/protocols/conn/base.bro b/policy/protocols/conn/base.bro index 0d8789656d..ac569fc6da 100644 --- a/policy/protocols/conn/base.bro +++ b/policy/protocols/conn/base.bro @@ -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; } diff --git a/policy/protocols/dns/detect.bro b/policy/protocols/dns/detect.bro index a1d4575f3b..181ad4e5f7 100644 --- a/policy/protocols/dns/detect.bro +++ b/policy/protocols/dns/detect.bro @@ -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), diff --git a/policy/protocols/smtp/base.bro b/policy/protocols/smtp/base.bro index 7d7781c8a5..1bdcfa00f2 100644 --- a/policy/protocols/smtp/base.bro +++ b/policy/protocols/smtp/base.bro @@ -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; } diff --git a/policy/protocols/ssh/base.bro b/policy/protocols/ssh/base.bro index b2b6439fe7..9a1b42de00 100644 --- a/policy/protocols/ssh/base.bro +++ b/policy/protocols/ssh/base.bro @@ -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 ) { diff --git a/policy/site.bro b/policy/site.bro index 1ad8df2619..f6e14f457f 100644 --- a/policy/site.bro +++ b/policy/site.bro @@ -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. @@ -19,6 +19,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; @@ -33,6 +39,10 @@ export { ## Function that returns true if an address corresponds to one of ## 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. @@ -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. diff --git a/policy/utils/directions-and-hosts.bro b/policy/utils/directions-and-hosts.bro index edd8e96e72..ae5feb7761 100644 --- a/policy/utils/directions-and-hosts.bro +++ b/policy/utils/directions-and-hosts.bro @@ -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)) ); }