diff --git a/scripts/base/protocols/ssl/main.bro b/scripts/base/protocols/ssl/main.bro index 50c4150c0a..e774a20b89 100644 --- a/scripts/base/protocols/ssl/main.bro +++ b/scripts/base/protocols/ssl/main.bro @@ -35,6 +35,11 @@ export { ## (especially with large file transfers). const disable_analyzer_after_detection = T &redef; + ## The openssl command line utility. If it's in the path the default + ## value will work, otherwise a full path string can be supplied for the + ## utility. + const openssl_util = "openssl" &redef; + global log_ssl: event(rec: Info); const ports = { diff --git a/scripts/policy/protocols/ssl/expiring-certs.bro b/scripts/policy/protocols/ssl/expiring-certs.bro new file mode 100644 index 0000000000..dc15bce077 --- /dev/null +++ b/scripts/policy/protocols/ssl/expiring-certs.bro @@ -0,0 +1,62 @@ +##! This script can be used to generate notices when X.509 certificates over +##! SSL/TLS are expired or going to expire based on the date and time values +##! stored within the certificate. Notices will be suppressed for 1 day +##! by default. + +@load base/protocols/ssl +@load base/frameworks/notice + +module SSL; + +export { + redef enum Notice::Type += { + ## Indicates that a certificate's NotValidAfter date has lapsed and + ## the certificate is now invalid. + Certificate_Expired, + ## Indicates that a certificate is going to expire within + ## :bro:id:`SSL::notify_when_cert_expiring_in`. + Certificate_Expires_Soon, + ## Indicates that a certificate's NotValidBefore date is future dated. + Certificate_Not_Valid_Yet, + }; + + ## Which hosts you would like to be notified about which have certificates + ## that are going to be expiring soon. + ## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS + const notify_certs_expiration = LOCAL_HOSTS &redef; + + ## The time before a certificate is going to expire that you would like to + ## start receiving notices. + const notify_when_cert_expiring_in = 30days &redef; +} + +redef Notice::type_suppression_intervals += { + [[Certificate_Expired, Certificate_Expires_Soon, Certificate_Not_Valid_Yet]] = 1day +}; + +event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=5 + { + # If this isn't the host cert or we aren't interested in the server, just return. + if ( chain_idx != 0 || ! addr_matches_host(c$id$resp_h, notify_certs_expiration) ) + return; + + if ( cert$not_valid_before > network_time() ) + NOTICE([$note=Certificate_Not_Valid_Yet, + $conn=c, $suppress_for=1day, + $msg=fmt("Certificate %s isn't valid until %T", cert$subject, cert$not_valid_before), + $identifier=fmt("%s:%d-%s", c$id$resp_h, c$id$resp_p, md5_hash(der_cert))]); + + else if ( cert$not_valid_after < network_time() ) + NOTICE([$note=Certificate_Expired, + $conn=c, $suppress_for=1day, + $msg=fmt("Certificate %s expired at %T", cert$subject, cert$not_valid_after), + $identifier=fmt("%s:%d-%s", c$id$resp_h, c$id$resp_p, md5_hash(der_cert))]); + + else if ( cert$not_valid_after - notify_when_cert_expiring_in < network_time() ) + NOTICE([$note=Certificate_Expires_Soon, + $msg=fmt("Certificate %s is going to expire at %T", cert$subject, cert$not_valid_after), + $conn=c, $suppress_for=1day, + $identifier=fmt("%s:%d-%s", c$id$resp_h, c$id$resp_p, md5_hash(der_cert))]); + } + + \ No newline at end of file diff --git a/scripts/policy/protocols/ssl/extract-certs-pem.bro b/scripts/policy/protocols/ssl/extract-certs-pem.bro new file mode 100644 index 0000000000..c0d9f9b36e --- /dev/null +++ b/scripts/policy/protocols/ssl/extract-certs-pem.bro @@ -0,0 +1,48 @@ +##! This script is used to extract host certificates seen on the wire to disk +##! after being converted to PEM files. The certificates will be stored in +##! a single file, one for local certificates and one for remote certificates. +##! +##! A couple of things to think about with this script:: +##! +##! - It doesn't work well on a cluster because each worker will write its +##! own certificate files and no duplicate checking is done across +##! clusters so each node would log each certificate. +##! +##! - If there is a certificate input based vulnerability found in the +##! openssl command line utility, you could be in trouble because this +##! script uses that utility to convert from DER to PEM certificates. +##! + +@load base/protocols/ssl + +module SSL; + +export { + ## Setting to control if host certificates offered by the defined hosts + ## will be written to the PEM certificates file. + ## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS + const extract_certs_pem = LOCAL_HOSTS &redef; +} + +## This is an internally maintained variable to prevent relogging of +## certificates that have already been seen. It is indexed on an md5 sum of +## the certificate. +global extracted_certs: set[string] = set() &read_expire=1hr &redef; + +event ssl_established(c: connection) + { + if ( ! c$ssl?$cert ) + return; + if ( ! addr_matches_host(c$id$resp_h, extract_certs_pem) ) + return; + + local cert_hash = md5_hash(c$ssl$cert); + if ( cert_hash in extracted_certs ) + # If we already extracted this cert, don't do it again. + return; + + add extracted_certs[cert_hash]; + local side = Site::is_local_addr(c$id$resp_h) ? "local" : "remote"; + local cmd = fmt("%s x509 -inform DER -outform PEM >> certs-%s.pem", openssl_util, side); + piped_exec(cmd, c$ssl$cert); + } \ No newline at end of file