diff --git a/policy.old/ftp.bro b/policy.old/ftp.bro index 4fc86df2c4..4686d2b6ee 100644 --- a/policy.old/ftp.bro +++ b/policy.old/ftp.bro @@ -287,7 +287,7 @@ function is_ftp_data_conn(c: connection): bool else if ( id$orig_p == 20/tcp && [$orig_h = id$resp_h, $orig_p = id$resp_p, - $resp_h = id$orig_h, $resp_p = 21/tcp] in ftp_sessions ) + $resp_h = id$orig_h, $resp_p = 21/tcp] in ftp_sessions ) return T; else return F; diff --git a/policy.old/portmapper.bro b/policy.old/portmapper.bro index 99ce096ee0..be576b3ffe 100644 --- a/policy.old/portmapper.bro +++ b/policy.old/portmapper.bro @@ -133,6 +133,11 @@ export { [NFS_world_servers, NFS_services], [sun-rpc.mcast.net, "ypserv"], # sigh } &redef; + + # Maps a given port on a given server's address to an RPC service. + # If we haven't loaded portmapper.bro, then it will be empty + # (and, ideally, queries to it would be optimized away ...). + global RPC_server_map: table[addr, port] of string; } redef capture_filters += { ["portmapper"] = "port 111" }; @@ -256,7 +261,7 @@ event pm_request_unset(r: connection, m: pm_mapping, success: bool) rpc_prog(m$program), m$p, success ? "ok" : "failed"), T); } -function update_RPC_server_map(server: addr, p: port, prog: string) +function update_RPC_server_map(r: connection, server: addr, p: port, prog: string) { if ( [server, p] in RPC_server_map ) { @@ -268,6 +273,8 @@ function update_RPC_server_map(server: addr, p: port, prog: string) } else RPC_server_map[server, p] = prog; + + add r$service[prog]; } event pm_request_getport(r: connection, pr: pm_port_request, p: port) @@ -275,12 +282,12 @@ event pm_request_getport(r: connection, pr: pm_port_request, p: port) local prog = rpc_prog(pr$program); local log_it = pm_check_getport(r, prog); - update_RPC_server_map(r$id$resp_h, p, prog); + update_RPC_server_map(r, r$id$resp_h, p, prog); pm_request(r, "pm_getport", fmt("%s -> %s", prog, p), log_it); } -function pm_mapping_to_text(server: addr, m: pm_mappings): string +function pm_mapping_to_text(r: connection, server: addr, m: pm_mappings): string { # Used to suppress multiple entries for multiple versions. local mapping_seen: set[count, port]; @@ -297,7 +304,7 @@ function pm_mapping_to_text(server: addr, m: pm_mappings): string add mapping_seen[prog, p]; addls[++num_addls] = fmt("%s -> %s", rpc_prog(prog), p); - update_RPC_server_map(server, p, rpc_prog(prog)); + update_RPC_server_map(r, server, p, rpc_prog(prog)); } } @@ -315,7 +322,7 @@ event pm_request_dump(r: connection, m: pm_mappings) { local log_it = [r$id$orig_h, r$id$resp_h] !in RPC_dump_okay; pm_request(r, "pm_dump", length(m) == 0 ? "(nil)" : "(done)", log_it); - append_addl(r, cat("<", pm_mapping_to_text(r$id$resp_h, m), ">")); + append_addl(r, cat("<", pm_mapping_to_text(r, r$id$resp_h, m), ">")); } event pm_request_callit(r: connection, call: pm_callit_request, p: port) diff --git a/policy/conn.bro b/policy/conn.bro new file mode 100644 index 0000000000..b32bb06637 --- /dev/null +++ b/policy/conn.bro @@ -0,0 +1,150 @@ +@load functions + +module Conn; + +export { + redef enum Log::ID += { CONN }; + type Log: record { + start_time: time; + orig_h: addr; + orig_p: count; + resp_h: addr; + resp_p: count; + proto: transport_proto; + service: string &default="other"; + duration: interval &default=0secs; + orig_bytes: count &default=0; + resp_bytes: count &default=0; + conn_state: string &default=""; + local_orig: bool &default=F; + addl: string &default=""; + history: string &default=""; + }; + + # Only log connections appear successful. + # TODO: implement this as a filter + const only_log_successful = T &redef; + + # Configure if only a certain direction of connection is desired. + # TODO: implement this as a filter + const logging = Enabled &redef; + + # If inbound/outbound connections are to be split into separate files. + # TODO: implement a log splitting option as a filter here too (inbound/outbound) + const split_log = F &redef; + + # This is where users can get access to the active Log record for a + # connection so they can extend and enhance the logged data. + global active_conns: table[conn_id] of Log; +} + +event bro_init() + { + Log::create_stream("CONN", "Conn::Log"); + Log::add_default_filter("CONN"); + } + +function conn_state(c: connection, trans: transport_proto): string + { + local os = c$orig$state; + local rs = c$resp$state; + + local o_inactive = os == TCP_INACTIVE || os == TCP_PARTIAL; + local r_inactive = rs == TCP_INACTIVE || rs == TCP_PARTIAL; + + if ( trans == tcp ) + { + if ( rs == TCP_RESET ) + { + if ( os == TCP_SYN_SENT || os == TCP_SYN_ACK_SENT || + (os == TCP_RESET && + c$orig$size == 0 && c$resp$size == 0) ) + return "REJ"; + else if ( o_inactive ) + return "RSTRH"; + else + return "RSTR"; + } + else if ( os == TCP_RESET ) + return r_inactive ? "RSTOS0" : "RSTO"; + else if ( rs == TCP_CLOSED && os == TCP_CLOSED ) + return "SF"; + else if ( os == TCP_CLOSED ) + return r_inactive ? "SH" : "S2"; + else if ( rs == TCP_CLOSED ) + return o_inactive ? "SHR" : "S3"; + else if ( os == TCP_SYN_SENT && rs == TCP_INACTIVE ) + return "S0"; + else if ( os == TCP_ESTABLISHED && rs == TCP_ESTABLISHED ) + return "S1"; + else + return "OTH"; + } + + else if ( trans == udp ) + { + if ( os == UDP_ACTIVE ) + return rs == UDP_ACTIVE ? "SF" : "S0"; + else + return rs == UDP_ACTIVE ? "SHR" : "OTH"; + } + + else + return "OTH"; + } + +function determine_service(c: connection): string + { + local service = ""; + for ( s in c$service ) + { + if ( sub_bytes(s, 0, 1) != "-" ) + service = service == "" ? s : cat(service, ",", s); + } + + return service == "" ? "other" : to_lower(service); + } + +function get_conn_log(c: connection): Log + { + local id = c$id; + local conn_log: Log; + if ( c$id in active_conns ) + conn_log = active_conns[id]; + else + { + conn_log$start_time=c$start_time; + conn_log$orig_h=id$orig_h; + conn_log$orig_p=port_to_count(id$orig_p); + conn_log$resp_h=id$resp_h; + conn_log$resp_p=port_to_count(id$resp_p); + conn_log$proto=get_port_transport_proto(id$resp_p); + conn_log$local_orig=is_local_addr(id$orig_h); + } + + conn_log$duration=c$duration; + conn_log$service=determine_service(c); + # TODO: these should optionally use Gregor's new + # actual byte counting code if it's enabled. + conn_log$orig_bytes=c$orig$size; + conn_log$resp_bytes=c$resp$size; + conn_log$conn_state=conn_state(c, get_port_transport_proto(c$id$resp_p)); + conn_log$addl=c$addl; + conn_log$history=c$history; + + return conn_log; + } + +event connection_established(c: connection) &priority = 10 + { + active_conns[c$id] = get_conn_log(c); + } + +event connection_state_remove(c: connection) &priority = 10 + { + local conn_log = get_conn_log(c); + Log::write("CONN", conn_log); + + if ( c$id in active_conns ) + delete active_conns[c$id]; + } diff --git a/policy/hot.conn.bro b/policy/hot.conn.bro new file mode 100644 index 0000000000..0ac070832c --- /dev/null +++ b/policy/hot.conn.bro @@ -0,0 +1,200 @@ +@load hot + +redef enum Notice += { + SensitiveConnection, # connection marked "hot" +}; + +# Whether to translate the local address in SensitiveConnection notices +# to a hostname. Meant as a demonstration of the "when" construct. +const xlate_hot_local_addr = F &redef; + +# The sets are indexed by the complete hot messages. +global hot_conns_reported: table[conn_id] of set[string]; + +const conn_closed = { TCP_CLOSED, TCP_RESET }; + +const state_graphic = { + ["OTH"] = "?>?", ["REJ"] = "[", + ["RSTO"] = ">]", ["RSTOS0"] = "}]", ["RSTR"] = ">[", ["RSTRH"] = "<[", + ["S0"] = "}", ["S1"] = ">", ["S2"] = "}2", ["S3"] = "}3", + ["SF"] = ">", ["SH"] = ">h", ["SHR"] = " 0 ) + log_hot_conn(c); + } + +event connection_state_remove(c: connection) &priority = -10 + { + local os = c$orig$state; + local rs = c$resp$state; + + if ( os == TCP_ESTABLISHED && rs == TCP_ESTABLISHED ) + # It was still active, no summary generated. + connection_gone(c, "remove"); + + else if ( (os == TCP_CLOSED || rs == TCP_CLOSED) && + (os == TCP_ESTABLISHED || rs == TCP_ESTABLISHED) ) + # One side has closed, the other hasn't - it's in state S2 + # or S3, hasn't been reported yet. + connection_gone(c, "remove"); + + delete hot_conns_reported[c$id]; + } + + +event partial_connection(c: connection) + { + if ( c$orig$state == TCP_PARTIAL && c$resp$state == TCP_INACTIVE ) + # This appears to be a stealth scan. Don't do hot-checking + # as there wasn't an established connection. + ; + else + { + Hot::check_hot(c, Hot::CONN_ESTABLISHED); + Hot::check_hot(c, Hot::APPL_ESTABLISHED); # assume it's been established + } + + if ( c$hot > 0 ) + log_hot_conn(c); + } + +event connection_attempt(c: connection) + { + Hot::check_spoof(c); + Hot::check_hot(c, Hot::CONN_ATTEMPTED); + } + +event connection_finished(c: connection) + { + if ( c$orig$size == 0 || c$resp$size == 0 ) + # Hard to get excited about this - not worth logging again. + c$hot = 0; + else + Hot::check_hot(c, Hot::CONN_FINISHED); + } + +event connection_partial_close(c: connection) + { + if ( c$orig$size == 0 || c$resp$size == 0 ) + # Hard to get excited about this - not worth logging again. + c$hot = 0; + else + Hot::check_hot(c, Hot::CONN_FINISHED); + } + +event connection_half_finished(c: connection) + { + Hot::check_hot(c, Hot::CONN_ATTEMPTED); + } + +event connection_rejected(c: connection) + { + Hot::check_hot(c, Hot::CONN_REJECTED); + } + +event connection_reset(c: connection) + { + Hot::check_hot(c, Hot::CONN_FINISHED); + } + +event connection_pending(c: connection) + { + if ( c$orig$state in conn_closed && + (c$resp$state == TCP_INACTIVE || c$resp$state == TCP_PARTIAL) ) + # This is a stray FIN or RST - don't bother reporting. + return; + + if ( c$orig$state == TCP_RESET || c$resp$state == TCP_RESET ) + # We already reported this connection when the RST + # occurred. + return; + + Hot::check_hot(c, Hot::CONN_FINISHED); + } + +function connection_gone(c: connection, gone_type: string) + { + if ( c$orig$size == 0 || c$resp$size == 0 ) + { + if ( c$orig$state == TCP_RESET && c$resp$state == TCP_INACTIVE) + # A bare RST, no other context. Ignore it. + return; + + # Hard to get excited about this - not worth logging again, + # per connection_finished(). + c$hot = 0; + } + else + Hot::check_hot(c, Hot::CONN_TIMEOUT); + }