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

This switches the DPD logic to always log analyzers that raised a protocol confirmation. The logic is that, once a protocol has been confirmed - and thus there probably is log output - it does not make sense to later remove it from the log. It does make sense to somehow flag it as failed - but that seems like a secondary step.
163 lines
4.6 KiB
Text
163 lines
4.6 KiB
Text
##! Activates port-independent protocol detection and selectively disables
|
|
##! analyzers if protocol violations occur.
|
|
|
|
module DPD;
|
|
|
|
export {
|
|
## Add the DPD 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 DPD logging stream.
|
|
type Info: record {
|
|
## Timestamp for when protocol analysis failed.
|
|
ts: time &log;
|
|
## Connection unique ID.
|
|
uid: string &log;
|
|
## Connection ID containing the 4-tuple which identifies endpoints.
|
|
id: conn_id &log;
|
|
## Transport protocol for the violation.
|
|
proto: transport_proto &log;
|
|
## The analyzer that generated the violation.
|
|
analyzer: string &log;
|
|
## The textual reason for the analysis failure.
|
|
failure_reason: string &log;
|
|
};
|
|
|
|
## Ongoing DPD state tracking information.
|
|
type State: record {
|
|
## Current number of protocol violations seen per analyzer instance.
|
|
violations: table[count] of count;
|
|
};
|
|
|
|
## Number of protocol violations to tolerate before disabling an analyzer.
|
|
option max_violations: table[Analyzer::Tag] of count = table() &default = 5;
|
|
|
|
## Analyzers which you don't want to throw
|
|
option ignore_violations: set[Analyzer::Tag] = set();
|
|
|
|
## Ignore violations which go this many bytes into the connection.
|
|
## Set to 0 to never ignore protocol violations.
|
|
option ignore_violations_after = 10 * 1024;
|
|
}
|
|
|
|
redef record connection += {
|
|
dpd: Info &optional;
|
|
dpd_state: State &optional;
|
|
## The set of services (analyzers) for which Zeek has observed a
|
|
## violation after the same service had previously been confirmed.
|
|
service_violation: set[string] &default=set();
|
|
};
|
|
|
|
event zeek_init() &priority=5
|
|
{
|
|
Log::create_stream(DPD::LOG, [$columns=Info, $path="dpd", $policy=log_policy]);
|
|
}
|
|
|
|
event analyzer_confirmation_info(atype: AllAnalyzers::Tag, info: AnalyzerConfirmationInfo) &priority=10
|
|
{
|
|
if ( ! is_protocol_analyzer(atype) && ! is_packet_analyzer(atype) )
|
|
return;
|
|
|
|
if ( ! info?$c )
|
|
return;
|
|
|
|
local c = info$c;
|
|
local analyzer = Analyzer::name(atype);
|
|
add c$service[analyzer];
|
|
}
|
|
|
|
event analyzer_violation_info(atype: AllAnalyzers::Tag, info: AnalyzerViolationInfo) &priority=10
|
|
{
|
|
if ( ! is_protocol_analyzer(atype) && ! is_packet_analyzer(atype) )
|
|
return;
|
|
|
|
if ( ! info?$c )
|
|
return;
|
|
|
|
local c = info$c;
|
|
local analyzer = Analyzer::name(atype);
|
|
# If the service hasn't been confirmed yet, or already failed,
|
|
# don't generate a log message for the protocol violation.
|
|
if ( analyzer !in c$service || analyzer in c$service_violation )
|
|
return;
|
|
|
|
# No longer delete a service once it has been confirmed.
|
|
# FIXME: track failed analyzers somehow - either by changing how they are logged, or by adding a new column
|
|
# delete c$service[analyzer];
|
|
add c$service_violation[analyzer];
|
|
|
|
local dpd: Info;
|
|
dpd$ts = network_time();
|
|
dpd$uid = c$uid;
|
|
dpd$id = c$id;
|
|
dpd$proto = get_port_transport_proto(c$id$orig_p);
|
|
dpd$analyzer = analyzer;
|
|
|
|
# Encode data into the reason if there's any as done for the old
|
|
# analyzer_violation event, previously.
|
|
local reason = info$reason;
|
|
if ( info?$data )
|
|
{
|
|
local ellipsis = |info$data| > 40 ? "..." : "";
|
|
local data = info$data[0:40];
|
|
reason = fmt("%s [%s%s]", reason, data, ellipsis);
|
|
}
|
|
|
|
dpd$failure_reason = reason;
|
|
c$dpd = dpd;
|
|
}
|
|
|
|
event analyzer_violation_info(atype: AllAnalyzers::Tag, info: AnalyzerViolationInfo ) &priority=5
|
|
{
|
|
if ( ! is_protocol_analyzer(atype) && ! is_packet_analyzer(atype) )
|
|
return;
|
|
|
|
if ( ! info?$c || ! info?$aid )
|
|
return;
|
|
|
|
if ( atype in ignore_violations )
|
|
return;
|
|
|
|
local c = info$c;
|
|
local aid = info$aid;
|
|
local size = c$orig$size + c$resp$size;
|
|
if ( ignore_violations_after > 0 && size > ignore_violations_after )
|
|
return;
|
|
|
|
if ( ! c?$dpd_state )
|
|
{
|
|
local s: State;
|
|
c$dpd_state = s;
|
|
}
|
|
|
|
if ( aid in c$dpd_state$violations )
|
|
++c$dpd_state$violations[aid];
|
|
else
|
|
c$dpd_state$violations[aid] = 1;
|
|
|
|
if ( c?$dpd || c$dpd_state$violations[aid] > max_violations[atype] )
|
|
{
|
|
# Disable an analyzer we've previously confirmed, but is now in
|
|
# violation, or else any analyzer in excess of the max allowed
|
|
# violations, regardless of whether it was previously confirmed.
|
|
disable_analyzer(c$id, aid, F);
|
|
}
|
|
}
|
|
|
|
event analyzer_violation_info(atype: AllAnalyzers::Tag, info: AnalyzerViolationInfo ) &priority=-5
|
|
{
|
|
if ( ! is_protocol_analyzer(atype) && ! is_packet_analyzer(atype) )
|
|
return;
|
|
|
|
if ( ! info?$c )
|
|
return;
|
|
|
|
if ( info$c?$dpd )
|
|
{
|
|
Log::write(DPD::LOG, info$c$dpd);
|
|
delete info$c$dpd;
|
|
}
|
|
}
|