mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 22:58:20 +00:00
Merge origin/master into topic/vladg/radius
This commit is contained in:
commit
df99f87dbf
617 changed files with 20195 additions and 5690 deletions
|
@ -63,15 +63,17 @@ export {
|
|||
## The DNS query was rejected by the server.
|
||||
rejected: bool &log &default=F;
|
||||
|
||||
## This value indicates if this request/response pair is ready
|
||||
## to be logged.
|
||||
ready: bool &default=F;
|
||||
## The total number of resource records in a reply message's
|
||||
## answer section.
|
||||
total_answers: count &optional;
|
||||
## The total number of resource records in a reply message's
|
||||
## answer, authority, and additional sections.
|
||||
total_replies: count &optional;
|
||||
|
||||
## Whether the full DNS query has been seen.
|
||||
saw_query: bool &default=F;
|
||||
## Whether the full DNS reply has been seen.
|
||||
saw_reply: bool &default=F;
|
||||
};
|
||||
|
||||
## An event that can be handled to access the :bro:type:`DNS::Info`
|
||||
|
@ -90,7 +92,7 @@ export {
|
|||
## ans: The general information of a RR response.
|
||||
##
|
||||
## reply: The specific response information according to RR type/class.
|
||||
global do_reply: event(c: connection, msg: dns_msg, ans: dns_answer, reply: string);
|
||||
global do_reply: hook(c: connection, msg: dns_msg, ans: dns_answer, reply: string);
|
||||
|
||||
## A hook that is called whenever a session is being set.
|
||||
## This can be used if additional initialization logic needs to happen
|
||||
|
@ -103,17 +105,37 @@ export {
|
|||
## is_query: Indicator for if this is being called for a query or a response.
|
||||
global set_session: hook(c: connection, msg: dns_msg, is_query: bool);
|
||||
|
||||
## Yields a queue of :bro:see:`DNS::Info` objects for a given
|
||||
## DNS message query/transaction ID.
|
||||
type PendingMessages: table[count] of Queue::Queue;
|
||||
|
||||
## The amount of time that DNS queries or replies for a given
|
||||
## query/transaction ID are allowed to be queued while waiting for
|
||||
## a matching reply or query.
|
||||
const pending_msg_expiry_interval = 2min &redef;
|
||||
|
||||
## Give up trying to match pending DNS queries or replies for a given
|
||||
## query/transaction ID once this number of unmatched queries or replies
|
||||
## is reached (this shouldn't happen unless either the DNS server/resolver
|
||||
## is broken, Bro is not seeing all the DNS traffic, or an AXFR query
|
||||
## response is ongoing).
|
||||
const max_pending_msgs = 50 &redef;
|
||||
|
||||
## Give up trying to match pending DNS queries or replies across all
|
||||
## query/transaction IDs once there is at least one unmatched query or
|
||||
## reply across this number of different query IDs.
|
||||
const max_pending_query_ids = 50 &redef;
|
||||
|
||||
## A record type which tracks the status of DNS queries for a given
|
||||
## :bro:type:`connection`.
|
||||
type State: record {
|
||||
## Indexed by query id, returns Info record corresponding to
|
||||
## query/response which haven't completed yet.
|
||||
pending: table[count] of Queue::Queue;
|
||||
## queries that haven't been matched with a response yet.
|
||||
pending_queries: PendingMessages;
|
||||
|
||||
## This is the list of DNS responses that have completed based
|
||||
## on the number of responses declared and the number received.
|
||||
## The contents of the set are transaction IDs.
|
||||
finished_answers: set[count];
|
||||
## Indexed by query id, returns Info record corresponding to
|
||||
## replies that haven't been matched with a query yet.
|
||||
pending_replies: PendingMessages;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -143,6 +165,66 @@ function new_session(c: connection, trans_id: count): Info
|
|||
return info;
|
||||
}
|
||||
|
||||
function log_unmatched_msgs_queue(q: Queue::Queue)
|
||||
{
|
||||
local infos: vector of Info;
|
||||
Queue::get_vector(q, infos);
|
||||
|
||||
for ( i in infos )
|
||||
{
|
||||
event flow_weird("dns_unmatched_msg",
|
||||
infos[i]$id$orig_h, infos[i]$id$resp_h);
|
||||
Log::write(DNS::LOG, infos[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function log_unmatched_msgs(msgs: PendingMessages)
|
||||
{
|
||||
for ( trans_id in msgs )
|
||||
log_unmatched_msgs_queue(msgs[trans_id]);
|
||||
|
||||
clear_table(msgs);
|
||||
}
|
||||
|
||||
function enqueue_new_msg(msgs: PendingMessages, id: count, msg: Info)
|
||||
{
|
||||
if ( id !in msgs )
|
||||
{
|
||||
if ( |msgs| > max_pending_query_ids )
|
||||
{
|
||||
event flow_weird("dns_unmatched_query_id_quantity",
|
||||
msg$id$orig_h, msg$id$resp_h);
|
||||
# Throw away all unmatched on assumption they'll never be matched.
|
||||
log_unmatched_msgs(msgs);
|
||||
}
|
||||
|
||||
msgs[id] = Queue::init();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( Queue::len(msgs[id]) > max_pending_msgs )
|
||||
{
|
||||
event flow_weird("dns_unmatched_msg_quantity",
|
||||
msg$id$orig_h, msg$id$resp_h);
|
||||
log_unmatched_msgs_queue(msgs[id]);
|
||||
# Throw away all unmatched on assumption they'll never be matched.
|
||||
msgs[id] = Queue::init();
|
||||
}
|
||||
}
|
||||
|
||||
Queue::put(msgs[id], msg);
|
||||
}
|
||||
|
||||
function pop_msg(msgs: PendingMessages, id: count): Info
|
||||
{
|
||||
local rval: Info = Queue::get(msgs[id]);
|
||||
|
||||
if ( Queue::len(msgs[id]) == 0 )
|
||||
delete msgs[id];
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
hook set_session(c: connection, msg: dns_msg, is_query: bool) &priority=5
|
||||
{
|
||||
if ( ! c?$dns_state )
|
||||
|
@ -151,29 +233,39 @@ hook set_session(c: connection, msg: dns_msg, is_query: bool) &priority=5
|
|||
c$dns_state = state;
|
||||
}
|
||||
|
||||
if ( msg$id !in c$dns_state$pending )
|
||||
c$dns_state$pending[msg$id] = Queue::init();
|
||||
|
||||
local info: Info;
|
||||
# If this is either a query or this is the reply but
|
||||
# no Info records are in the queue (we missed the query?)
|
||||
# we need to create an Info record and put it in the queue.
|
||||
if ( is_query ||
|
||||
Queue::len(c$dns_state$pending[msg$id]) == 0 )
|
||||
{
|
||||
info = new_session(c, msg$id);
|
||||
Queue::put(c$dns_state$pending[msg$id], info);
|
||||
}
|
||||
|
||||
if ( is_query )
|
||||
# If this is a query, assign the newly created info variable
|
||||
# so that the world looks correct to anything else handling
|
||||
# this query.
|
||||
c$dns = info;
|
||||
{
|
||||
if ( msg$id in c$dns_state$pending_replies &&
|
||||
Queue::len(c$dns_state$pending_replies[msg$id]) > 0 )
|
||||
{
|
||||
# Match this DNS query w/ what's at head of pending reply queue.
|
||||
c$dns = pop_msg(c$dns_state$pending_replies, msg$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
# Create a new DNS session and put it in the query queue so
|
||||
# we can wait for a matching reply.
|
||||
c$dns = new_session(c, msg$id);
|
||||
enqueue_new_msg(c$dns_state$pending_queries, msg$id, c$dns);
|
||||
}
|
||||
}
|
||||
else
|
||||
# Peek at the next item in the queue for this trans_id and
|
||||
# assign it to c$dns since this is a response.
|
||||
c$dns = Queue::peek(c$dns_state$pending[msg$id]);
|
||||
{
|
||||
if ( msg$id in c$dns_state$pending_queries &&
|
||||
Queue::len(c$dns_state$pending_queries[msg$id]) > 0 )
|
||||
{
|
||||
# Match this DNS reply w/ what's at head of pending query queue.
|
||||
c$dns = pop_msg(c$dns_state$pending_queries, msg$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
# Create a new DNS session and put it in the reply queue so
|
||||
# we can wait for a matching query.
|
||||
c$dns = new_session(c, msg$id);
|
||||
event conn_weird("dns_unmatched_reply", c, "");
|
||||
enqueue_new_msg(c$dns_state$pending_replies, msg$id, c$dns);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! is_query )
|
||||
{
|
||||
|
@ -183,36 +275,36 @@ hook set_session(c: connection, msg: dns_msg, is_query: bool) &priority=5
|
|||
if ( ! c$dns?$total_answers )
|
||||
c$dns$total_answers = msg$num_answers;
|
||||
|
||||
if ( c$dns?$total_replies &&
|
||||
c$dns$total_replies != msg$num_answers + msg$num_addl + msg$num_auth )
|
||||
{
|
||||
event conn_weird("dns_changed_number_of_responses", c,
|
||||
fmt("The declared number of responses changed from %d to %d",
|
||||
c$dns$total_replies,
|
||||
msg$num_answers + msg$num_addl + msg$num_auth));
|
||||
}
|
||||
else
|
||||
{
|
||||
# Store the total number of responses expected from the first reply.
|
||||
if ( ! c$dns?$total_replies )
|
||||
c$dns$total_replies = msg$num_answers + msg$num_addl + msg$num_auth;
|
||||
}
|
||||
|
||||
if ( msg$rcode != 0 && msg$num_queries == 0 )
|
||||
c$dns$rejected = T;
|
||||
}
|
||||
}
|
||||
|
||||
event dns_message(c: connection, is_orig: bool, msg: dns_msg, len: count) &priority=5
|
||||
{
|
||||
hook set_session(c, msg, is_orig);
|
||||
if ( msg$opcode != 0 )
|
||||
# Currently only standard queries are tracked.
|
||||
return;
|
||||
|
||||
hook set_session(c, msg, ! msg$QR);
|
||||
}
|
||||
|
||||
event DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string) &priority=5
|
||||
hook DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string) &priority=5
|
||||
{
|
||||
if ( msg$opcode != 0 )
|
||||
# Currently only standard queries are tracked.
|
||||
return;
|
||||
|
||||
if ( ! msg$QR )
|
||||
# This is weird: the inquirer must also be providing answers in
|
||||
# the request, which is not what we want to track.
|
||||
return;
|
||||
|
||||
if ( ans$answer_type == DNS_ANS )
|
||||
{
|
||||
if ( ! c?$dns )
|
||||
{
|
||||
event conn_weird("dns_unmatched_reply", c, "");
|
||||
hook set_session(c, msg, F);
|
||||
}
|
||||
c$dns$AA = msg$AA;
|
||||
c$dns$RA = msg$RA;
|
||||
|
||||
|
@ -226,29 +318,35 @@ event DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string)
|
|||
c$dns$TTLs = vector();
|
||||
c$dns$TTLs[|c$dns$TTLs|] = ans$TTL;
|
||||
}
|
||||
|
||||
if ( c$dns?$answers && c$dns?$total_answers &&
|
||||
|c$dns$answers| == c$dns$total_answers )
|
||||
{
|
||||
# Indicate this request/reply pair is ready to be logged.
|
||||
c$dns$ready = T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string) &priority=-5
|
||||
event dns_end(c: connection, msg: dns_msg) &priority=5
|
||||
{
|
||||
if ( c$dns$ready )
|
||||
if ( ! c?$dns )
|
||||
return;
|
||||
|
||||
if ( msg$QR )
|
||||
c$dns$saw_reply = T;
|
||||
else
|
||||
c$dns$saw_query = T;
|
||||
}
|
||||
|
||||
event dns_end(c: connection, msg: dns_msg) &priority=-5
|
||||
{
|
||||
if ( c?$dns && c$dns$saw_reply && c$dns$saw_query )
|
||||
{
|
||||
Log::write(DNS::LOG, c$dns);
|
||||
# This record is logged and no longer pending.
|
||||
Queue::get(c$dns_state$pending[c$dns$trans_id]);
|
||||
delete c$dns;
|
||||
}
|
||||
}
|
||||
|
||||
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) &priority=5
|
||||
{
|
||||
if ( msg$opcode != 0 )
|
||||
# Currently only standard queries are tracked.
|
||||
return;
|
||||
|
||||
c$dns$RD = msg$RD;
|
||||
c$dns$TC = msg$TC;
|
||||
c$dns$qclass = qclass;
|
||||
|
@ -261,64 +359,88 @@ event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qcla
|
|||
# Note: I'm ignoring the name type for now. Not sure if this should be
|
||||
# worked into the query/response in some fashion.
|
||||
if ( c$id$resp_p == 137/udp )
|
||||
{
|
||||
query = decode_netbios_name(query);
|
||||
if ( c$dns$qtype_name == "SRV" )
|
||||
{
|
||||
# The SRV RFC used the ID used for NetBios Status RRs.
|
||||
# So if this is NetBios Name Service we name it correctly.
|
||||
c$dns$qtype_name = "NBSTAT";
|
||||
}
|
||||
}
|
||||
c$dns$query = query;
|
||||
}
|
||||
|
||||
|
||||
event dns_unknown_reply(c: connection, msg: dns_msg, ans: dns_answer) &priority=5
|
||||
{
|
||||
hook DNS::do_reply(c, msg, ans, fmt("<unknown type=%s>", ans$qtype));
|
||||
}
|
||||
|
||||
event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, fmt("%s", a));
|
||||
hook DNS::do_reply(c, msg, ans, fmt("%s", a));
|
||||
}
|
||||
|
||||
event dns_TXT_reply(c: connection, msg: dns_msg, ans: dns_answer, str: string) &priority=5
|
||||
event dns_TXT_reply(c: connection, msg: dns_msg, ans: dns_answer, strs: string_vec) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, str);
|
||||
local txt_strings: string = "";
|
||||
|
||||
for ( i in strs )
|
||||
{
|
||||
if ( i > 0 )
|
||||
txt_strings += " ";
|
||||
|
||||
txt_strings += fmt("TXT %d %s", |strs[i]|, strs[i]);
|
||||
}
|
||||
|
||||
hook DNS::do_reply(c, msg, ans, txt_strings);
|
||||
}
|
||||
|
||||
event dns_AAAA_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, fmt("%s", a));
|
||||
hook DNS::do_reply(c, msg, ans, fmt("%s", a));
|
||||
}
|
||||
|
||||
event dns_A6_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, fmt("%s", a));
|
||||
hook DNS::do_reply(c, msg, ans, fmt("%s", a));
|
||||
}
|
||||
|
||||
event dns_NS_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, name);
|
||||
hook DNS::do_reply(c, msg, ans, name);
|
||||
}
|
||||
|
||||
event dns_CNAME_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, name);
|
||||
hook DNS::do_reply(c, msg, ans, name);
|
||||
}
|
||||
|
||||
event dns_MX_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string,
|
||||
preference: count) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, name);
|
||||
hook DNS::do_reply(c, msg, ans, name);
|
||||
}
|
||||
|
||||
event dns_PTR_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, name);
|
||||
hook DNS::do_reply(c, msg, ans, name);
|
||||
}
|
||||
|
||||
event dns_SOA_reply(c: connection, msg: dns_msg, ans: dns_answer, soa: dns_soa) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, soa$mname);
|
||||
hook DNS::do_reply(c, msg, ans, soa$mname);
|
||||
}
|
||||
|
||||
event dns_WKS_reply(c: connection, msg: dns_msg, ans: dns_answer) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, "");
|
||||
hook DNS::do_reply(c, msg, ans, "");
|
||||
}
|
||||
|
||||
event dns_SRV_reply(c: connection, msg: dns_msg, ans: dns_answer) &priority=5
|
||||
event dns_SRV_reply(c: connection, msg: dns_msg, ans: dns_answer, target: string, priority: count, weight: count, p: count) &priority=5
|
||||
{
|
||||
event DNS::do_reply(c, msg, ans, "");
|
||||
hook DNS::do_reply(c, msg, ans, target);
|
||||
}
|
||||
|
||||
# TODO: figure out how to handle these
|
||||
|
@ -339,7 +461,8 @@ event dns_SRV_reply(c: connection, msg: dns_msg, ans: dns_answer) &priority=5
|
|||
|
||||
event dns_rejected(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) &priority=5
|
||||
{
|
||||
c$dns$rejected = T;
|
||||
if ( c?$dns )
|
||||
c$dns$rejected = T;
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
|
@ -347,16 +470,8 @@ event connection_state_remove(c: connection) &priority=-5
|
|||
if ( ! c?$dns_state )
|
||||
return;
|
||||
|
||||
# If Bro is expiring state, we should go ahead and log all unlogged
|
||||
# request/response pairs now.
|
||||
for ( trans_id in c$dns_state$pending )
|
||||
{
|
||||
local infos: vector of Info;
|
||||
Queue::get_vector(c$dns_state$pending[trans_id], infos);
|
||||
for ( i in infos )
|
||||
{
|
||||
Log::write(DNS::LOG, infos[i]);
|
||||
}
|
||||
}
|
||||
# If Bro is expiring state, we should go ahead and log all unmatched
|
||||
# queries and replies now.
|
||||
log_unmatched_msgs(c$dns_state$pending_queries);
|
||||
log_unmatched_msgs(c$dns_state$pending_replies);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# List of HTTP headers pulled from:
|
||||
# http://annevankesteren.nl/2007/10/http-methods
|
||||
signature dpd_http_client {
|
||||
ip-proto == tcp
|
||||
payload /^[[:space:]]*(GET|HEAD|POST)[[:space:]]*/
|
||||
payload /^[[:space:]]*(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT|PROPFIND|PROPPATCH|MKCOL|COPY|MOVE|LOCK|UNLOCK|VERSION-CONTROL|REPORT|CHECKOUT|CHECKIN|UNCHECKOUT|MKWORKSPACE|UPDATE|LABEL|MERGE|BASELINE-CONTROL|MKACTIVITY|ORDERPATCH|ACL|PATCH|SEARCH|BCOPY|BDELETE|BMOVE|BPROPFIND|BPROPPATCH|NOTIFY|POLL|SUBSCRIBE|UNSUBSCRIBE|X-MS-ENUMATTS|RPC_OUT_DATA|RPC_IN_DATA)[[:space:]]*/
|
||||
tcp-state originator
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priori
|
|||
|
||||
if ( f$is_orig )
|
||||
{
|
||||
if ( ! c$http?$orig_mime_types )
|
||||
if ( ! c$http?$orig_fuids )
|
||||
c$http$orig_fuids = string_vec(f$id);
|
||||
else
|
||||
c$http$orig_fuids[|c$http$orig_fuids|] = f$id;
|
||||
|
@ -87,7 +87,7 @@ event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priori
|
|||
}
|
||||
else
|
||||
{
|
||||
if ( ! c$http?$resp_mime_types )
|
||||
if ( ! c$http?$resp_fuids )
|
||||
c$http$resp_fuids = string_vec(f$id);
|
||||
else
|
||||
c$http$resp_fuids[|c$http$resp_fuids|] = f$id;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
@load base/utils/numbers
|
||||
@load base/utils/files
|
||||
@load base/frameworks/tunnels
|
||||
|
||||
module HTTP;
|
||||
|
||||
|
@ -217,6 +218,17 @@ event http_reply(c: connection, version: string, code: count, reason: string) &p
|
|||
c$http$info_code = code;
|
||||
c$http$info_msg = reason;
|
||||
}
|
||||
|
||||
if ( c$http?$method && c$http$method == "CONNECT" && code == 200 )
|
||||
{
|
||||
# Copy this conn_id and set the orig_p to zero because in the case of CONNECT
|
||||
# proxies there will be potentially many source ports since a new proxy connection
|
||||
# is established for each proxied connection. We treat this as a singular
|
||||
# "tunnel".
|
||||
local tid = copy(c$id);
|
||||
tid$orig_p = 0/tcp;
|
||||
Tunnel::register([$cid=tid, $tunnel_type=Tunnel::HTTP]);
|
||||
}
|
||||
}
|
||||
|
||||
event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=5
|
||||
|
|
|
@ -76,7 +76,7 @@ event irc_dcc_message(c: connection, is_orig: bool,
|
|||
dcc_expected_transfers[address, p] = c$irc;
|
||||
}
|
||||
|
||||
event expected_connection_seen(c: connection, a: Analyzer::Tag) &priority=10
|
||||
event scheduled_analyzer_applied(c: connection, a: Analyzer::Tag) &priority=10
|
||||
{
|
||||
local id = c$id;
|
||||
if ( [id$resp_h, id$resp_p] in dcc_expected_transfers )
|
||||
|
|
1
scripts/base/protocols/snmp/README
Normal file
1
scripts/base/protocols/snmp/README
Normal file
|
@ -0,0 +1 @@
|
|||
Support for Simple Network Management Protocol (SNMP) analysis.
|
1
scripts/base/protocols/snmp/__load__.bro
Normal file
1
scripts/base/protocols/snmp/__load__.bro
Normal file
|
@ -0,0 +1 @@
|
|||
@load ./main
|
182
scripts/base/protocols/snmp/main.bro
Normal file
182
scripts/base/protocols/snmp/main.bro
Normal file
|
@ -0,0 +1,182 @@
|
|||
##! Enables analysis and logging of SNMP datagrams.
|
||||
|
||||
module SNMP;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
## Information tracked per SNMP session.
|
||||
type Info: record {
|
||||
## Timestamp of first packet belonging to the SNMP session.
|
||||
ts: time &log;
|
||||
## The unique ID for the connection.
|
||||
uid: string &log;
|
||||
## The connection's 5-tuple of addresses/ports (ports inherently
|
||||
## include transport protocol information)
|
||||
id: conn_id &log;
|
||||
## The amount of time between the first packet beloning to
|
||||
## the SNMP session and the latest one seen.
|
||||
duration: interval &log &default=0secs;
|
||||
## The version of SNMP being used.
|
||||
version: string &log;
|
||||
## The community string of the first SNMP packet associated with
|
||||
## the session. This is used as part of SNMP's (v1 and v2c)
|
||||
## administrative/security framework. See :rfc:`1157` or :rfc:`1901`.
|
||||
community: string &log &optional;
|
||||
|
||||
## The number of variable bindings in GetRequest/GetNextRequest PDUs
|
||||
## seen for the session.
|
||||
get_requests: count &log &default=0;
|
||||
## The number of variable bindings in GetBulkRequest PDUs seen for
|
||||
## the session.
|
||||
get_bulk_requests: count &log &default=0;
|
||||
## The number of variable bindings in GetResponse/Response PDUs seen
|
||||
## for the session.
|
||||
get_responses: count &log &default=0;
|
||||
## The number of variable bindings in SetRequest PDUs seen for
|
||||
## the session.
|
||||
set_requests: count &log &default=0;
|
||||
|
||||
## A system description of the SNMP responder endpoint.
|
||||
display_string: string &log &optional;
|
||||
## The time at which the SNMP responder endpoint claims it's been
|
||||
## up since.
|
||||
up_since: time &log &optional;
|
||||
};
|
||||
|
||||
## Maps an SNMP version integer to a human readable string.
|
||||
const version_map: table[count] of string = {
|
||||
[0] = "1",
|
||||
[1] = "2c",
|
||||
[3] = "3",
|
||||
} &redef &default="unknown";
|
||||
|
||||
## Event that can be handled to access the SNMP record as it is sent on
|
||||
## to the logging framework.
|
||||
global log_snmp: event(rec: Info);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
snmp: SNMP::Info &optional;
|
||||
};
|
||||
|
||||
const ports = { 161/udp, 162/udp };
|
||||
redef likely_server_ports += { ports };
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Analyzer::register_for_ports(Analyzer::ANALYZER_SNMP, ports);
|
||||
Log::create_stream(SNMP::LOG, [$columns=SNMP::Info, $ev=log_snmp]);
|
||||
}
|
||||
|
||||
function init_state(c: connection, h: SNMP::Header): Info
|
||||
{
|
||||
if ( ! c?$snmp )
|
||||
{
|
||||
c$snmp = Info($ts=network_time(),
|
||||
$uid=c$uid, $id=c$id,
|
||||
$version=version_map[h$version]);
|
||||
}
|
||||
|
||||
local s = c$snmp;
|
||||
|
||||
if ( ! s?$community )
|
||||
{
|
||||
if ( h?$v1 )
|
||||
s$community = h$v1$community;
|
||||
else if ( h?$v2 )
|
||||
s$community = h$v2$community;
|
||||
}
|
||||
|
||||
s$duration = network_time() - s$ts;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( c?$snmp )
|
||||
Log::write(LOG, c$snmp);
|
||||
}
|
||||
|
||||
event snmp_get_request(c: connection, is_orig: bool, header: SNMP::Header, pdu: SNMP::PDU) &priority=5
|
||||
{
|
||||
local s = init_state(c, header);
|
||||
s$get_requests += |pdu$bindings|;
|
||||
}
|
||||
|
||||
event snmp_get_bulk_request(c: connection, is_orig: bool, header: SNMP::Header, pdu: SNMP::BulkPDU) &priority=5
|
||||
{
|
||||
local s = init_state(c, header);
|
||||
s$get_bulk_requests += |pdu$bindings|;
|
||||
}
|
||||
|
||||
event snmp_get_next_request(c: connection, is_orig: bool, header: SNMP::Header, pdu: SNMP::PDU) &priority=5
|
||||
{
|
||||
local s = init_state(c, header);
|
||||
s$get_requests += |pdu$bindings|;
|
||||
}
|
||||
|
||||
event snmp_response(c: connection, is_orig: bool, header: SNMP::Header, pdu: SNMP::PDU) &priority=5
|
||||
{
|
||||
local s = init_state(c, header);
|
||||
s$get_responses += |pdu$bindings|;
|
||||
|
||||
for ( i in pdu$bindings )
|
||||
{
|
||||
local binding = pdu$bindings[i];
|
||||
|
||||
if ( binding$oid == "1.3.6.1.2.1.1.1.0" && binding$value?$octets )
|
||||
c$snmp$display_string = binding$value$octets;
|
||||
else if ( binding$oid == "1.3.6.1.2.1.1.3.0" && binding$value?$unsigned )
|
||||
{
|
||||
local up_seconds = binding$value$unsigned / 100.0;
|
||||
s$up_since = network_time() - double_to_interval(up_seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event snmp_set_request(c: connection, is_orig: bool, header: SNMP::Header, pdu: SNMP::PDU) &priority=5
|
||||
{
|
||||
local s = init_state(c, header);
|
||||
s$set_requests += |pdu$bindings|;
|
||||
}
|
||||
|
||||
event snmp_trap(c: connection, is_orig: bool, header: SNMP::Header, pdu: SNMP::TrapPDU) &priority=5
|
||||
{
|
||||
init_state(c, header);
|
||||
}
|
||||
|
||||
event snmp_inform_request(c: connection, is_orig: bool, header: SNMP::Header, pdu: SNMP::PDU) &priority=5
|
||||
{
|
||||
init_state(c, header);
|
||||
}
|
||||
|
||||
event snmp_trapV2(c: connection, is_orig: bool, header: SNMP::Header, pdu: SNMP::PDU) &priority=5
|
||||
{
|
||||
init_state(c, header);
|
||||
}
|
||||
|
||||
event snmp_report(c: connection, is_orig: bool, header: SNMP::Header, pdu: SNMP::PDU) &priority=5
|
||||
{
|
||||
init_state(c, header);
|
||||
}
|
||||
|
||||
event snmp_unknown_pdu(c: connection, is_orig: bool, header: SNMP::Header, tag: count) &priority=5
|
||||
{
|
||||
init_state(c, header);
|
||||
}
|
||||
|
||||
event snmp_unknown_scoped_pdu(c: connection, is_orig: bool, header: SNMP::Header, tag: count) &priority=5
|
||||
{
|
||||
init_state(c, header);
|
||||
}
|
||||
|
||||
event snmp_encrypted_pdu(c: connection, is_orig: bool, header: SNMP::Header) &priority=5
|
||||
{
|
||||
init_state(c, header);
|
||||
}
|
||||
|
||||
#event snmp_unknown_header_version(c: connection, is_orig: bool, version: count) &priority=5
|
||||
# {
|
||||
# }
|
|
@ -1,5 +1,6 @@
|
|||
@load ./consts
|
||||
@load ./main
|
||||
@load ./mozilla-ca-list
|
||||
@load ./files
|
||||
|
||||
@load-sigs ./dpd.sig
|
||||
|
|
|
@ -14,15 +14,15 @@ export {
|
|||
[TLSv11] = "TLSv11",
|
||||
[TLSv12] = "TLSv12",
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
||||
## Mapping between numeric codes and human readable strings for alert
|
||||
|
||||
## Mapping between numeric codes and human readable strings for alert
|
||||
## levels.
|
||||
const alert_levels: table[count] of string = {
|
||||
[1] = "warning",
|
||||
[2] = "fatal",
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
||||
## Mapping between numeric codes and human readable strings for alert
|
||||
|
||||
## Mapping between numeric codes and human readable strings for alert
|
||||
## descriptions.
|
||||
const alert_descriptions: table[count] of string = {
|
||||
[0] = "close_notify",
|
||||
|
@ -47,6 +47,7 @@ export {
|
|||
[70] = "protocol_version",
|
||||
[71] = "insufficient_security",
|
||||
[80] = "internal_error",
|
||||
[86] = "inappropriate_fallback",
|
||||
[90] = "user_canceled",
|
||||
[100] = "no_renegotiation",
|
||||
[110] = "unsupported_extension",
|
||||
|
@ -55,8 +56,9 @@ export {
|
|||
[113] = "bad_certificate_status_response",
|
||||
[114] = "bad_certificate_hash_value",
|
||||
[115] = "unknown_psk_identity",
|
||||
[120] = "no_application_protocol",
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
||||
|
||||
## Mapping between numeric codes and human readable strings for SSL/TLS
|
||||
## extensions.
|
||||
# More information can be found here:
|
||||
|
@ -86,9 +88,55 @@ export {
|
|||
[13172] = "next_protocol_negotiation",
|
||||
[13175] = "origin_bound_certificates",
|
||||
[13180] = "encrypted_client_certificates",
|
||||
[30031] = "channel_id",
|
||||
[30032] = "channel_id_new",
|
||||
[35655] = "padding",
|
||||
[65281] = "renegotiation_info"
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
||||
|
||||
## Mapping between numeric codes and human readable string for SSL/TLS elliptic curves.
|
||||
# See http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
|
||||
const ec_curves: table[count] of string = {
|
||||
[1] = "sect163k1",
|
||||
[2] = "sect163r1",
|
||||
[3] = "sect163r2",
|
||||
[4] = "sect193r1",
|
||||
[5] = "sect193r2",
|
||||
[6] = "sect233k1",
|
||||
[7] = "sect233r1",
|
||||
[8] = "sect239k1",
|
||||
[9] = "sect283k1",
|
||||
[10] = "sect283r1",
|
||||
[11] = "sect409k1",
|
||||
[12] = "sect409r1",
|
||||
[13] = "sect571k1",
|
||||
[14] = "sect571r1",
|
||||
[15] = "secp160k1",
|
||||
[16] = "secp160r1",
|
||||
[17] = "secp160r2",
|
||||
[18] = "secp192k1",
|
||||
[19] = "secp192r1",
|
||||
[20] = "secp224k1",
|
||||
[21] = "secp224r1",
|
||||
[22] = "secp256k1",
|
||||
[23] = "secp256r1",
|
||||
[24] = "secp384r1",
|
||||
[25] = "secp521r1",
|
||||
[26] = "brainpoolP256r1",
|
||||
[27] = "brainpoolP384r1",
|
||||
[28] = "brainpoolP512r1",
|
||||
[0xFF01] = "arbitrary_explicit_prime_curves",
|
||||
[0xFF02] = "arbitrary_explicit_char2_curves"
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
||||
## Mapping between numeric codes and human readable string for SSL/TLC EC point formats.
|
||||
# See http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-9
|
||||
const ec_point_formats: table[count] of string = {
|
||||
[0] = "uncompressed",
|
||||
[1] = "ansiX962_compressed_prime",
|
||||
[2] = "ansiX962_compressed_char2"
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
||||
# SSLv2
|
||||
const SSLv20_CK_RC4_128_WITH_MD5 = 0x010080;
|
||||
const SSLv20_CK_RC4_128_EXPORT40_WITH_MD5 = 0x020080;
|
||||
|
@ -262,6 +310,8 @@ export {
|
|||
const TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3;
|
||||
const TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4;
|
||||
const TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5;
|
||||
# draft-bmoeller-tls-downgrade-scsv-01
|
||||
const TLS_FALLBACK_SCSV = 0x5600;
|
||||
# RFC 4492
|
||||
const TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001;
|
||||
const TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002;
|
||||
|
@ -437,6 +487,10 @@ export {
|
|||
const TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9;
|
||||
const TLS_PSK_DHE_WITH_AES_128_CCM_8 = 0xC0AA;
|
||||
const TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_128_CCM = 0xC0AC;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_256_CCM = 0xC0AD;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 0xC0AE;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = 0xC0AF;
|
||||
# draft-agl-tls-chacha20poly1305-02
|
||||
const TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC13;
|
||||
const TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC14;
|
||||
|
@ -451,8 +505,8 @@ export {
|
|||
const SSL_RSA_WITH_DES_CBC_MD5 = 0xFF82;
|
||||
const SSL_RSA_WITH_3DES_EDE_CBC_MD5 = 0xFF83;
|
||||
const TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF;
|
||||
|
||||
## This is a table of all known cipher specs. It can be used for
|
||||
|
||||
## This is a table of all known cipher specs. It can be used for
|
||||
## detecting unknown ciphers and for converting the cipher spec
|
||||
## constants into a human readable format.
|
||||
const cipher_desc: table[count] of string = {
|
||||
|
@ -628,6 +682,7 @@ export {
|
|||
[TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256] = "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
[TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256] = "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
[TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256] = "TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
[TLS_FALLBACK_SCSV] = "TLS_FALLBACK_SCSV",
|
||||
[TLS_ECDH_ECDSA_WITH_NULL_SHA] = "TLS_ECDH_ECDSA_WITH_NULL_SHA",
|
||||
[TLS_ECDH_ECDSA_WITH_RC4_128_SHA] = "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
|
||||
[TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA] = "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
||||
|
@ -799,6 +854,10 @@ export {
|
|||
[TLS_PSK_WITH_AES_256_CCM_8] = "TLS_PSK_WITH_AES_256_CCM_8",
|
||||
[TLS_PSK_DHE_WITH_AES_128_CCM_8] = "TLS_PSK_DHE_WITH_AES_128_CCM_8",
|
||||
[TLS_PSK_DHE_WITH_AES_256_CCM_8] = "TLS_PSK_DHE_WITH_AES_256_CCM_8",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_128_CCM] = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_256_CCM] = "TLS_ECDHE_ECDSA_WITH_AES_256_CCM",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8] = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8] = "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8",
|
||||
[TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256] = "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
[TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256] = "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
[TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256] = "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
|
@ -812,43 +871,5 @@ export {
|
|||
[SSL_RSA_WITH_3DES_EDE_CBC_MD5] = "SSL_RSA_WITH_3DES_EDE_CBC_MD5",
|
||||
[TLS_EMPTY_RENEGOTIATION_INFO_SCSV] = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
||||
## Mapping between the constants and string values for SSL/TLS errors.
|
||||
const x509_errors: table[count] of string = {
|
||||
[0] = "ok",
|
||||
[1] = "unable to get issuer cert",
|
||||
[2] = "unable to get crl",
|
||||
[3] = "unable to decrypt cert signature",
|
||||
[4] = "unable to decrypt crl signature",
|
||||
[5] = "unable to decode issuer public key",
|
||||
[6] = "cert signature failure",
|
||||
[7] = "crl signature failure",
|
||||
[8] = "cert not yet valid",
|
||||
[9] = "cert has expired",
|
||||
[10] = "crl not yet valid",
|
||||
[11] = "crl has expired",
|
||||
[12] = "error in cert not before field",
|
||||
[13] = "error in cert not after field",
|
||||
[14] = "error in crl last update field",
|
||||
[15] = "error in crl next update field",
|
||||
[16] = "out of mem",
|
||||
[17] = "depth zero self signed cert",
|
||||
[18] = "self signed cert in chain",
|
||||
[19] = "unable to get issuer cert locally",
|
||||
[20] = "unable to verify leaf signature",
|
||||
[21] = "cert chain too long",
|
||||
[22] = "cert revoked",
|
||||
[23] = "invalid ca",
|
||||
[24] = "path length exceeded",
|
||||
[25] = "invalid purpose",
|
||||
[26] = "cert untrusted",
|
||||
[27] = "cert rejected",
|
||||
[28] = "subject issuer mismatch",
|
||||
[29] = "akid skid mismatch",
|
||||
[30] = "akid issuer serial mismatch",
|
||||
[31] = "keyusage no certsign",
|
||||
[32] = "unable to get crl issuer",
|
||||
[33] = "unhandled critical extension",
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
signature dpd_ssl_server {
|
||||
ip-proto == tcp
|
||||
# Server hello.
|
||||
payload /^(\x16\x03[\x00\x01\x02]..\x02...\x03[\x00\x01\x02]|...?\x04..\x00\x02).*/
|
||||
payload /^(\x16\x03[\x00\x01\x02\x03]..\x02...\x03[\x00\x01\x02\x03]|...?\x04..\x00\x02).*/
|
||||
requires-reverse-signature dpd_ssl_client
|
||||
enable "ssl"
|
||||
tcp-state responder
|
||||
|
@ -10,6 +10,6 @@ signature dpd_ssl_server {
|
|||
signature dpd_ssl_client {
|
||||
ip-proto == tcp
|
||||
# Client hello.
|
||||
payload /^(\x16\x03[\x00\x01\x02]..\x01...\x03[\x00\x01\x02]|...?\x01[\x00\x01\x02][\x02\x03]).*/
|
||||
payload /^(\x16\x03[\x00\x01\x02\x03]..\x01...\x03[\x00\x01\x02\x03]|...?\x01[\x00\x03][\x00\x01\x02\x03]).*/
|
||||
tcp-state originator
|
||||
}
|
||||
|
|
135
scripts/base/protocols/ssl/files.bro
Normal file
135
scripts/base/protocols/ssl/files.bro
Normal file
|
@ -0,0 +1,135 @@
|
|||
@load ./main
|
||||
@load base/utils/conn-ids
|
||||
@load base/frameworks/files
|
||||
@load base/files/x509
|
||||
|
||||
module SSL;
|
||||
|
||||
export {
|
||||
redef record Info += {
|
||||
## Chain of certificates offered by the server to validate its
|
||||
## complete signing chain.
|
||||
cert_chain: vector of Files::Info &optional;
|
||||
|
||||
## An ordered vector of all certicate file unique IDs for the
|
||||
## certificates offered by the server.
|
||||
cert_chain_fuids: vector of string &optional &log;
|
||||
|
||||
## Chain of certificates offered by the client to validate its
|
||||
## complete signing chain.
|
||||
client_cert_chain: vector of Files::Info &optional;
|
||||
|
||||
## An ordered vector of all certicate file unique IDs for the
|
||||
## certificates offered by the client.
|
||||
client_cert_chain_fuids: vector of string &optional &log;
|
||||
|
||||
## Subject of the X.509 certificate offered by the server.
|
||||
subject: string &log &optional;
|
||||
|
||||
## Subject of the signer of the X.509 certificate offered by the
|
||||
## server.
|
||||
issuer: string &log &optional;
|
||||
|
||||
## Subject of the X.509 certificate offered by the client.
|
||||
client_subject: string &log &optional;
|
||||
|
||||
## Subject of the signer of the X.509 certificate offered by the
|
||||
## client.
|
||||
client_issuer: string &log &optional;
|
||||
|
||||
## Current number of certificates seen from either side. Used
|
||||
## to create file handles.
|
||||
server_depth: count &default=0;
|
||||
client_depth: count &default=0;
|
||||
};
|
||||
|
||||
## Default file handle provider for SSL.
|
||||
global get_file_handle: function(c: connection, is_orig: bool): string;
|
||||
|
||||
## Default file describer for SSL.
|
||||
global describe_file: function(f: fa_file): string;
|
||||
}
|
||||
|
||||
function get_file_handle(c: connection, is_orig: bool): string
|
||||
{
|
||||
# Unused. File handles are generated in the analyzer.
|
||||
return "";
|
||||
}
|
||||
|
||||
function describe_file(f: fa_file): string
|
||||
{
|
||||
if ( f$source != "SSL" || ! f?$info || ! f$info?$x509 || ! f$info$x509?$certificate )
|
||||
return "";
|
||||
|
||||
# It is difficult to reliably describe a certificate - especially since
|
||||
# we do not know when this function is called (hence, if the data structures
|
||||
# are already populated).
|
||||
#
|
||||
# Just return a bit of our connection information and hope that that is good enough.
|
||||
for ( cid in f$conns )
|
||||
{
|
||||
if ( f$conns[cid]?$ssl )
|
||||
{
|
||||
local c = f$conns[cid];
|
||||
return cat(c$id$resp_h, ":", c$id$resp_p);
|
||||
}
|
||||
}
|
||||
|
||||
return cat("Serial: ", f$info$x509$certificate$serial, " Subject: ",
|
||||
f$info$x509$certificate$subject, " Issuer: ",
|
||||
f$info$x509$certificate$issuer);
|
||||
}
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Files::register_protocol(Analyzer::ANALYZER_SSL,
|
||||
[$get_file_handle = SSL::get_file_handle,
|
||||
$describe = SSL::describe_file]);
|
||||
}
|
||||
|
||||
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5
|
||||
{
|
||||
if ( ! c?$ssl )
|
||||
return;
|
||||
|
||||
if ( ! c$ssl?$cert_chain )
|
||||
{
|
||||
c$ssl$cert_chain = vector();
|
||||
c$ssl$client_cert_chain = vector();
|
||||
c$ssl$cert_chain_fuids = string_vec();
|
||||
c$ssl$client_cert_chain_fuids = string_vec();
|
||||
}
|
||||
|
||||
if ( is_orig )
|
||||
{
|
||||
c$ssl$client_cert_chain[|c$ssl$client_cert_chain|] = f$info;
|
||||
c$ssl$client_cert_chain_fuids[|c$ssl$client_cert_chain_fuids|] = f$id;
|
||||
}
|
||||
else
|
||||
{
|
||||
c$ssl$cert_chain[|c$ssl$cert_chain|] = f$info;
|
||||
c$ssl$cert_chain_fuids[|c$ssl$cert_chain_fuids|] = f$id;
|
||||
}
|
||||
|
||||
Files::add_analyzer(f, Files::ANALYZER_X509);
|
||||
# always calculate hashes. They are not necessary for base scripts
|
||||
# but very useful for identification, and required for policy scripts
|
||||
Files::add_analyzer(f, Files::ANALYZER_MD5);
|
||||
Files::add_analyzer(f, Files::ANALYZER_SHA1);
|
||||
}
|
||||
|
||||
event ssl_established(c: connection) &priority=6
|
||||
{
|
||||
# update subject and issuer information
|
||||
if ( c$ssl?$cert_chain && |c$ssl$cert_chain| > 0 )
|
||||
{
|
||||
c$ssl$subject = c$ssl$cert_chain[0]$x509$certificate$subject;
|
||||
c$ssl$issuer = c$ssl$cert_chain[0]$x509$certificate$issuer;
|
||||
}
|
||||
|
||||
if ( c$ssl?$client_cert_chain && |c$ssl$client_cert_chain| > 0 )
|
||||
{
|
||||
c$ssl$client_subject = c$ssl$client_cert_chain[0]$x509$certificate$subject;
|
||||
c$ssl$client_issuer = c$ssl$client_cert_chain[0]$x509$certificate$issuer;
|
||||
}
|
||||
}
|
|
@ -19,45 +19,28 @@ export {
|
|||
version: string &log &optional;
|
||||
## SSL/TLS cipher suite that the server chose.
|
||||
cipher: string &log &optional;
|
||||
## Elliptic curve the server chose when using ECDH/ECDHE.
|
||||
curve: string &log &optional;
|
||||
## Value of the Server Name Indicator SSL/TLS extension. It
|
||||
## indicates the server name that the client was requesting.
|
||||
server_name: string &log &optional;
|
||||
## Session ID offered by the client for session resumption.
|
||||
session_id: string &log &optional;
|
||||
## Subject of the X.509 certificate offered by the server.
|
||||
subject: string &log &optional;
|
||||
## Subject of the signer of the X.509 certificate offered by the
|
||||
## server.
|
||||
issuer_subject: string &log &optional;
|
||||
## NotValidBefore field value from the server certificate.
|
||||
not_valid_before: time &log &optional;
|
||||
## NotValidAfter field value from the server certificate.
|
||||
not_valid_after: time &log &optional;
|
||||
## Last alert that was seen during the connection.
|
||||
last_alert: string &log &optional;
|
||||
|
||||
## Subject of the X.509 certificate offered by the client.
|
||||
client_subject: string &log &optional;
|
||||
## Subject of the signer of the X.509 certificate offered by the
|
||||
## client.
|
||||
client_issuer_subject: string &log &optional;
|
||||
|
||||
## Full binary server certificate stored in DER format.
|
||||
cert: string &optional;
|
||||
## Chain of certificates offered by the server to validate its
|
||||
## complete signing chain.
|
||||
cert_chain: vector of string &optional;
|
||||
|
||||
## Full binary client certificate stored in DER format.
|
||||
client_cert: string &optional;
|
||||
## Chain of certificates offered by the client to validate its
|
||||
## complete signing chain.
|
||||
client_cert_chain: vector of string &optional;
|
||||
|
||||
## The analyzer ID used for the analyzer instance attached
|
||||
## to each connection. It is not used for logging since it's a
|
||||
## meaningless arbitrary number.
|
||||
analyzer_id: count &optional;
|
||||
|
||||
## Flag to indicate if this ssl session has been established
|
||||
## succesfully, or if it was aborted during the handshake.
|
||||
established: bool &log &default=F;
|
||||
|
||||
## Flag to indicate if this record already has been logged, to
|
||||
## prevent duplicates.
|
||||
logged: bool &default=F;
|
||||
};
|
||||
|
||||
## The default root CA bundle. By default, the mozilla-ca-list.bro
|
||||
|
@ -108,8 +91,7 @@ event bro_init() &priority=5
|
|||
function set_session(c: connection)
|
||||
{
|
||||
if ( ! c?$ssl )
|
||||
c$ssl = [$ts=network_time(), $uid=c$uid, $id=c$id, $cert_chain=vector(),
|
||||
$client_cert_chain=vector()];
|
||||
c$ssl = [$ts=network_time(), $uid=c$uid, $id=c$id];
|
||||
}
|
||||
|
||||
function delay_log(info: Info, token: string)
|
||||
|
@ -127,9 +109,13 @@ function undelay_log(info: Info, token: string)
|
|||
|
||||
function log_record(info: Info)
|
||||
{
|
||||
if ( info$logged )
|
||||
return;
|
||||
|
||||
if ( ! info?$delay_tokens || |info$delay_tokens| == 0 )
|
||||
{
|
||||
Log::write(SSL::LOG, info);
|
||||
info$logged = T;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -146,11 +132,16 @@ function log_record(info: Info)
|
|||
}
|
||||
}
|
||||
|
||||
function finish(c: connection)
|
||||
# remove_analyzer flag is used to prevent disabling analyzer for finished
|
||||
# connections.
|
||||
function finish(c: connection, remove_analyzer: bool)
|
||||
{
|
||||
log_record(c$ssl);
|
||||
if ( disable_analyzer_after_detection && c?$ssl && c$ssl?$analyzer_id )
|
||||
if ( remove_analyzer && disable_analyzer_after_detection && c?$ssl && c$ssl?$analyzer_id )
|
||||
{
|
||||
disable_analyzer(c$id, c$ssl$analyzer_id);
|
||||
delete c$ssl$analyzer_id;
|
||||
}
|
||||
}
|
||||
|
||||
event ssl_client_hello(c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) &priority=5
|
||||
|
@ -170,55 +161,23 @@ event ssl_server_hello(c: connection, version: count, possible_ts: time, server_
|
|||
c$ssl$cipher = cipher_desc[cipher];
|
||||
}
|
||||
|
||||
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string) &priority=5
|
||||
event ssl_server_curve(c: connection, curve: count) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
|
||||
# We aren't doing anything with client certificates yet.
|
||||
if ( is_orig )
|
||||
{
|
||||
if ( chain_idx == 0 )
|
||||
{
|
||||
# Save the primary cert.
|
||||
c$ssl$client_cert = der_cert;
|
||||
|
||||
# Also save other certificate information about the primary cert.
|
||||
c$ssl$client_subject = cert$subject;
|
||||
c$ssl$client_issuer_subject = cert$issuer;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Otherwise, add it to the cert validation chain.
|
||||
c$ssl$client_cert_chain[|c$ssl$client_cert_chain|] = der_cert;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( chain_idx == 0 )
|
||||
{
|
||||
# Save the primary cert.
|
||||
c$ssl$cert = der_cert;
|
||||
|
||||
# Also save other certificate information about the primary cert.
|
||||
c$ssl$subject = cert$subject;
|
||||
c$ssl$issuer_subject = cert$issuer;
|
||||
c$ssl$not_valid_before = cert$not_valid_before;
|
||||
c$ssl$not_valid_after = cert$not_valid_after;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Otherwise, add it to the cert validation chain.
|
||||
c$ssl$cert_chain[|c$ssl$cert_chain|] = der_cert;
|
||||
}
|
||||
}
|
||||
c$ssl$curve = ec_curves[curve];
|
||||
}
|
||||
|
||||
event ssl_extension(c: connection, is_orig: bool, code: count, val: string) &priority=5
|
||||
event ssl_extension_server_name(c: connection, is_orig: bool, names: string_vec) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
|
||||
if ( is_orig && extensions[code] == "server_name" )
|
||||
c$ssl$server_name = sub_bytes(val, 6, |val|);
|
||||
if ( is_orig && |names| > 0 )
|
||||
{
|
||||
c$ssl$server_name = names[0];
|
||||
if ( |names| > 1 )
|
||||
event conn_weird("SSL_many_server_names", c, cat(names));
|
||||
}
|
||||
}
|
||||
|
||||
event ssl_alert(c: connection, is_orig: bool, level: count, desc: count) &priority=5
|
||||
|
@ -228,26 +187,36 @@ event ssl_alert(c: connection, is_orig: bool, level: count, desc: count) &priori
|
|||
c$ssl$last_alert = alert_descriptions[desc];
|
||||
}
|
||||
|
||||
event ssl_established(c: connection) &priority=5
|
||||
event ssl_established(c: connection) &priority=7
|
||||
{
|
||||
set_session(c);
|
||||
c$ssl$established = T;
|
||||
}
|
||||
|
||||
event ssl_established(c: connection) &priority=-5
|
||||
{
|
||||
finish(c);
|
||||
finish(c, T);
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( c?$ssl )
|
||||
# called in case a SSL connection that has not been established terminates
|
||||
finish(c, F);
|
||||
}
|
||||
|
||||
event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) &priority=5
|
||||
{
|
||||
# Check by checking for existence of c$ssl record.
|
||||
if ( c?$ssl && atype == Analyzer::ANALYZER_SSL )
|
||||
if ( atype == Analyzer::ANALYZER_SSL )
|
||||
{
|
||||
set_session(c);
|
||||
c$ssl$analyzer_id = aid;
|
||||
}
|
||||
}
|
||||
|
||||
event protocol_violation(c: connection, atype: Analyzer::Tag, aid: count,
|
||||
reason: string) &priority=5
|
||||
{
|
||||
if ( c?$ssl )
|
||||
finish(c);
|
||||
finish(c, T);
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue