zeek/scripts/base/protocols/ssl/files.zeek
Johanna Amann 833168090a Add ability to check if hostname is valid for a specific cert
This commit adds two new bifs, x509_check_hostname and
x509_check_cert_hostname. These bifs can be used to check if a given
hostname which can, e.g., be sent in a SNI is valid for a specific
certificate.

This PR furthermore modifies the ssl logs again, and adds information
about this to the log-file. Furthermore we now by default remove the
server certificate information from ssl.log - I doubt that this is often
looked at, it is not present in TLS 1.3, we do still have the SNI, and
if you need it you have the information in x509.log.

This also fixes a small potential problem in X509.cc assuming there
might be SAN-entries that contain null-bytes.

Baseline update will follow in another commit.
2021-06-29 15:00:48 +01:00

194 lines
5.8 KiB
Text

@load ./main
@load base/utils/conn-ids
@load base/frameworks/files
@load base/files/x509
module SSL;
export {
## Set this to true to includd the server certificate subject and
## issuer from the SSL log file. This information is still available
## in x509.log.
const log_include_server_certificate_subject_issuer = F &redef;
## Set this to true to include the client certificate subject
## and issuer in the SSL logfile. This information is rarely present
## and probably only interesting in very specific circumstances
const log_include_client_certificate_subject_issuer = F &redef;
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 certificate fingerprints for the
## certificates offered by the server.
cert_chain_fps: 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 certificate fingerprints for the
## certificates offered by the client.
client_cert_chain_fps: vector of string &optional &log;
## Subject of the X.509 certificate offered by the server.
subject: string &log &optional;
## Issuer 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;
## Set to true if the hostname sent in the SNI matches the certificate.
## Set to false if they do not match. Unset if the client did not send
## an SNI.
sni_matches_cert: bool &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, c in f$conns )
{
if ( c?$ssl )
{
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 zeek_init() &priority=5
{
Files::register_protocol(Analyzer::ANALYZER_SSL,
[$get_file_handle = SSL::get_file_handle,
$describe = SSL::describe_file]);
Files::register_protocol(Analyzer::ANALYZER_DTLS,
[$get_file_handle = SSL::get_file_handle,
$describe = SSL::describe_file]);
local ssl_filter = Log::get_filter(SSL::LOG, "default");
if ( ssl_filter$name != "<not found>" )
{
if ( ! ssl_filter?$exclude )
ssl_filter$exclude = set();
if ( ! log_include_server_certificate_subject_issuer )
{
add ssl_filter$exclude["subject"];
add ssl_filter$exclude["issuer"];
}
if ( ! log_include_client_certificate_subject_issuer )
{
add ssl_filter$exclude["client_subject"];
add ssl_filter$exclude["client_issuer"];
}
Log::add_filter(SSL::LOG, ssl_filter);
}
}
event file_sniff(f: fa_file, meta: fa_metadata) &priority=5
{
if ( ! f?$conns || |f$conns| != 1 )
return;
if ( ! f?$info || ! f$info?$mime_type )
return;
if ( ! ( f$info$mime_type == "application/x-x509-ca-cert" || f$info$mime_type == "application/x-x509-user-cert"
|| f$info$mime_type == "application/pkix-cert" ) )
return;
local c: connection &is_assigned; # to help static analysis
for ( cid, c in f$conns )
{
if ( ! c?$ssl )
return;
}
if ( ! c$ssl?$cert_chain )
{
c$ssl$cert_chain = vector();
c$ssl$client_cert_chain = vector();
c$ssl$cert_chain_fps = string_vec();
c$ssl$client_cert_chain_fps = string_vec();
}
if ( f$is_orig )
c$ssl$client_cert_chain += f$info;
else
c$ssl$cert_chain += f$info;
}
hook ssl_finishing(c: connection) &priority=20
{
if ( c$ssl?$cert_chain)
for ( i in c$ssl$cert_chain )
if ( c$ssl$cert_chain[i]?$x509 && c$ssl$cert_chain[i]$x509?$fp )
c$ssl$cert_chain_fps += c$ssl$cert_chain[i]$x509$fp;
if ( c$ssl?$client_cert_chain )
for ( i in c$ssl$client_cert_chain )
if ( c$ssl$client_cert_chain[i]?$x509 && c$ssl$client_cert_chain[i]$x509?$fp )
c$ssl$client_cert_chain_fps += c$ssl$client_cert_chain[i]$x509$fp;
if ( c$ssl?$cert_chain && |c$ssl$cert_chain| > 0 &&
c$ssl$cert_chain[0]?$x509 )
{
if ( c$ssl?$server_name )
{
if ( x509_check_cert_hostname(c$ssl$cert_chain[0]$x509$handle, c$ssl$server_name) != "" )
c$ssl$sni_matches_cert = T;
else
c$ssl$sni_matches_cert = F;
}
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_cert_chain[0]?$x509 )
{
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;
}
}