mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
SSL/TLS: Parse CertificateRequest message
This commit introduces parsing of the CertificateRequest message in the TLS handshake. It introduces a new event ssl_certificate_request, as well as a new function parse_distinguished_name, which can be used to parse part of the ssl_certificate_request event parameters. This commit also introduces a new policy script, which appends information about the CAs a TLS server requests in the CertificateRequest message, if it sends it.
This commit is contained in:
parent
b73dda5cff
commit
b56b856da9
17 changed files with 221 additions and 4 deletions
5
NEWS
5
NEWS
|
@ -58,6 +58,11 @@ New Functionality
|
|||
- The X.509 certificate parser now exposes the signature type that is given inside
|
||||
the signed portion of the certificate.
|
||||
|
||||
- The SSL parser now parses the CertificateRequest handshake message. There is a new
|
||||
``ssl_certificate_request`` event and a new ``parse_distinguished_name`` function.
|
||||
We also added the ``protocols/ssl/certificate-request-info`` policy script, that
|
||||
adds some additional information to ``ssl.log``.
|
||||
|
||||
- Add logging metrics for streams (``zeek-log-stream-writes``) and writers
|
||||
(``zeek-log-writer-writes-total``).
|
||||
|
||||
|
|
23
scripts/policy/protocols/ssl/certificate-request-info.zeek
Normal file
23
scripts/policy/protocols/ssl/certificate-request-info.zeek
Normal file
|
@ -0,0 +1,23 @@
|
|||
##! When the server requests a client certificate, it optionally may specify a list of CAs that
|
||||
##! it accepts. If the server does this, this script adds this list to ssl.log.
|
||||
|
||||
@load base/protocols/ssl
|
||||
|
||||
module SSL;
|
||||
|
||||
redef record SSL::Info += {
|
||||
## List of cient certificate CAs accepted by the server
|
||||
requested_client_certificate_authorities: vector of string &optional &log;
|
||||
};
|
||||
|
||||
event ssl_certificate_request(c: connection, is_client: bool, certificate_types: index_vec, supported_signature_algorithms: SSL::SignatureAndHashAlgorithm, certificate_authorities: string_vec)
|
||||
{
|
||||
if ( is_client )
|
||||
return;
|
||||
|
||||
local out: vector of string = vector();
|
||||
for ( i in certificate_authorities )
|
||||
out[i] = parse_distinguished_name(certificate_authorities[i]);
|
||||
|
||||
c$ssl$requested_client_certificate_authorities = out;
|
||||
}
|
|
@ -122,6 +122,7 @@
|
|||
@load protocols/ssh/geo-data.zeek
|
||||
@load protocols/ssh/interesting-hostnames.zeek
|
||||
@load protocols/ssh/software.zeek
|
||||
@load protocols/ssl/certificate-request-info.zeek
|
||||
@load protocols/ssl/decryption.zeek
|
||||
@load protocols/ssl/expiring-certs.zeek
|
||||
@load protocols/ssl/heartbleed.zeek
|
||||
|
|
|
@ -657,7 +657,7 @@ event ssl_stapled_ocsp%(c: connection, is_client: bool, response: string%);
|
|||
##
|
||||
## .. zeek:see:: ssl_alert ssl_established ssl_extension ssl_server_hello
|
||||
## ssl_session_ticket_handshake x509_certificate ssl_client_hello
|
||||
## ssl_change_cipher_spec ssl_connection_flipped
|
||||
## ssl_change_cipher_spec ssl_connection_flipped ssl_certificate_request
|
||||
event ssl_handshake_message%(c: connection, is_client: bool, msg_type: count, length: count%);
|
||||
|
||||
## This event is raised when a SSL/TLS ChangeCipherSpec message is encountered
|
||||
|
@ -687,3 +687,26 @@ event ssl_change_cipher_spec%(c: connection, is_client: bool%);
|
|||
## ssl_session_ticket_handshake x509_certificate ssl_client_hello
|
||||
## ssl_handshake_message
|
||||
event ssl_connection_flipped%(c: connection%);
|
||||
|
||||
## This event is raised, when a Certificate Request handshake message is encountered. This
|
||||
## Message can be used by a TLS server to request a client certificate.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## is_client: True if event is raised for the client side of the connection
|
||||
## (the side that sends the client hello). This is typically equivalent
|
||||
## with the originator, but does not have to be in all circumstances.
|
||||
##
|
||||
## certificate_types: List of the types of certificates that the client may offer.
|
||||
##
|
||||
## supported_signature_algorithms: List of hash/sighature algorithm pairs that the server
|
||||
## supports, listed in descending order of preferences.
|
||||
##
|
||||
## certificate_authorities: List of distinguished names of certificate authorities that are
|
||||
## acceptable to the server. The individual entries are DER encoded.
|
||||
## :zeek:id:`parse_distinguished_name` can be used to decode the strings.
|
||||
##
|
||||
## .. zeek:see:: ssl_handshake_message x509_certificate ssl_server_hello ssl_client_hello
|
||||
## parse_distinguished_name
|
||||
event ssl_certificate_request%(c: connection, is_client: bool, certificate_types: index_vec, supported_signature_algorithms: SSL::SignatureAndHashAlgorithm, certificate_authorities: string_vec%);
|
||||
|
||||
|
|
|
@ -65,3 +65,35 @@ function set_keys%(c: connection, keys: string%): bool
|
|||
|
||||
return zeek::val_mgr->False();
|
||||
%}
|
||||
|
||||
## Decodes a DER-encoded distinguished name into an ASCII string,
|
||||
## using the RFC2253 representation
|
||||
##
|
||||
## dn: DER encoded distinguished name
|
||||
##
|
||||
## Returns: Ascii representation on success, empty string on failure
|
||||
##
|
||||
## .. zeek:see:: ssl_certificate_request
|
||||
function parse_distinguished_name%(dn: string%): string
|
||||
%{
|
||||
std::string out = "";
|
||||
const unsigned char* in = dn->Bytes();
|
||||
|
||||
X509_NAME* dn_x509 = d2i_X509_NAME(nullptr, &in, dn->Len());
|
||||
if ( ! dn_x509 )
|
||||
{
|
||||
// we were not able to parse. Let's return an empty string.
|
||||
return zeek::make_intrusive<zeek::StringVal>("");
|
||||
}
|
||||
|
||||
char buf[256];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
|
||||
X509_NAME_print_ex(bio, dn_x509, 0, XN_FLAG_RFC2253);
|
||||
int len = BIO_gets(bio, buf, sizeof(buf));
|
||||
out.assign(buf, len);
|
||||
BIO_free(bio);
|
||||
X509_NAME_free(dn_x509);
|
||||
return zeek::make_intrusive<zeek::StringVal>(out);
|
||||
%}
|
||||
|
|
|
@ -626,6 +626,47 @@ refine connection Handshake_Conn += {
|
|||
return true;
|
||||
%}
|
||||
|
||||
function proc_certificate_request(rec: HandshakeRecord, req: CertificateRequest) : bool
|
||||
%{
|
||||
if ( ! ssl_certificate_request )
|
||||
return true;
|
||||
|
||||
auto ctlist = zeek::make_intrusive<zeek::VectorVal>(zeek::id::find_type<zeek::VectorType>("index_vec"));
|
||||
auto ctypes = ${req.certificate_types};
|
||||
|
||||
if ( ctypes )
|
||||
for ( unsigned int i = 0; i < ctypes->size(); ++i)
|
||||
ctlist->Assign(i, zeek::val_mgr->Count((*ctypes)[i]));
|
||||
|
||||
auto slist = zeek::make_intrusive<zeek::VectorVal>(zeek::id::find_type<zeek::VectorType>("signature_and_hashalgorithm_vec"));
|
||||
if ( ${req.uses_signature_and_hashalgorithm} )
|
||||
{
|
||||
auto sigalgs = ${req.supported_signature_algorithms.supported_signature_algorithms};
|
||||
|
||||
if ( sigalgs )
|
||||
{
|
||||
for ( unsigned int i = 0; i < sigalgs->size(); ++i )
|
||||
{
|
||||
auto el = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SSL::SignatureAndHashAlgorithm);
|
||||
el->Assign(0, (*sigalgs)[i]->HashAlgorithm());
|
||||
el->Assign(1, (*sigalgs)[i]->SignatureAlgorithm());
|
||||
slist->Assign(i, std::move(el));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto calist = zeek::make_intrusive<zeek::VectorVal>(zeek::id::find_type<zeek::VectorType>("string_vec"));
|
||||
auto certificate_authorities = ${req.certificate_authorities.certificate_authorities};
|
||||
if ( certificate_authorities )
|
||||
for ( unsigned int i = 0; i < certificate_authorities->size(); ++i )
|
||||
calist->Assign(i, zeek::make_intrusive<zeek::StringVal>((*certificate_authorities)[i]->certificate_authority().length(), (const char*) (*certificate_authorities)[i]->certificate_authority().data()));
|
||||
|
||||
zeek::BifEvent::enqueue_ssl_certificate_request(zeek_analyzer(), zeek_analyzer()->Conn(), ${rec.is_orig} ^ flipped_, ctlist, slist, calist);
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
};
|
||||
|
||||
refine typeattr ClientHello += &let {
|
||||
|
@ -754,3 +795,7 @@ refine typeattr Handshake += &let {
|
|||
refine typeattr SignedCertificateTimestamp += &let {
|
||||
proc : bool = $context.connection.proc_signedcertificatetimestamp(rec, version, logid, timestamp, digitally_signed_algorithms, digitally_signed_signature);
|
||||
};
|
||||
|
||||
refine typeattr CertificateRequest += &let {
|
||||
proc: bool = $context.connection.proc_certificate_request(rec, this);
|
||||
};
|
||||
|
|
|
@ -439,9 +439,30 @@ type DhAnonServerKeyExchange(rec: HandshakeRecord) = record {
|
|||
# V3 Certificate Request (7.4.4.)
|
||||
######################################################################
|
||||
|
||||
type CertificateAuthorities = record {
|
||||
certificate_authority_len: uint16;
|
||||
certificate_authority: bytestring &length=certificate_authority_len;
|
||||
};
|
||||
|
||||
type CertificateAuthoritiesContainer = record {
|
||||
certificate_authorities: CertificateAuthorities[] &until($input.length() == 0);
|
||||
};
|
||||
|
||||
# For now, ignore Certificate Request Details; just eat up message.
|
||||
type CertificateRequest(rec: HandshakeRecord) = record {
|
||||
certificate_types_len: uint8;
|
||||
certificate_types: uint8[certificate_types_len];
|
||||
alg: case uses_signature_and_hashalgorithm of {
|
||||
true -> supported_signature_algorithms: SignatureAlgorithm(rec);
|
||||
false -> nothing: bytestring &length=0;
|
||||
} &requires(uses_signature_and_hashalgorithm);
|
||||
certificate_authorities_len: uint16;
|
||||
certificate_authorities: CertificateAuthoritiesContainer &length=certificate_authorities_len;
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
uses_signature_and_hashalgorithm : bool =
|
||||
($context.connection.chosen_version() > TLSv11) &&
|
||||
($context.connection.chosen_version() != DTLSv10);
|
||||
};
|
||||
|
||||
|
||||
|
@ -931,7 +952,7 @@ type PreSharedKey(rec: HandshakeRecord) = case rec.msg_type of {
|
|||
type SignatureAlgorithm(rec: HandshakeRecord) = record {
|
||||
length: uint16;
|
||||
supported_signature_algorithms: SignatureAndHashAlgorithm[] &until($input.length() == 0);
|
||||
}
|
||||
} &length=length+2;
|
||||
|
||||
type EllipticCurves(rec: HandshakeRecord) = record {
|
||||
length: uint16;
|
||||
|
|
5
testing/btest/Baseline/bifs.x509_parse_dn/.stdout
Normal file
5
testing/btest/Baseline/bifs.x509_parse_dn/.stdout
Normal file
|
@ -0,0 +1,5 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
CN=certauth.idrix.fr
|
||||
|
||||
CN=Senate PIV-I CA G4,OU=Office of the Sergeant at Arms,OU=U.S. Senate,O=U.S. Government,C=US
|
||||
OU=\E3\82\A2\E3\83\97\E3\83\AA\E3\82\B1\E3\83\BC\E3\82\B7\E3\83\A7\E3\83\B3CA,O=\E6\97\A5\E6\9C\AC\E5\9B\BD\E6\94\BF\E5\BA\9C,C=JP
|
|
@ -0,0 +1,15 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
[1, 2, 64]
|
||||
[[HashAlgorithm=4, SignatureAlgorithm=3], [HashAlgorithm=5, SignatureAlgorithm=3], [HashAlgorithm=6, SignatureAlgorithm=3], [HashAlgorithm=8, SignatureAlgorithm=7], [HashAlgorithm=8, SignatureAlgorithm=8], [HashAlgorithm=8, SignatureAlgorithm=9], [HashAlgorithm=8, SignatureAlgorithm=10], [HashAlgorithm=8, SignatureAlgorithm=11], [HashAlgorithm=8, SignatureAlgorithm=4], [HashAlgorithm=8, SignatureAlgorithm=5], [HashAlgorithm=8, SignatureAlgorithm=6], [HashAlgorithm=4, SignatureAlgorithm=1], [HashAlgorithm=5, SignatureAlgorithm=1], [HashAlgorithm=6, SignatureAlgorithm=1], [HashAlgorithm=3, SignatureAlgorithm=3], [HashAlgorithm=3, SignatureAlgorithm=1], [HashAlgorithm=3, SignatureAlgorithm=2], [HashAlgorithm=4, SignatureAlgorithm=2], [HashAlgorithm=5, SignatureAlgorithm=2], [HashAlgorithm=6, SignatureAlgorithm=2]]
|
||||
========
|
||||
[1]
|
||||
[[HashAlgorithm=4, SignatureAlgorithm=1]]
|
||||
0H1\x0b0\x09\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\x08\x0c\x0aSome-State1\x120\x10\x06\x03U\x04\x07\x0c\x09Somewhere1\x100\x0e\x06\x03U\x04\x0a\x0c\x07SomeOrg
|
||||
O=SomeOrg,L=Somewhere,ST=Some-State,C=US
|
||||
========
|
||||
[1, 64, 2]
|
||||
[]
|
||||
========
|
||||
[1, 2]
|
||||
[]
|
||||
========
|
|
@ -0,0 +1,11 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
#separator \x09
|
||||
#set_separator ,
|
||||
#empty_field (empty)
|
||||
#unset_field -
|
||||
#path ssl
|
||||
#open XXXX-XX-XX-XX-XX-XX
|
||||
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert requested_client_certificate_authorities
|
||||
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool vector[string]
|
||||
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 fd42:496a:d659:bb85::1 52464 fd42:496a:d659:bb85:216:3eff:fe6a:a257 3000 TLSv12 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 x25519 - F handshake_failure - F CsxkrnXGIl 0a171ee771a26530c650fe8b8a6bf205177bfb64fbb3e5303ba348c13ffc7dfa,c628dd5aae1f216da6ce4f8f914fb7141c2b0afd3522cce5900bcc4840657bfd (empty) - O=SomeOrg\x2cL=Somewhere\x2cST=Some-State\x2cC=US
|
||||
#close XXXX-XX-XX-XX-XX-XX
|
BIN
testing/btest/Traces/tls/certificate-request-failed.pcap
Normal file
BIN
testing/btest/Traces/tls/certificate-request-failed.pcap
Normal file
Binary file not shown.
BIN
testing/btest/Traces/tls/client-certificate.pcap
Normal file
BIN
testing/btest/Traces/tls/client-certificate.pcap
Normal file
Binary file not shown.
10
testing/btest/bifs/x509_parse_dn.zeek
Normal file
10
testing/btest/bifs/x509_parse_dn.zeek
Normal file
|
@ -0,0 +1,10 @@
|
|||
# @TEST-EXEC: zeek -b %INPUT
|
||||
# @TEST-EXEC: btest-diff .stdout
|
||||
|
||||
event zeek_init()
|
||||
{
|
||||
print parse_distinguished_name("0\x1c1\x1a0\x18\x06\x03U\x04\x03\x13\x11certauth.idrix.fr");
|
||||
print parse_distinguished_name("00000\x1c1\x1a0\x18\x06\x03U\x04\x03\x13\x11certauth.idrix.fr"); # invalid
|
||||
print parse_distinguished_name("\x30\x81\x83\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x18\x30\x16\x06\x03\x55\x04\x0A\x13\x0F\x55\x2E\x53\x2E\x20\x47\x6F\x76\x65\x72\x6E\x6D\x65\x6E\x74\x31\x14\x30\x12\x06\x03\x55\x04\x0B\x13\x0B\x55\x2E\x53\x2E\x20\x53\x65\x6E\x61\x74\x65\x31\x27\x30\x25\x06\x03\x55\x04\x0B\x13\x1E\x4F\x66\x66\x69\x63\x65\x20\x6F\x66\x20\x74\x68\x65\x20\x53\x65\x72\x67\x65\x61\x6E\x74\x20\x61\x74\x20\x41\x72\x6D\x73\x31\x1B\x30\x19\x06\x03\x55\x04\x03\x13\x12\x53\x65\x6E\x61\x74\x65\x20\x50\x49\x56\x2D\x49\x20\x43\x41\x20\x47\x34");
|
||||
print parse_distinguished_name("\x30\x4C\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x4A\x50\x31\x18\x30\x16\x06\x03\x55\x04\x0A\x0C\x0F\xE6\x97\xA5\xE6\x9C\xAC\xE5\x9B\xBD\xE6\x94\xBF\xE5\xBA\x9C\x31\x23\x30\x21\x06\x03\x55\x04\x0B\x0C\x1A\xE3\x82\xA2\xE3\x83\x97\xE3\x83\xAA\xE3\x82\xB1\xE3\x83\xBC\xE3\x82\xB7\xE3\x83\xA7\xE3\x83\xB3\x43\x41\x30");
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
# This tests the certificate_request message parsing
|
||||
|
||||
# @TEST-EXEC: zeek -b -r $TRACES/tls/client-certificate.pcap %INPUT > out
|
||||
# @TEST-EXEC: zeek -C -b -r $TRACES/tls/certificate-request-failed.pcap %INPUT >> out
|
||||
# @TEST-EXEC: zeek -C -b -r $TRACES/tls/webrtc-stun.pcap %INPUT >> out
|
||||
# @TEST-EXEC: zeek -C -b -r $TRACES/mysql/encrypted.trace %INPUT >> out
|
||||
# @TEST-EXEC: btest-diff out
|
||||
|
||||
@load base/protocols/ssl
|
||||
@load base/protocols/mysql
|
||||
|
||||
event ssl_certificate_request(c: connection, is_client: bool, certificate_types: index_vec, supported_signature_algorithms: SSL::SignatureAndHashAlgorithm, certificate_authorities: string_vec)
|
||||
{
|
||||
print certificate_types;
|
||||
print supported_signature_algorithms;
|
||||
for ( i in certificate_authorities )
|
||||
{
|
||||
print certificate_authorities[i];
|
||||
print parse_distinguished_name(certificate_authorities[i]);
|
||||
}
|
||||
print "========";
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
# @TEST-EXEC: zeek -C -r $TRACES/tls/certificate-request-failed.pcap %INPUT
|
||||
# @TEST-EXEC: btest-diff ssl.log
|
||||
|
||||
@load protocols/ssl/certificate-request-info
|
2
testing/external/commit-hash.zeek-testing
vendored
2
testing/external/commit-hash.zeek-testing
vendored
|
@ -1 +1 @@
|
|||
ce420be749831180789003152320cadb6c8101e1
|
||||
0afa1572c003bceccb6fc10a4c7865e8777799c9
|
||||
|
|
|
@ -1 +1 @@
|
|||
b78a6881733079ef5e17c4dea95a8a2566f8b3b8
|
||||
a35c14be50c65604d31d672166bd1fef2791e6e9
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue