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

This commit revamps the handling of analyzer violations that happen
before an analyzer confirms the protocol.
The current state is that an analyzer is disabled after 5 violations, if
it has not been confirmed. If it has been confirmed, it is disabled
after a single violation.
The reason for this is a historic mistake. In Zeek up to versions 1.5,
analyzers were unconditianally removed when they raised the first
protocol violation.
When this script was ported to the new layout for Zeek 2.0 in
b4b990cfb5
, a logic error was introduced
that caused analyzers to no longer be disabled if they were not
confirmed.
This was the state for ~8 years, till the DPD::max_violations options
was added, which instates the current approach of disabling unconfirmed
analyzers after 5 violations. Sadly, there is not much discussion about
this change - from my hazy memory, I think this was discovered during
performance tests and the new behavior was added without checking into
the history of previous changes.
This commit reinstates the originally intended behavior of DPD. When an
analyzer that has not been confirmed raises a protocol violation, it is
immediately removed from the connection. This also makes a lot of sense
- this allows the analyzer to be in a "tasting" phase at the beginning
of the connection, and to error out quickly once it realizes that it was
attached to a connection not containing the desired protocol.
This change also removes the DPD::max_violations option, as it no longer
serves any purpose after this change. (In practice, the option remains
with an &deprecated warning, but it is no longer used for anything).
There are relatively minimal test-baseline changes due to this; they are
mostly triggered by the removal of the data structure and by less
analyzer errors being thrown, as unconfirmed analyzers are disabled
after the first error.
218 lines
6.3 KiB
Text
218 lines
6.3 KiB
Text
##! Logging analyzer confirmations and violations into analyzer.log
|
|
|
|
@load base/frameworks/config
|
|
@load base/frameworks/logging
|
|
|
|
@load ./main
|
|
|
|
module Analyzer::Logging;
|
|
|
|
export {
|
|
## Add the analyzer logging stream identifier.
|
|
redef enum Log::ID += { LOG };
|
|
|
|
## A default logging policy hook for the stream.
|
|
global log_policy: Log::PolicyHook;
|
|
|
|
## The record type defining the columns to log in the analyzer logging stream.
|
|
type Info: record {
|
|
## Timestamp of confirmation or violation.
|
|
ts: time &log;
|
|
## What caused this log entry to be produced. This can
|
|
## currently be "violation" or "confirmation".
|
|
cause: string &log;
|
|
## The kind of analyzer involved. Currently "packet", "file"
|
|
## or "protocol".
|
|
analyzer_kind: string &log;
|
|
## The name of the analyzer as produced by :zeek:see:`Analyzer::name`
|
|
## for the analyzer's tag.
|
|
analyzer_name: string &log;
|
|
## Connection UID if available.
|
|
uid: string &log &optional;
|
|
## File UID if available.
|
|
fuid: string &log &optional;
|
|
## Connection identifier if available
|
|
id: conn_id &log &optional;
|
|
|
|
## Failure or violation reason, if available.
|
|
failure_reason: string &log &optional;
|
|
|
|
## Data causing failure or violation if available. Truncated
|
|
## to :zeek:see:`Analyzer::Logging::failure_data_max_size`.
|
|
failure_data: string &log &optional;
|
|
};
|
|
|
|
## Enable logging of analyzer violations and optionally confirmations
|
|
## when :zeek:see:`Analyzer::Logging::include_confirmations` is set.
|
|
option enable = T;
|
|
|
|
## Enable analyzer_confirmation. They are usually less interesting
|
|
## outside of development of analyzers or troubleshooting scenarios.
|
|
## Setting this option may also generated multiple log entries per
|
|
## connection, minimally one for each conn.log entry with a populated
|
|
## service field.
|
|
option include_confirmations = F;
|
|
|
|
## Enable tracking of analyzers getting disabled. This is mostly
|
|
## interesting for troubleshooting of analyzers in DPD scenarios.
|
|
## Setting this option may also generated multiple log entries per
|
|
## connection.
|
|
option include_disabling = F;
|
|
|
|
## If a violation contains information about the data causing it,
|
|
## include at most this many bytes of it in the log.
|
|
option failure_data_max_size = 40;
|
|
|
|
## Set of analyzers for which to not log confirmations or violations.
|
|
option ignore_analyzers: set[AllAnalyzers::Tag] = set();
|
|
}
|
|
|
|
|
|
event zeek_init() &priority=5
|
|
{
|
|
Log::create_stream(LOG, [$columns=Info, $path="analyzer", $policy=log_policy,
|
|
$event_groups=set("Analyzer::Logging")]);
|
|
|
|
local enable_handler = function(id: string, new_value: bool): bool {
|
|
if ( new_value )
|
|
Log::enable_stream(LOG);
|
|
else
|
|
Log::disable_stream(LOG);
|
|
|
|
return new_value;
|
|
};
|
|
Option::set_change_handler("Analyzer::Logging::enable", enable_handler);
|
|
|
|
local include_confirmations_handler = function(id: string, new_value: bool): bool {
|
|
if ( new_value )
|
|
enable_event_group("Analyzer::Logging::include_confirmations");
|
|
else
|
|
disable_event_group("Analyzer::Logging::include_confirmations");
|
|
|
|
return new_value;
|
|
};
|
|
Option::set_change_handler("Analyzer::Logging::include_confirmations",
|
|
include_confirmations_handler);
|
|
|
|
local include_disabling_handler = function(id: string, new_value: bool): bool {
|
|
if ( new_value )
|
|
enable_event_group("Analyzer::Logging::include_disabling");
|
|
else
|
|
disable_event_group("Analyzer::Logging::include_disabling");
|
|
|
|
return new_value;
|
|
};
|
|
Option::set_change_handler("Analyzer::Logging::include_disabling",
|
|
include_disabling_handler);
|
|
|
|
# Call the handlers directly with the current values to avoid config
|
|
# framework interactions like creating entries in config.log.
|
|
enable_handler("Analyzer::Logging::enable", Analyzer::Logging::enable);
|
|
include_confirmations_handler("Analyzer::Logging::include_confirmations",
|
|
Analyzer::Logging::include_confirmations);
|
|
include_disabling_handler("Analyzer::Logging::include_disabling",
|
|
Analyzer::Logging::include_disabling);
|
|
|
|
}
|
|
|
|
function analyzer_kind(atype: AllAnalyzers::Tag): string
|
|
{
|
|
if ( is_protocol_analyzer(atype) )
|
|
return "protocol";
|
|
else if ( is_packet_analyzer(atype) )
|
|
return "packet";
|
|
else if ( is_file_analyzer(atype) )
|
|
return "file";
|
|
|
|
Reporter::warning(fmt("Unknown kind of analyzer %s", atype));
|
|
return "unknown";
|
|
}
|
|
|
|
function populate_from_conn(rec: Info, c: connection)
|
|
{
|
|
rec$id = c$id;
|
|
rec$uid = c$uid;
|
|
}
|
|
|
|
function populate_from_file(rec: Info, f: fa_file)
|
|
{
|
|
rec$fuid = f$id;
|
|
# If the confirmation didn't have a connection, but the
|
|
# fa_file object has exactly one, use it.
|
|
if ( ! rec?$uid && f?$conns && |f$conns| == 1 )
|
|
{
|
|
for ( _, c in f$conns )
|
|
{
|
|
rec$id = c$id;
|
|
rec$uid = c$uid;
|
|
}
|
|
}
|
|
}
|
|
|
|
event analyzer_confirmation_info(atype: AllAnalyzers::Tag, info: AnalyzerConfirmationInfo) &group="Analyzer::Logging::include_confirmations"
|
|
{
|
|
if ( atype in ignore_analyzers )
|
|
return;
|
|
|
|
local rec = Info(
|
|
$ts=network_time(),
|
|
$cause="confirmation",
|
|
$analyzer_kind=analyzer_kind(atype),
|
|
$analyzer_name=Analyzer::name(atype),
|
|
);
|
|
|
|
if ( info?$c )
|
|
populate_from_conn(rec, info$c);
|
|
|
|
if ( info?$f )
|
|
populate_from_file(rec, info$f);
|
|
|
|
Log::write(LOG, rec);
|
|
}
|
|
|
|
event analyzer_violation_info(atype: AllAnalyzers::Tag, info: AnalyzerViolationInfo) &priority=6
|
|
{
|
|
if ( atype in ignore_analyzers )
|
|
return;
|
|
|
|
local rec = Info(
|
|
$ts=network_time(),
|
|
$cause="violation",
|
|
$analyzer_kind=analyzer_kind(atype),
|
|
$analyzer_name=Analyzer::name(atype),
|
|
$failure_reason=info$reason,
|
|
);
|
|
|
|
if ( info?$c )
|
|
populate_from_conn(rec, info$c);
|
|
|
|
if ( info?$f )
|
|
populate_from_file(rec, info$f);
|
|
|
|
if ( info?$data )
|
|
{
|
|
if ( failure_data_max_size > 0 )
|
|
rec$failure_data = info$data[0:failure_data_max_size];
|
|
else
|
|
rec$failure_data = info$data;
|
|
}
|
|
|
|
Log::write(LOG, rec);
|
|
}
|
|
|
|
hook Analyzer::disabling_analyzer(c: connection, atype: AllAnalyzers::Tag, aid: count) &priority=-1000 &group="Analyzer::Logging::include_disabling"
|
|
{
|
|
if ( atype in ignore_analyzers )
|
|
return;
|
|
|
|
local rec = Info(
|
|
$ts=network_time(),
|
|
$cause="disabled",
|
|
$analyzer_kind=analyzer_kind(atype),
|
|
$analyzer_name=Analyzer::name(atype),
|
|
);
|
|
|
|
populate_from_conn(rec, c);
|
|
|
|
Log::write(LOG, rec);
|
|
}
|