From 8c814fa88c88702adeda8090f5c0d2537463e8d8 Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Wed, 9 Apr 2025 15:25:01 +0100 Subject: [PATCH] Introduce analyzer-failed.log, as a replacement for dpd.log Analyzer-failed.log is, essentially, the replacement for dpd.log. The name should make more sense, as it does now log analyzer failures. For protocol analyzers specifically, these are failures that lead to the analyzer being disabled. --- .../base/frameworks/analyzer/__load__.zeek | 1 + .../analyzer/analyzer-failed-log.zeek | 88 +++++++++++++++++++ scripts/base/frameworks/analyzer/dpd.zeek | 29 +++--- scripts/base/frameworks/analyzer/main.zeek | 22 +++++ .../analyzer/analyzer-debug-log.zeek | 21 +---- 5 files changed, 130 insertions(+), 31 deletions(-) create mode 100644 scripts/base/frameworks/analyzer/analyzer-failed-log.zeek diff --git a/scripts/base/frameworks/analyzer/__load__.zeek b/scripts/base/frameworks/analyzer/__load__.zeek index e2c42c7a8e..ab0b2416e4 100644 --- a/scripts/base/frameworks/analyzer/__load__.zeek +++ b/scripts/base/frameworks/analyzer/__load__.zeek @@ -1,2 +1,3 @@ @load ./main @load ./dpd +@load ./analyzer-failed-log diff --git a/scripts/base/frameworks/analyzer/analyzer-failed-log.zeek b/scripts/base/frameworks/analyzer/analyzer-failed-log.zeek new file mode 100644 index 0000000000..07550b861c --- /dev/null +++ b/scripts/base/frameworks/analyzer/analyzer-failed-log.zeek @@ -0,0 +1,88 @@ +##! Logging analyzer violations into analyzer-failed.log + +@load base/frameworks/logging +@load ./main + +module Analyzer::Logging; + +export { + ## Add the analyzer logging stream identifier. + redef enum Log::ID += { LOG }; + + ## The record type defining the columns to log in the analyzer-failed logging stream. + type Info: record { + ## Timestamp of the violation. + ts: time &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; + + ## Data causing failure or violation if available. Truncated + ## to :zeek:see:`Analyzer::Logging::failure_data_max_size`. + failure_data: string &log &optional; + }; + + ## 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; + + ## An event that can be handled to access the :zeek:type:`Analyzer::Logging::Info` + ## record as it is sent on to the logging framework. + global log_analyzer_failed: event(rec: Info); + + ## A default logging policy hook for the stream. + global log_policy: Log::PolicyHook; +} + +event zeek_init() &priority=5 + { + Log::create_stream(LOG, [$columns=Info, $path="analyzer-failed", $ev=log_analyzer_failed, $policy=log_policy]); + } + +event analyzer_failed(ts: time, atype: AllAnalyzers::Tag, info: AnalyzerViolationInfo) + { + local rec = Info( + $ts=ts, + $analyzer_kind=Analyzer::kind(atype), + $analyzer_name=Analyzer::name(atype), + $failure_reason=info$reason + ); + + if ( info?$c ) + { + rec$id = info$c$id; + rec$uid = info$c$uid; + } + + if ( info?$f ) + { + rec$fuid = info$f$id; + # If the confirmation didn't have a connection, but the + # fa_file object has exactly one, use it. + if ( ! rec?$uid && info$f?$conns && |info$f$conns| == 1 ) + { + for ( _, c in info$f$conns ) + { + rec$id = c$id; + rec$uid = c$uid; + } + } + } + + if ( info?$data ) + rec$failure_data = info$data; + + Log::write(LOG, rec); + } diff --git a/scripts/base/frameworks/analyzer/dpd.zeek b/scripts/base/frameworks/analyzer/dpd.zeek index 0f810b6151..36cba619fa 100644 --- a/scripts/base/frameworks/analyzer/dpd.zeek +++ b/scripts/base/frameworks/analyzer/dpd.zeek @@ -1,13 +1,15 @@ ##! Disables analyzers if protocol violations occur, and add service information ##! to connection log. +@load ./main + module DPD; export { ## Deprecated, please see https://github.com/zeek/zeek/pull/4200 for details option max_violations: table[Analyzer::Tag] of count = table() &deprecated="Remove in v8.1: This has become non-functional in Zeek 7.2, see PR #4200" &default = 5; - ## Analyzers which you don't want to throw + ## Analyzers which you don't want to remove on violations. option ignore_violations: set[Analyzer::Tag] = set(); ## Ignore violations which go this many bytes into the connection. @@ -43,7 +45,7 @@ event analyzer_confirmation_info(atype: AllAnalyzers::Tag, info: AnalyzerConfirm } ## Remove failed analyzers from service field and add them to c$service_violation -event analyzer_violation_info(atype: AllAnalyzers::Tag, info: AnalyzerViolationInfo) &priority=10 +event analyzer_failed(ts: time, atype: AllAnalyzers::Tag, info: AnalyzerViolationInfo) { if ( ! is_protocol_analyzer(atype) && ! is_packet_analyzer(atype) ) return; @@ -64,13 +66,18 @@ event analyzer_violation_info(atype: AllAnalyzers::Tag, info: AnalyzerViolationI # if statement is separate, to allow repeated removal of service, in case there are several # confirmation and violation events - if ( analyzer in c$service_violation ) - return; + if ( analyzer !in c$service_violation ) + add c$service_violation[analyzer]; - add c$service_violation[analyzer]; + # add "-service" to the list of services on removal due to violation, if analyzer was confirmed before + if ( track_removed_services_in_connection && Analyzer::name(atype) in c$service ) + { + local rname = cat("-", Analyzer::name(atype)); + if ( rname !in c$service ) + add c$service[rname]; + } } - event analyzer_violation_info(atype: AllAnalyzers::Tag, info: AnalyzerViolationInfo ) &priority=5 { if ( ! is_protocol_analyzer(atype) && ! is_packet_analyzer(atype) ) @@ -90,13 +97,7 @@ event analyzer_violation_info(atype: AllAnalyzers::Tag, info: AnalyzerViolationI local disabled = disable_analyzer(c$id, aid, F); - # add "-service" to the list of services on removal due to violation, if analyzer was confirmed before - if ( track_removed_services_in_connection && disabled && Analyzer::name(atype) in c$service ) - { - local rname = cat("-", Analyzer::name(atype)); - if ( rname !in c$service ) - add c$service[rname]; - } - + # If no one objected to the removal, send failed event + event analyzer_failed(network_time(), atype, info); } diff --git a/scripts/base/frameworks/analyzer/main.zeek b/scripts/base/frameworks/analyzer/main.zeek index dd911c2d3c..e32c7e040c 100644 --- a/scripts/base/frameworks/analyzer/main.zeek +++ b/scripts/base/frameworks/analyzer/main.zeek @@ -88,6 +88,15 @@ export { ## Returns: The analyzer name corresponding to the tag. global name: function(tag: Analyzer::Tag) : string; + ## Translates an analyzer type to a string with the analyzer's type. + ## + ## Possible values are "protocol", "packet", "file", or "unknown". + ## + ## tag: The analyzer tag. + ## + ## Returns: The analyzer kind corresponding to the tag. + global kind: function(tag: Analyzer::Tag) : string; + ## Check whether the given analyzer name exists. ## ## This can be used before calling :zeek:see:`Analyzer::get_tag` to @@ -246,6 +255,19 @@ function name(atype: AllAnalyzers::Tag) : string return __name(atype); } +function 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 has_tag(name: string): bool { return __has_tag(name); diff --git a/scripts/policy/frameworks/analyzer/analyzer-debug-log.zeek b/scripts/policy/frameworks/analyzer/analyzer-debug-log.zeek index e0af2dc65e..153086def8 100644 --- a/scripts/policy/frameworks/analyzer/analyzer-debug-log.zeek +++ b/scripts/policy/frameworks/analyzer/analyzer-debug-log.zeek @@ -1,4 +1,4 @@ -##! Logging analyzer confirmations and violations into analyzer-debug.log +#! Logging analyzer confirmations and violations into analyzer-debug.log @load base/frameworks/config @load base/frameworks/logging @@ -117,19 +117,6 @@ event zeek_init() &priority=5 } -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; @@ -159,7 +146,7 @@ event analyzer_confirmation_info(atype: AllAnalyzers::Tag, info: AnalyzerConfirm local rec = Info( $ts=network_time(), $cause="confirmation", - $analyzer_kind=analyzer_kind(atype), + $analyzer_kind=Analyzer::kind(atype), $analyzer_name=Analyzer::name(atype), ); @@ -180,7 +167,7 @@ event analyzer_violation_info(atype: AllAnalyzers::Tag, info: AnalyzerViolationI local rec = Info( $ts=network_time(), $cause="violation", - $analyzer_kind=analyzer_kind(atype), + $analyzer_kind=Analyzer::kind(atype), $analyzer_name=Analyzer::name(atype), $failure_reason=info$reason, ); @@ -210,7 +197,7 @@ hook Analyzer::disabling_analyzer(c: connection, atype: AllAnalyzers::Tag, aid: local rec = Info( $ts=network_time(), $cause="disabled", - $analyzer_kind=analyzer_kind(atype), + $analyzer_kind=Analyzer::kind(atype), $analyzer_name=Analyzer::name(atype), );