mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
201 lines
5.5 KiB
Text
201 lines
5.5 KiB
Text
##! Log information about certificates while attempting to avoid duplicate
|
|
##! logging.
|
|
|
|
@load base/utils/directions-and-hosts
|
|
@load base/protocols/ssl
|
|
@load base/files/x509
|
|
@load base/frameworks/cluster
|
|
|
|
module Known;
|
|
|
|
export {
|
|
redef enum Log::ID += { CERTS_LOG };
|
|
|
|
global log_policy_certs: Log::PolicyHook;
|
|
|
|
type CertsInfo: record {
|
|
## The timestamp when the certificate was detected.
|
|
ts: time &log;
|
|
## The address that offered the certificate.
|
|
host: addr &log;
|
|
## If the certificate was handed out by a server, this is the
|
|
## port that the server was listening on.
|
|
port_num: port &log &optional;
|
|
## Certificate subject.
|
|
subject: string &log &optional;
|
|
## Certificate issuer subject.
|
|
issuer_subject: string &log &optional;
|
|
## Serial number for the certificate.
|
|
serial: string &log &optional;
|
|
};
|
|
|
|
## The certificates whose existence should be logged and tracked.
|
|
## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS.
|
|
option cert_tracking = LOCAL_HOSTS;
|
|
|
|
## Toggles between different implementations of this script.
|
|
## When true, use a Broker data store, else use a regular Zeek set
|
|
## with keys uniformly distributed over proxy nodes in cluster
|
|
## operation.
|
|
const use_cert_store = T &redef;
|
|
|
|
type AddrCertHashPair: record {
|
|
host: addr;
|
|
hash: string;
|
|
};
|
|
|
|
## Holds the set of all known certificates. Keys in the store are of
|
|
## type :zeek:type:`Known::AddrCertHashPair` and their associated value is
|
|
## always the boolean value of "true".
|
|
global cert_store: Cluster::StoreInfo;
|
|
|
|
## The Broker topic name to use for :zeek:see:`Known::cert_store`.
|
|
const cert_store_name = "zeek/known/certs" &redef;
|
|
|
|
## The expiry interval of new entries in :zeek:see:`Known::cert_store`.
|
|
## This also changes the interval at which certs get logged.
|
|
option cert_store_expiry = 1day;
|
|
|
|
## The timeout interval to use for operations against
|
|
## :zeek:see:`Known::cert_store`.
|
|
option cert_store_timeout = 15sec;
|
|
|
|
## The set of all known certificates to store for preventing duplicate
|
|
## logging. It can also be used from other scripts to
|
|
## inspect if a certificate has been seen in use. The string value
|
|
## in the set is for storing the DER formatted certificate' SHA1 hash.
|
|
##
|
|
## In cluster operation, this set is uniformly distributed across
|
|
## proxy nodes.
|
|
global certs: set[addr, string] &create_expire=1day &redef;
|
|
|
|
## Event that can be handled to access the loggable record as it is sent
|
|
## on to the logging framework.
|
|
global log_known_certs: event(rec: CertsInfo);
|
|
}
|
|
|
|
event zeek_init()
|
|
{
|
|
if ( ! Known::use_cert_store )
|
|
return;
|
|
|
|
Known::cert_store = Cluster::create_store(Known::cert_store_name);
|
|
}
|
|
|
|
event Known::cert_found(info: CertsInfo, hash: string)
|
|
{
|
|
if ( ! Known::use_cert_store )
|
|
return;
|
|
|
|
local key = AddrCertHashPair($host = info$host, $hash = hash);
|
|
|
|
when [info, key] ( local r = Broker::put_unique(Known::cert_store$store, key,
|
|
T, Known::cert_store_expiry) )
|
|
{
|
|
if ( r$status == Broker::SUCCESS )
|
|
{
|
|
if ( r$result as bool )
|
|
Log::write(Known::CERTS_LOG, info);
|
|
}
|
|
else
|
|
Reporter::error(fmt("%s: data store put_unique failure",
|
|
Known::cert_store_name));
|
|
}
|
|
timeout Known::cert_store_timeout
|
|
{
|
|
# Can't really tell if master store ended up inserting a key.
|
|
Log::write(Known::CERTS_LOG, info);
|
|
}
|
|
}
|
|
|
|
event known_cert_add(info: CertsInfo, hash: string)
|
|
{
|
|
if ( Known::use_cert_store )
|
|
return;
|
|
|
|
if ( [info$host, hash] in Known::certs )
|
|
return;
|
|
|
|
add Known::certs[info$host, hash];
|
|
|
|
@if ( ! Cluster::is_enabled() ||
|
|
Cluster::local_node_type() == Cluster::PROXY )
|
|
Log::write(Known::CERTS_LOG, info);
|
|
@endif
|
|
}
|
|
|
|
event Known::cert_found(info: CertsInfo, hash: string)
|
|
{
|
|
if ( Known::use_cert_store )
|
|
return;
|
|
|
|
if ( [info$host, hash] in Known::certs )
|
|
return;
|
|
|
|
local key = cat(info$host, hash);
|
|
Cluster::publish_hrw(Cluster::proxy_pool, key, known_cert_add, info, hash);
|
|
event known_cert_add(info, hash);
|
|
}
|
|
|
|
event Cluster::node_up(name: string, id: string)
|
|
{
|
|
if ( Known::use_cert_store )
|
|
return;
|
|
|
|
if ( Cluster::local_node_type() != Cluster::WORKER )
|
|
return;
|
|
|
|
# Drop local suppression cache on workers to force HRW key repartitioning.
|
|
Known::certs = table();
|
|
}
|
|
|
|
event Cluster::node_down(name: string, id: string)
|
|
{
|
|
if ( Known::use_cert_store )
|
|
return;
|
|
|
|
if ( Cluster::local_node_type() != Cluster::WORKER )
|
|
return;
|
|
|
|
# Drop local suppression cache on workers to force HRW key repartitioning.
|
|
Known::certs = table();
|
|
}
|
|
|
|
event ssl_established(c: connection) &priority=3
|
|
{
|
|
if ( ! c$ssl?$cert_chain )
|
|
return;
|
|
|
|
if ( |c$ssl$cert_chain| < 1 )
|
|
return;
|
|
|
|
if ( ! c$ssl$cert_chain[0]?$x509 )
|
|
return;
|
|
|
|
local fuid = c$ssl$cert_chain[0]$fuid;
|
|
|
|
if ( ! c$ssl$cert_chain[0]?$sha1 )
|
|
{
|
|
Reporter::error(fmt("Certificate with fuid %s did not contain sha1 hash when checking for known certs. Aborting",
|
|
fuid));
|
|
return;
|
|
}
|
|
|
|
local host = c$id$resp_h;
|
|
|
|
if ( ! addr_matches_host(host, cert_tracking) )
|
|
return;
|
|
|
|
local hash = c$ssl$cert_chain[0]$sha1;
|
|
local cert = c$ssl$cert_chain[0]$x509$certificate;
|
|
local info = CertsInfo($ts = network_time(), $host = host,
|
|
$port_num = c$id$resp_p, $subject = cert$subject,
|
|
$issuer_subject = cert$issuer,
|
|
$serial = cert$serial);
|
|
event Known::cert_found(info, hash);
|
|
}
|
|
|
|
event zeek_init() &priority=5
|
|
{
|
|
Log::create_stream(Known::CERTS_LOG, [$columns=CertsInfo, $ev=log_known_certs, $path="known_certs", $policy=log_policy_certs]);
|
|
}
|