mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 22:58:20 +00:00

By default, each certificate is now output only once per hour. This also should work in cluster mode, where we use the net broker-table-syncing feature to distribute the information about already seen certificates across the entire cluster. Log caching is also pretty configureable and can be changed using a range of confiuration options and hooks. Note that this is currently completely separate from X509 events caching, which prevents duplicate parsing of X509 certificates.
112 lines
4.3 KiB
Text
112 lines
4.3 KiB
Text
##! This script sets up the certificate event cache handling of Zeek.
|
|
##!
|
|
##! The Zeek core provided a method to skip certificate processing for known certificates.
|
|
##! For more details about this functionality, see :zeek:see:`x509_set_certificate_cache`.
|
|
##!
|
|
##! This script uses this feature to lower the amount of processing that has to be performed
|
|
##! by Zeek by caching all certificate events for common certificates. For these certificates,
|
|
##! the parsing of certificate information in the core is disabled. Instead, the cached events
|
|
##! and data structures from the previous certificates are used.
|
|
|
|
@load ./main
|
|
|
|
module X509;
|
|
|
|
export {
|
|
## How often do you have to encounter a certificate before
|
|
## caching the events for it. Set to 0 to disable caching of certificates.
|
|
option caching_required_encounters : count = 10;
|
|
|
|
## The timespan over which caching_required_encounters has to be reached
|
|
option caching_required_encounters_interval : interval = 62 secs;
|
|
|
|
## After a certificate has not been encountered for this time, it
|
|
## may be evicted from the certificate event cache.
|
|
option certificate_cache_minimum_eviction_interval : interval = 62 secs;
|
|
|
|
## Maximum size of the certificate event cache
|
|
option certificate_cache_max_entries : count = 10000;
|
|
|
|
## This hook performs event-replays in case a certificate that already
|
|
## is in the cache is encountered.
|
|
##
|
|
## It is possible to change this behavior/skip sending the events by
|
|
## installing a higher priority hook instead.
|
|
global x509_certificate_cache_replay: hook(f: fa_file, e: X509::Info, sha256: string);
|
|
}
|
|
|
|
# Table tracking potential certificates to cache - indexed by the SHA256 of the
|
|
# raw on-the-wire representation (DER).
|
|
global certificates_encountered: table[string] of count &create_expire=caching_required_encounters_interval;
|
|
|
|
# Table caching the output of the X509 analyzer for commonly seen certificates.
|
|
# This is indexed by SHA256 and contains the Info record of the first certificate
|
|
# encountered. We use this info record to re-play the events.
|
|
global certificate_cache: table[string] of X509::Info &read_expire=certificate_cache_minimum_eviction_interval;
|
|
|
|
event zeek_init() &priority=5
|
|
{
|
|
x509_set_certificate_cache(certificate_cache);
|
|
x509_set_certificate_cache_hit_callback(x509_certificate_cache_replay);
|
|
}
|
|
|
|
hook x509_certificate_cache_replay(f: fa_file, e: X509::Info, sha256: string)
|
|
{
|
|
# we encountered a cached cert. The X509 analyzer will skip it. Let's raise all the events that it typically
|
|
# raises by ourselfes.
|
|
|
|
# first - let's checked if it already has an x509 record. That would mean that someone raised the file_hash event
|
|
# several times for the certificate - in which case we bail out.
|
|
if ( f$info?$x509 )
|
|
return;
|
|
|
|
event x509_certificate(f, e$handle, e$certificate);
|
|
for ( i in e$extensions_cache )
|
|
{
|
|
local ext = e$extensions_cache[i];
|
|
|
|
if ( ext is X509::Extension )
|
|
event x509_extension(f, (ext as X509::Extension));
|
|
else if ( ext is X509::BasicConstraints )
|
|
event x509_ext_basic_constraints(f, (ext as X509::BasicConstraints));
|
|
else if ( ext is X509::SubjectAlternativeName )
|
|
event x509_ext_subject_alternative_name(f, (ext as X509::SubjectAlternativeName));
|
|
else if ( ext is X509::SctInfo )
|
|
{
|
|
local s = ( ext as X509::SctInfo);
|
|
event x509_ocsp_ext_signed_certificate_timestamp(f, s$version, s$logid, s$timestamp, s$hash_alg, s$sig_alg, s$signature);
|
|
}
|
|
else
|
|
Reporter::error(fmt("Encountered unknown extension while replaying certificate with fuid %s", f$id));
|
|
}
|
|
}
|
|
|
|
event file_state_remove(f: fa_file) &priority=5
|
|
{
|
|
if ( ! f$info?$x509 )
|
|
return;
|
|
|
|
if ( f$info?$sha256 && f$info$sha256 !in certificate_cache &&
|
|
caching_required_encounters > 0 &&
|
|
f$info$sha256 in certificates_encountered &&
|
|
certificates_encountered[f$info$sha256] >= caching_required_encounters &&
|
|
|certificate_cache| < certificate_cache_max_entries )
|
|
{
|
|
delete certificates_encountered[f$info$sha256];
|
|
certificate_cache[f$info$sha256] = f$info$x509;
|
|
}
|
|
}
|
|
|
|
event file_hash(f: fa_file, kind: string, hash: string)
|
|
{
|
|
if ( ! f?$info || "X509" !in f$info$analyzers || kind != "sha256" )
|
|
return;
|
|
|
|
if ( caching_required_encounters == 0 || hash in certificate_cache )
|
|
return;
|
|
|
|
if ( hash !in certificates_encountered )
|
|
certificates_encountered[hash] = 1;
|
|
else
|
|
certificates_encountered[hash] += 1;
|
|
}
|