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 ssl-ciphers
@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 += {
SSL_X509Violation, # blanket X509 error
SSL_SessConIncon, # session data not consistent with connection
};
const SSLv2 = 0x0002;
const SSLv3 = 0x0300;
const TLSv10 = 0x0301;
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
# --- well-known ports for ssl ---------
redef capture_filters += {
@ -69,11 +99,6 @@ redef capture_filters += {
["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 += {
[[ANALYZER_SSL, ANALYZER_SSL_BINPAC]] = [$ports = ssl_ports]
};
@ -130,29 +155,33 @@ redef Weird::weird_action += {
"SSLv3_data_without_full_handshake"]] = Weird::WEIRD_IGNORE
};
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 {
id: count; # the log identifier number
connection_id: conn_id; # IP connection information
version: string; # version assosciated with connection
client_cert: X509;
server_cert: X509;
id_index: string; # index for associated SSL_sessionID
handshake_cipher: string; # agreed-upon cipher for session/conn.
id: count; # the log identifier number
connection_id: conn_id; # IP connection information
version: string; # version associated with connection
client_cert: X509;
server_cert: X509;
id_index: string; # index for associated SSL_sessionID
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 {
num_reuse: count;
id: SSL_sessionID; # literal session ID
num_reuse: count;
id: SSL_sessionID; # literal session ID
# everything below is an example of session vs connection monitoring.
version: string; # version assosciated with session id
client_cert: X509;
server_cert: X509;
handshake_cipher: string;
version: string; # version assosciated with session id
client_cert: X509;
server_cert: X509;
handshake_cipher: string;
};
global ssl_connections: table[conn_id] of ssl_connection_info;
@ -181,10 +210,7 @@ function new_ssl_connection(c: connection)
info$connection_id = conn;
ssl_connections[conn] = info;
append_addl(c, fmt("#%d", new_id));
print ssl_log, fmt("%.6f #%d %s start",
network_time(), new_id, id_string(conn));
append_addl( c, fmt( "#%d", new_id ) );
}
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)
{
if ( c$id !in ssl_connections )
{
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)
{
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 -------------------
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...
# 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 =
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,
@ -303,30 +314,28 @@ event ssl_conn_attempt(c: connection, version: count,
local conn = ssl_connections[c$id];
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;
local has_weak_ciphers = F;
for ( cs in ciphers )
{ # display a list of the cipher suites
# 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(
fmt("SSL client supports weak cipher: %s (0x%x)",
ssl_get_cipher_name(cs), cs), c);
}
# Demo: report unknown ciphers.
if ( ssl_report_client_unknown && cs !in ssl_cipher_desc )
event ssl_conn_weak(
fmt("SSL: unknown cipher-spec: %s (0x%x)",
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,
@ -337,25 +346,26 @@ event ssl_conn_server_reply(c: connection, version: count,
local conn = ssl_connections[c$id];
local version_string = ssl_get_version_string(version);
print ssl_log, fmt("%.6f #%d SSL connection server reply, %s",
network_time(), conn$id, version_string);
# print ssl_log, fmt("%.6f #%d SSL connection server reply, %s",
# network_time(), conn$id, version_string);
conn$version = version_string;
local has_weak_ciphers = F;
for ( cs in ciphers )
{
# Demo: report servers who support weak ciphers.
if ( ssl_report_server_weak && version == SSLv2 &&
cs in myWeakCiphers )
{
has_weak_ciphers = T;
event ssl_conn_weak(
fmt("SSLv2 server supports weak cipher: %s (0x%x)",
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)
@ -365,25 +375,34 @@ event ssl_conn_established(c: connection, version: count, cipher_suite: count)
local conn = ssl_connections[c$id];
local version_string = ssl_get_version_string(version);
print ssl_log,
fmt("%.6f #%d handshake finished, %s",
network_time(), conn$id, version_string);
local has_weak_ciphers = F;
if ( cipher_suite in myWeakCiphers )
{
has_weak_ciphers = T;
event ssl_conn_weak(fmt("%.6f #%d weak cipher: %s (0x%x)",
network_time(), conn$id,
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];
# This should be the version identified with the session, unless
# there is some renegotiation. That will be caught later.
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)
@ -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);
for ( i in ex )
msg = fmt("%s, %s", msg, ex[i]);
print ssl_log, msg;
}
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);
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
# to prevent needless crashing.
return;
@ -447,9 +460,6 @@ event ssl_conn_reused(c: connection, session_id: SSL_sessionID)
local conn = ssl_connections[c$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.
if ( conn$version == ssl_get_version_string(SSLv2) )
return;
@ -496,10 +506,6 @@ event ssl_X509_error(c: connection, err: int, err_string: string)
++c$hot;
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)
@ -517,20 +523,21 @@ event bro_init()
event bro_done()
{
print ssl_log, "Cipher suite statistics: ";
for ( i in SSL_cipherCount )
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, ("----- ---------------------------------");
for ( j in ssl_sessionIDs )
if ( ssl_sessionIDs[j]$server_cert$subject != NONE )
{
print ssl_log,
fmt("(%s) %s %s",
ssl_sessionIDs[j]$num_reuse,
ssl_sessionIDs[j]$server_cert$subject,
j);
}
# TODO: Do we want this end-of-run logging back?
# print ssl_log, "Cipher suite statistics: ";
# for ( i in SSL_cipherCount )
# 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, ("----- ---------------------------------");
# for ( j in ssl_sessionIDs )
# if ( ssl_sessionIDs[j]$server_cert$subject != NONE )
# {
# print ssl_log,
# fmt("(%s) %s %s",
# ssl_sessionIDs[j]$num_reuse,
# ssl_sessionIDs[j]$server_cert$subject,
# j);
# }
}