Initial drop of updated ssl.bro

This commit is contained in:
Don Appleman 2011-03-07 14:46:23 -06:00
parent 68d4e612f1
commit 2c975495d3

View file

@ -5,54 +5,84 @@
@load weird @load weird
@load ssl-ciphers @load ssl-ciphers
@load ssl-errors @load ssl-errors
@load functions
module SSL;
export {
redef enum Log::ID += { SSL_SERVER };
type ServerLog: record {
ts: time; # timestamp
client_address: addr; # client address
client_port: port; # client port
cert_subject: X509; # certificate subject
not_valid_before: time; # certificate valid time constraint
not_valid_after: time; # certificate valid time constraint
ssl_tls_version: string; # version number
weak_client_ciphers_offered: bool; # true if client offered insecure ciphers
weak_server_ciphers_offered: bool; # true if server offered insecure ciphers
weak_cipher_agreed: bool; # true if insecure cipher agreed upon for use
};
# If true, Bro stores the client and server cipher specs and performs
# additional tests. This costs an extra amount of memory (normally
# only for a short time) but enables detecting of non-intersecting
# cipher sets, for example.
const ssl_compare_cipherspecs = T &redef;
# Whether to analyze certificates seen in SSL connections.
const ssl_analyze_certificates = T &redef;
# If we analyze SSL certificates, we can choose to store them.
const ssl_store_certificates = T &redef;
# Path where we dump the certificates into. If it's empty,
# use the current directory.
const ssl_store_cert_path = "certs" &redef;
# If we analyze SSL certificates, we can choose to verify them.
const ssl_verify_certificates = T &redef;
# This is the path where OpenSSL looks after the trusted certificates.
# If empty, the default path will be used.
const x509_trusted_cert_path = "" &redef;
# Whether to store key-material exchanged in the handshaking phase.
const ssl_store_key_material = F &redef;
# Report weak/unknown ciphers in CLIENT_HELLO, SSLv2 SERVER_HELLO.
const ssl_report_client_weak = F &redef;
const ssl_report_client_unknown = F &redef;
const ssl_report_server_weak = F &redef;
# Log all ciphers.
# TODO: dga 3/11 Discarded for now; could be re-added, perhaps as a separate stream for the curious
# const ssl_log_ciphers = T &redef;
global ssl_ports = {
443/tcp, 563/tcp, 585/tcp, 614/tcp, 636/tcp,
989/tcp, 990/tcp, 992/tcp, 993/tcp, 995/tcp,
} &redef;
}
event bro_init()
{
Log::create_stream( "SSL_SERVER", "SSL::ServerLog" );
Log::add_default_filter( "SSL_SERVER" );
}
global ssl_log = open_log_file("ssl") &redef;
redef enum Notice += { redef enum Notice += {
SSL_X509Violation, # blanket X509 error SSL_X509Violation, # blanket X509 error
SSL_SessConIncon, # session data not consistent with connection SSL_SessConIncon, # session data not consistent with connection
}; };
const SSLv2 = 0x0002; const SSLv2 = 0x0002;
const SSLv3 = 0x0300; const SSLv3 = 0x0300;
const TLSv10 = 0x0301; const TLSv10 = 0x0301;
const TLSv11 = 0x0302; const TLSv11 = 0x0302;
# If true, Bro stores the client and server cipher specs and performs
# additional tests. This costs an extra amount of memory (normally
# only for a short time) but enables detecting of non-intersecting
# cipher sets, for example.
const ssl_compare_cipherspecs = T &redef;
# Whether to analyze certificates seen in SSL connections.
const ssl_analyze_certificates = T &redef;
# If we analyze SSL certificates, we can choose to store them.
const ssl_store_certificates = T &redef;
# Path where we dump the certificates into. If it's empty,
# use the current directory.
const ssl_store_cert_path = "certs" &redef;
# If we analyze SSL certificates, we can choose to verify them.
const ssl_verify_certificates = T &redef;
# This is the path where OpenSSL looks after the trusted certificates.
# If empty, the default path will be used.
const x509_trusted_cert_path = "" &redef;
# Whether to store key-material exchanged in the handshaking phase.
const ssl_store_key_material = F &redef;
# Report weak/unknown ciphers in CLIENT_HELLO, SSLv2 SERVER_HELLO.
const ssl_report_client_weak = F &redef;
const ssl_report_client_unknown = F &redef;
const ssl_report_server_weak = F &redef;
# Log all ciphers.
const ssl_log_ciphers = T &redef;
# NOTE: this is a 'local' port format for your site # NOTE: this is a 'local' port format for your site
# --- well-known ports for ssl --------- # --- well-known ports for ssl ---------
redef capture_filters += { redef capture_filters += {
@ -69,11 +99,6 @@ redef capture_filters += {
["pop3s"] = "tcp port 995" ["pop3s"] = "tcp port 995"
}; };
global ssl_ports = {
443/tcp, 563/tcp, 585/tcp, 614/tcp, 636/tcp,
989/tcp, 990/tcp, 992/tcp, 993/tcp, 995/tcp,
} &redef;
redef dpd_config += { redef dpd_config += {
[[ANALYZER_SSL, ANALYZER_SSL_BINPAC]] = [$ports = ssl_ports] [[ANALYZER_SSL, ANALYZER_SSL_BINPAC]] = [$ports = ssl_ports]
}; };
@ -130,29 +155,33 @@ redef Weird::weird_action += {
"SSLv3_data_without_full_handshake"]] = Weird::WEIRD_IGNORE "SSLv3_data_without_full_handshake"]] = Weird::WEIRD_IGNORE
}; };
global SSL_cipherCount: table[count] of count &default = 0; global SSL_cipherCount: table[count] of count &default = 0;
# track weak ciphers offered by client and/or server until it can be logged
global ssl_weak_client_ciphers: table[conn_id] of bool &read_expire = 2 hrs;
global ssl_weak_server_ciphers: table[conn_id] of bool &read_expire = 2 hrs;
type ssl_connection_info: record { type ssl_connection_info: record {
id: count; # the log identifier number id: count; # the log identifier number
connection_id: conn_id; # IP connection information connection_id: conn_id; # IP connection information
version: string; # version assosciated with connection version: string; # version associated with connection
client_cert: X509; client_cert: X509;
server_cert: X509; server_cert: X509;
id_index: string; # index for associated SSL_sessionID id_index: string; # index for associated SSL_sessionID
handshake_cipher: string; # agreed-upon cipher for session/conn. handshake_cipher: string; # agreed-upon cipher for session/conn.
}; };
# SSL_sessionID index - used to track version assosciated with a session id. # SSL_sessionID index - used to track version associated with a session id.
type SSL_sessionID_record: record { type SSL_sessionID_record: record {
num_reuse: count; num_reuse: count;
id: SSL_sessionID; # literal session ID id: SSL_sessionID; # literal session ID
# everything below is an example of session vs connection monitoring. # everything below is an example of session vs connection monitoring.
version: string; # version assosciated with session id version: string; # version assosciated with session id
client_cert: X509; client_cert: X509;
server_cert: X509; server_cert: X509;
handshake_cipher: string; handshake_cipher: string;
}; };
global ssl_connections: table[conn_id] of ssl_connection_info; global ssl_connections: table[conn_id] of ssl_connection_info;
@ -181,10 +210,7 @@ function new_ssl_connection(c: connection)
info$connection_id = conn; info$connection_id = conn;
ssl_connections[conn] = info; ssl_connections[conn] = info;
append_addl(c, fmt("#%d", new_id)); append_addl( c, fmt( "#%d", new_id ) );
print ssl_log, fmt("%.6f #%d %s start",
network_time(), new_id, id_string(conn));
} }
function new_sessionID_record(session: SSL_sessionID) function new_sessionID_record(session: SSL_sessionID)
@ -231,28 +257,19 @@ function ssl_con2str(c: connection): string
function lookup_ssl_conn(c: connection, func: string, log_if_new: bool) function lookup_ssl_conn(c: connection, func: string, log_if_new: bool)
{ {
if ( c$id !in ssl_connections ) if ( c$id !in ssl_connections )
{
new_ssl_connection(c); new_ssl_connection(c);
if ( log_if_new )
print ssl_log,
fmt("%.6f #%d creating new SSL connection in %s",
network_time(), ssl_connections[c$id]$id, func);
}
} }
event ssl_conn_weak(name: string, c: connection) event ssl_conn_weak(name: string, c: connection)
{ {
lookup_ssl_conn(c, "ssl_conn_weak", T); lookup_ssl_conn(c, "ssl_conn_weak", T);
print ssl_log, fmt("%.6f #%d %s",
network_time(), ssl_connections[c$id]$id, name);
} }
# --- SSL events ------------------- # --- SSL events -------------------
event ssl_certificate_seen(c: connection, is_server: bool) event ssl_certificate_seen(c: connection, is_server: bool)
{ {
# Called whenever there's an certificate to analyze. # Called whenever there's a certificate to analyze.
# we could do something here, like... # we could do something here, like...
# if ( c$id$orig_h in hostsToIgnore ) # if ( c$id$orig_h in hostsToIgnore )
@ -288,12 +305,6 @@ event ssl_certificate(c: connection, cert: X509, is_server: bool)
ssl_sessionIDs[conn$id_index]$server_cert$subject = ssl_sessionIDs[conn$id_index]$server_cert$subject =
cert$subject; cert$subject;
} }
print ssl_log, fmt("%.6f #%d X.509 %s issuer %s",
network_time(), conn$id, direction, cert$issuer);
print ssl_log, fmt("%.6f #%d X.509 %s subject %s",
network_time(), conn$id, direction, cert$subject);
} }
event ssl_conn_attempt(c: connection, version: count, event ssl_conn_attempt(c: connection, version: count,
@ -303,30 +314,28 @@ event ssl_conn_attempt(c: connection, version: count,
local conn = ssl_connections[c$id]; local conn = ssl_connections[c$id];
local version_string = ssl_get_version_string(version); local version_string = ssl_get_version_string(version);
print ssl_log, fmt("%.6f #%d SSL connection attempt %s",
network_time(), conn$id, version_string);
conn$version = version_string; conn$version = version_string;
local has_weak_ciphers = F;
for ( cs in ciphers ) for ( cs in ciphers )
{ # display a list of the cipher suites { # display a list of the cipher suites
# Demo: report clients who support weak ciphers. # Demo: report clients who support weak ciphers.
if ( ssl_report_client_weak && cs in myWeakCiphers ) if ( cs in myWeakCiphers )
{
has_weak_ciphers = T;
event ssl_conn_weak( event ssl_conn_weak(
fmt("SSL client supports weak cipher: %s (0x%x)", fmt("SSL client supports weak cipher: %s (0x%x)",
ssl_get_cipher_name(cs), cs), c); ssl_get_cipher_name(cs), cs), c);
}
# Demo: report unknown ciphers. # Demo: report unknown ciphers.
if ( ssl_report_client_unknown && cs !in ssl_cipher_desc ) if ( ssl_report_client_unknown && cs !in ssl_cipher_desc )
event ssl_conn_weak( event ssl_conn_weak(
fmt("SSL: unknown cipher-spec: %s (0x%x)", fmt("SSL: unknown cipher-spec: %s (0x%x)",
ssl_get_cipher_name(cs), cs), c); ssl_get_cipher_name(cs), cs), c);
if ( ssl_log_ciphers )
print ssl_log, fmt("%.6f #%d client cipher %s (0x%x)",
network_time(), conn$id,
ssl_get_cipher_name(cs), cs);
} }
ssl_weak_client_ciphers[ c$id ] = has_weak_ciphers;
} }
event ssl_conn_server_reply(c: connection, version: count, event ssl_conn_server_reply(c: connection, version: count,
@ -337,25 +346,26 @@ event ssl_conn_server_reply(c: connection, version: count,
local conn = ssl_connections[c$id]; local conn = ssl_connections[c$id];
local version_string = ssl_get_version_string(version); local version_string = ssl_get_version_string(version);
print ssl_log, fmt("%.6f #%d SSL connection server reply, %s", # print ssl_log, fmt("%.6f #%d SSL connection server reply, %s",
network_time(), conn$id, version_string); # network_time(), conn$id, version_string);
conn$version = version_string; conn$version = version_string;
local has_weak_ciphers = F;
for ( cs in ciphers ) for ( cs in ciphers )
{ {
# Demo: report servers who support weak ciphers. # Demo: report servers who support weak ciphers.
if ( ssl_report_server_weak && version == SSLv2 && if ( ssl_report_server_weak && version == SSLv2 &&
cs in myWeakCiphers ) cs in myWeakCiphers )
{
has_weak_ciphers = T;
event ssl_conn_weak( event ssl_conn_weak(
fmt("SSLv2 server supports weak cipher: %s (0x%x)", fmt("SSLv2 server supports weak cipher: %s (0x%x)",
ssl_get_cipher_name(cs), cs), c); ssl_get_cipher_name(cs), cs), c);
}
if ( ssl_log_ciphers )
print ssl_log, fmt("%.6f #%d server cipher %s (0x%x)",
network_time(), conn$id,
ssl_get_cipher_name(cs), cs);
} }
ssl_weak_server_ciphers[ c$id ] = has_weak_ciphers;
} }
event ssl_conn_established(c: connection, version: count, cipher_suite: count) event ssl_conn_established(c: connection, version: count, cipher_suite: count)
@ -365,25 +375,34 @@ event ssl_conn_established(c: connection, version: count, cipher_suite: count)
local conn = ssl_connections[c$id]; local conn = ssl_connections[c$id];
local version_string = ssl_get_version_string(version); local version_string = ssl_get_version_string(version);
print ssl_log, local has_weak_ciphers = F;
fmt("%.6f #%d handshake finished, %s",
network_time(), conn$id, version_string);
if ( cipher_suite in myWeakCiphers ) if ( cipher_suite in myWeakCiphers )
{
has_weak_ciphers = T;
event ssl_conn_weak(fmt("%.6f #%d weak cipher: %s (0x%x)", event ssl_conn_weak(fmt("%.6f #%d weak cipher: %s (0x%x)",
network_time(), conn$id, network_time(), conn$id,
ssl_get_cipher_name(cipher_suite), cipher_suite), c); ssl_get_cipher_name(cipher_suite), cipher_suite), c);
}
if ( ssl_log_ciphers )
print ssl_log, fmt("%.6f #%d connection cipher %s (0x%x)",
network_time(), conn$id,
ssl_get_cipher_name(cipher_suite), cipher_suite);
++SSL_cipherCount[cipher_suite]; ++SSL_cipherCount[cipher_suite];
# This should be the version identified with the session, unless # This should be the version identified with the session, unless
# there is some renegotiation. That will be caught later. # there is some renegotiation. That will be caught later.
conn$version = version_string; conn$version = version_string;
# log the connection
Log::write( "SSL_SERVER", [ $ts = network_time(),
$client_address = c$id$orig_h,
$client_port = c$id$orig_p,
$cert_subject = conn$client_cert$subject,
# TODO: dga 3/11 The following are not yet picked up
# $not_valid_before = ???,
# $not_valid_after = ???,
# $ssl_tls_version = ???,
$weak_client_ciphers_offered = ssl_weak_client_ciphers[ c$id ],
$weak_server_ciphers_offered = ssl_weak_server_ciphers[ c$id ],
$weak_cipher_agreed = has_weak_ciphers
] );
} }
event process_X509_extensions(c: connection, ex: X509_extension) event process_X509_extensions(c: connection, ex: X509_extension)
@ -394,8 +413,6 @@ event process_X509_extensions(c: connection, ex: X509_extension)
local msg = fmt("%.6f #%d X.509 extensions: ", network_time(), conn$id); local msg = fmt("%.6f #%d X.509 extensions: ", network_time(), conn$id);
for ( i in ex ) for ( i in ex )
msg = fmt("%s, %s", msg, ex[i]); msg = fmt("%s, %s", msg, ex[i]);
print ssl_log, msg;
} }
event ssl_session_insertion(c: connection, id: SSL_sessionID) event ssl_session_insertion(c: connection, id: SSL_sessionID)
@ -406,10 +423,6 @@ event ssl_session_insertion(c: connection, id: SSL_sessionID)
{ {
new_ssl_connection(c); new_ssl_connection(c);
print ssl_log,
fmt("%.6f #%d creating new SSL connection in ssl_session_insertion",
network_time(), ssl_connections[c$id]$id);
# None of the conn$object values will exist, so we leave this # None of the conn$object values will exist, so we leave this
# to prevent needless crashing. # to prevent needless crashing.
return; return;
@ -447,9 +460,6 @@ event ssl_conn_reused(c: connection, session_id: SSL_sessionID)
local conn = ssl_connections[c$id]; local conn = ssl_connections[c$id];
local id_index = md5_hash(session_id); local id_index = md5_hash(session_id);
print ssl_log, fmt("%.6f #%d reusing former SSL session: %s",
network_time(), conn$id, id_index);
# We cannot track sessions with SSLv2. # We cannot track sessions with SSLv2.
if ( conn$version == ssl_get_version_string(SSLv2) ) if ( conn$version == ssl_get_version_string(SSLv2) )
return; return;
@ -496,10 +506,6 @@ event ssl_X509_error(c: connection, err: int, err_string: string)
++c$hot; ++c$hot;
severity = "error"; severity = "error";
} }
print ssl_log,
fmt("%.6f #%d X.509 %s %s (%s)",
network_time(), conn$id, severity, error, err_string);
} }
event connection_state_remove(c: connection) event connection_state_remove(c: connection)
@ -517,20 +523,21 @@ event bro_init()
event bro_done() event bro_done()
{ {
print ssl_log, "Cipher suite statistics: "; # TODO: Do we want this end-of-run logging back?
for ( i in SSL_cipherCount ) # print ssl_log, "Cipher suite statistics: ";
print ssl_log, fmt("%s (0x%x): %d", ssl_get_cipher_name(i), i, # for ( i in SSL_cipherCount )
SSL_cipherCount[i]); # print ssl_log, fmt("%s (0x%x): %d", ssl_get_cipher_name(i), i,
# SSL_cipherCount[i]);
print ssl_log, ("count session ID"); #
print ssl_log, ("----- ---------------------------------"); # print ssl_log, ("count session ID");
for ( j in ssl_sessionIDs ) # print ssl_log, ("----- ---------------------------------");
if ( ssl_sessionIDs[j]$server_cert$subject != NONE ) # for ( j in ssl_sessionIDs )
{ # if ( ssl_sessionIDs[j]$server_cert$subject != NONE )
print ssl_log, # {
fmt("(%s) %s %s", # print ssl_log,
ssl_sessionIDs[j]$num_reuse, # fmt("(%s) %s %s",
ssl_sessionIDs[j]$server_cert$subject, # ssl_sessionIDs[j]$num_reuse,
j); # ssl_sessionIDs[j]$server_cert$subject,
} # j);
# }
} }