zeek/policy/ssl.bro
Robin Sommer 4bdb94955d Merge remote branch 'origin/topic/seth/ssl-binpac'
* origin/topic/seth/ssl-binpac:
  Fixed bug due to vectors now initially indexed on 0.
  Finished core support for new SSL analyzer.
  SSL analyzer changes with accompanying BiF.
  A table_s_of_s type to get around bifcl type limitation.
  Regenerated the Mozilla CA bundle without the untrusted server authentication certs.
  Complete rewrite to SSL analyzer.

Conflicts:
	src/AnalyzerTags.h
	src/CMakeLists.txt

Notes:

    - Haven't looked at the script-level, postponed to
      policy-scripts-new.

    - I renamed X509Extension to X509_extension for consistency.
2011-06-07 10:12:25 -07:00

591 lines
16 KiB
Text

@load conn-id
@load ssl-mozilla-CAs
global ssl_log = open_log_file("ssl") &raw_output &redef;
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 += {
["ssl"] = "tcp port 443",
["nntps"] = "tcp port 563",
["imap4-ssl"] = "tcp port 585",
["sshell"] = "tcp port 614",
["ldaps"] = "tcp port 636",
["ftps-data"] = "tcp port 989",
["ftps"] = "tcp port 990",
["telnets"] = "tcp port 992",
["imaps"] = "tcp port 993",
["ircs"] = "tcp port 994",
["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]] = [$ports = ssl_ports]
};
function ssl_get_version_string(version: count): string
{
if ( version == SSLv2 )
return "SSL version 2";
else if ( version == SSLv3 )
return "SSL version 3";
else if ( version == TLSv10 )
return "TLS version 1.0";
else if ( version == TLSv11 )
return "TLS version 1.1";
else
return "?.?";
}
event ssl_client_hello(c: connection, version: count, possible_ts: time, session_id: string, ciphers: count_set)
{
print ssl_log, cat_sep("\t", "\\N", network_time(), id_string(c$id),
"Client Hello",
ssl_get_version_string(version),
fmt("0x%s", bytestring_to_hexstr(session_id)),
fmt("%D", possible_ts));
}
event ssl_extension(c: connection, code: count, val: string)
{
if ( code == 0 )
print ssl_log, cat_sep("\t", "\\N", network_time(), id_string(c$id),
"ssl_extension", "server_name", sub(val, /^...../, ""));
}
event ssl_server_hello(c: connection, version: count, possible_ts: time, session_id: string, cipher: count, comp_method: count)
{
print ssl_log, cat_sep("\t", "\\N", network_time(), id_string(c$id),
"Server Hello",
ssl_get_version_string(version),
fmt("0x%s", bytestring_to_hexstr(session_id)),
fmt("%D", possible_ts));
}
event ssl_established(c: connection)
{
print ssl_log, cat_sep("\t", "\\N", network_time(), id_string(c$id),
"SSL session established");
}
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string)
{
print ssl_log, cat_sep("\t", "\\N", network_time(), id_string(c$id),
"X509 certificate", chain_idx,
cert$subject, cert$issuer,
fmt("%D", cert$not_valid_before),
fmt("%D", cert$not_valid_after));
}
event x509_cert_body(c: connection, cert: string)
{
print ssl_log, cat_sep("\t", "\\N", network_time(), id_string(c$id),
"X509 certificate body", fmt("0x%s", bytestring_to_hexstr(cert)));
}
## Old Code Below here!!!!
## --- Weak Cipher Demo -------------
#
#const myWeakCiphers: set[count] = {
# SSLv20_CK_RC4_128_EXPORT40_WITH_MD5,
# SSLv20_CK_RC2_128_CBC_EXPORT40_WITH_MD5,
# SSLv20_CK_DES_64_CBC_WITH_MD5,
#
# TLS_NULL_WITH_NULL_NULL,
# TLS_RSA_WITH_NULL_MD5,
# TLS_RSA_WITH_NULL_SHA,
# TLS_RSA_EXPORT_WITH_RC4_40_MD5,
# TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
# TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
# TLS_RSA_WITH_DES_CBC_SHA,
#
# TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
# TLS_DH_DSS_WITH_DES_CBC_SHA,
# TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
# TLS_DH_RSA_WITH_DES_CBC_SHA,
# TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
# TLS_DHE_DSS_WITH_DES_CBC_SHA,
# TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
# TLS_DHE_RSA_WITH_DES_CBC_SHA,
#
# TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5,
# TLS_DH_ANON_WITH_RC4_128_MD5,
# TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA,
# TLS_DH_ANON_WITH_DES_CBC_SHA,
# TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA,
#};
#
#const x509_ignore_errors: set[int] = {
# X509_V_OK,
# # X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
#};
#
#const x509_hot_errors: set[int] = {
# X509_V_ERR_CRL_SIGNATURE_FAILURE,
# X509_V_ERR_CERT_NOT_YET_VALID,
# X509_V_ERR_CERT_HAS_EXPIRED,
# X509_V_ERR_CERT_REVOKED,
# X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
# # X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE # for testing
#};
#
#redef Weird::weird_action += {
# [["SSLv2: Unknown CIPHER-SPEC in CLIENT-HELLO!",
# "SSLv2: Client has CipherSpecs > MAX_CIPHERSPEC_SIZE",
# "unexpected_SSLv3_record",
# "SSLv3_data_without_full_handshake"]] = Weird::WEIRD_IGNORE
#};
#global SSL_cipherCount: table[count] of count &default = 0;
#
#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.
#};
# SSL_sessionID index - used to track version assosciated with a session id.
#type SSL_sessionID_record: record {
# num_reuse: count;
# id: string; # 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;
#};
#
#global ssl_connections: table[conn_id] of ssl_connection_info;
#global ssl_sessionIDs: table[string] of SSL_sessionID_record
# &read_expire = 2 hrs;
#global ssl_connection_id = 0;
#
## Used when there's no issuer/subject/cipher.
#const NONE = "<none>";
#
## --- SSL helper functions ---------
#function new_ssl_connection(c: connection)
# {
# local conn = c$id;
# local new_id = ++ssl_connection_id;
#
# local info: ssl_connection_info;
# info$id = new_id;
# info$id_index = md5_hash(info$id);
# info$version = "";
# info$client_cert$issuer = NONE;
# info$client_cert$subject = NONE;
# info$server_cert$issuer = NONE;
# info$server_cert$subject = NONE;
# info$handshake_cipher = NONE;
# 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));
# }
#
#function new_sessionID_record(session: string)
# {
# local info: SSL_sessionID_record;
#
# info$num_reuse = 1;
# info$client_cert$issuer = NONE;
# info$client_cert$subject = NONE;
# info$server_cert$issuer = NONE;
# info$server_cert$subject = NONE;
# info$handshake_cipher = NONE;
#
# local index = md5_hash(session);
# ssl_sessionIDs[index] = info;
# }
#
#function ssl_get_cipher_name(cipherSuite: count): string
# {
# return cipherSuite in ssl_cipher_desc ?
# ssl_cipher_desc[cipherSuite] : "UNKNOWN";
# }
#
#function ssl_get_version_string(version: count): string
# {
# if ( version == SSLv2 )
# return "SSL version 2";
# else if ( version == SSLv3 )
# return "SSL version 3";
# else if ( version == TLSv10 )
# return "TLS version 1.0";
# else if ( version == TLSv11 )
# return "TLS version 1.1";
# else
# return "?.?";
# }
#
#function ssl_con2str(c: connection): string
# {
# return fmt("%s:%s -> %s:%s",
# c$id$orig_h, c$id$orig_p, c$id$resp_h, c$id$resp_p);
# }
#
#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)
# {
# print "ssl_certificate_seen";
# # Called whenever there's an certificate to analyze.
# # we could do something here, like...
#
# # if ( c$id$orig_h in hostsToIgnore )
# # {
# # ssl_store_certificates = F;
# # ssl_verify_certificates = F;
# # }
# # else
# # {
# # ssl_store_certificates = T;
# # ssl_verify_certificates = T;
# # }
# }
#
#event ssl_certificate(c: connection, cert: X509, is_server: bool)
# {
# print "ssl cert!";
# local direction = is_local_addr(c$id$orig_h) ? "client" : "server";
#
# lookup_ssl_conn(c, "ssl_certificate", T);
# local conn = ssl_connections[c$id];
#
# if( direction == "client" )
# conn$client_cert = cert;
# else
# {
# conn$server_cert = cert;
#
# # We have not filled in the field for the master session
# # for this connection. Do it now, but only if this is not a
# # SSLv2 connection (no session information in that case).
# if ( conn$id_index in ssl_sessionIDs &&
# ssl_sessionIDs[conn$id_index]$server_cert$subject == NONE )
# 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,
# ciphers: CipherSuitesList)
# {
# lookup_ssl_conn(c, "ssl_conn_attempt", F);
# 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;
#
# 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 )
# 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);
# }
# }
#
#event ssl_conn_server_reply(c: connection, version: count,
# ciphers: CipherSuitesList)
# {
# lookup_ssl_conn(c, "ssl_conn_server_reply", T);
#
# 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);
#
# conn$version = version_string;
#
# for ( cs in ciphers )
# {
# # Demo: report servers who support weak ciphers.
# if ( ssl_report_server_weak && version == SSLv2 &&
# cs in myWeakCiphers )
# 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);
# }
# }
#
#event ssl_conn_established(c: connection, version: count, cipher_suite: count)
# {
# lookup_ssl_conn(c, "ssl_conn_established", T);
#
# 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);
#
# if ( cipher_suite in myWeakCiphers )
# 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;
# }
#
#event process_X509_extensions(c: connection, ex: X509_extension)
# {
# lookup_ssl_conn(c, "process_X509_extensions", T);
# local conn = ssl_connections[c$id];
#
# 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: string)
# {
# local idd = c$id;
#
# if ( idd !in ssl_connections)
# {
# 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;
# }
#
# local conn = ssl_connections[idd];
# local id_index = md5_hash(id);
#
# # If there is no session with thIS id we create (a typical) one,
# # otherwise we move on.
# if ( id_index !in ssl_sessionIDs )
# {
# new_sessionID_record(id);
#
# local session = ssl_sessionIDs[id_index];
# session$version = conn$version;
# session$client_cert$subject = conn$client_cert$subject;
# session$server_cert$subject = conn$server_cert$subject;
# session$handshake_cipher = conn$handshake_cipher;
# session$id = id;
#
# conn$id_index = id_index;
# }
#
# else
# { # should we ever get here?
# session = ssl_sessionIDs[id_index];
# conn$id_index = id_index;
# }
# }
#
#event ssl_conn_reused(c: connection, session_id: string)
# {
# lookup_ssl_conn(c, "ssl_conn_reused", T);
# 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;
#
# if ( id_index !in ssl_sessionIDs )
# {
# new_sessionID_record(session_id);
# local session = ssl_sessionIDs[id_index];
# session$version = conn$version;
# session$client_cert$subject = conn$client_cert$subject;
# session$server_cert$subject = conn$server_cert$subject;
# session$id = session_id;
# }
# else
# session = ssl_sessionIDs[id_index];
#
# ++session$num_reuse;
#
# # At this point, the connection values have been set. We can then
# # compare session and connection values with some confidence.
# if ( session$version != conn$version ||
# session$handshake_cipher != conn$handshake_cipher )
# {
# NOTICE([$note=SSL_SessConIncon, $conn=c,
# $msg="session violation"]);
# ++c$hot;
# }
# }
#
#event ssl_X509_error(c: connection, err: int, err_string: string)
# {
# if ( err in x509_ignore_errors )
# return;
#
# lookup_ssl_conn(c, "ssl_X509_error", T);
# local conn = ssl_connections[c$id];
# local error =
# err in x509_errors ? x509_errors[err] : "unknown X.509 error";
#
# local severity = "warning";
# if ( err in x509_hot_errors )
# {
# NOTICE([$note=SSL_X509Violation, $conn=c, $msg=error]);
# ++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)
# {
# delete ssl_connections[c$id];
# }
#
#event bro_init()
# {
# if ( ssl_store_cert_path != "" )
# # The event engine will generate a run-time if this fails for
# # reasons other than that the directory already exists.
# mkdir(ssl_store_cert_path);
# }
#
#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);
# }
# }