mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
Introduce global disabling_analyzer() hook to veto disable_analyzer()
This hook can be used to coordinate disabling an analyzer for a given connection. The contract is simple: Any script can veto a disable_analyzer() call by breaking from this hook. The decision is local to the script taking into account any state attached to the connection object or script specific state stored elsewhere. A script breaking from the hook takes over the responsibility to call disable_analyzer() at a later point when it finds the condition due to which it vetoed fulfilled (which may be never). Signature: disabling_analyzer: hook(c: connection, atype: AllAnalyzers::Tag, aid: count); Example use-cases are keeping the SSL analyzer enabled for finger-printing until a certain amount of bytes or packets have been transferred or similarly the connection duration exceed a certain threshold. Other example use-cases might be keeping analyzers for SSH, RDP or SSL enabled for connections from specific subnets. It's a bit quirky as it makes disable_analyzer() a maybe operation. While log policy hooks and/or the notice hook have similar semantics, they are not as stateful. It still seems like a quite powerful primitive. The disable_analyzer() call in dpd/main.zeek may motivate the addition of a force flag as a follow-up for situations where the caller "knows better" or absolutely wants to override. Closes #1678 #1593.
This commit is contained in:
parent
02985b9966
commit
abb7f9a509
6 changed files with 149 additions and 0 deletions
|
@ -584,6 +584,22 @@ type fa_metadata: record {
|
||||||
inferred: bool &default=T;
|
inferred: bool &default=T;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## A hook taking a connection, analyzer tag and analyzer id that can be
|
||||||
|
## used to veto disabling analyzers. This hook is invoked synchronously
|
||||||
|
## during a :zeek:see:`disable_analyzer` call.
|
||||||
|
##
|
||||||
|
## Scripts implementing this hook should have other logic that will eventually
|
||||||
|
## disable the analyzer for the given connection. That is, if a script vetoes
|
||||||
|
## disabling an analyzer, it takes responsibility for a later call to
|
||||||
|
## :zeek:see:`disable_analyzer`, which may be never.
|
||||||
|
##
|
||||||
|
## c: The connection
|
||||||
|
##
|
||||||
|
## atype: The type / tag of the analyzer being disabled.
|
||||||
|
##
|
||||||
|
## aid: The analyzer ID.
|
||||||
|
type disabling_analyzer: hook(c: connection, atype: AllAnalyzers::Tag, aid: count);
|
||||||
|
|
||||||
## Fields of a SYN packet.
|
## Fields of a SYN packet.
|
||||||
##
|
##
|
||||||
## .. zeek:see:: connection_SYN_packet
|
## .. zeek:see:: connection_SYN_packet
|
||||||
|
|
|
@ -4590,6 +4590,15 @@ function disable_analyzer%(cid: conn_id, aid: count, err_if_no_conn: bool &defau
|
||||||
return zeek::val_mgr->False();
|
return zeek::val_mgr->False();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto disabling_analyzer_hook = id::find_func("disabling_analyzer");
|
||||||
|
if ( disabling_analyzer_hook )
|
||||||
|
{
|
||||||
|
auto hook_rval = disabling_analyzer_hook->Invoke(c->GetVal(), a->GetAnalyzerTag().AsVal(),
|
||||||
|
zeek::val_mgr->Count(aid));
|
||||||
|
if ( hook_rval && ! hook_rval->AsBool() )
|
||||||
|
return zeek::val_mgr->False();
|
||||||
|
}
|
||||||
|
|
||||||
if ( prevent )
|
if ( prevent )
|
||||||
a->Parent()->PreventChildren(a->GetAnalyzerTag());
|
a->Parent()->PreventChildren(a->GetAnalyzerTag());
|
||||||
|
|
||||||
|
|
16
testing/btest/Baseline/bifs.disable_analyzer-hook/out
Normal file
16
testing/btest/Baseline/bifs.disable_analyzer-hook/out
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
proto confirm, Analyzer::ANALYZER_HTTP
|
||||||
|
http_request, GET, /style/enhanced.css
|
||||||
|
preventing disable_analyzer, [orig_h=192.168.1.104, orig_p=1673/tcp, resp_h=63.245.209.11, resp_p=80/tcp], Analyzer::ANALYZER_HTTP, 3, 1
|
||||||
|
F
|
||||||
|
http_reply, 200
|
||||||
|
http_request, GET, /script/urchin.js
|
||||||
|
preventing disable_analyzer, [orig_h=192.168.1.104, orig_p=1673/tcp, resp_h=63.245.209.11, resp_p=80/tcp], Analyzer::ANALYZER_HTTP, 3, 3
|
||||||
|
F
|
||||||
|
http_reply, 200
|
||||||
|
http_request, GET, /images/template/screen/bullet_utility.png
|
||||||
|
allowing disable_analyzer, [orig_h=192.168.1.104, orig_p=1673/tcp, resp_h=63.245.209.11, resp_p=80/tcp], Analyzer::ANALYZER_HTTP, 3, 5
|
||||||
|
T
|
||||||
|
total http messages, {
|
||||||
|
[[orig_h=192.168.1.104, orig_p=1673/tcp, resp_h=63.245.209.11, resp_p=80/tcp]] = 5
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
analyzer_confirmation, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp], Analyzer::ANALYZER_SSL, 3
|
||||||
|
encrypted_data, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp], T, 22, 32, 1
|
||||||
|
established, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp]
|
||||||
|
disabling_analyzer, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp], Analyzer::ANALYZER_SSL, 3
|
||||||
|
preventing disabling_analyzer, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp], Analyzer::ANALYZER_SSL, 3
|
||||||
|
encrypted_data, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp], F, 22, 32, 2
|
||||||
|
encrypted_data, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp], T, 23, 31, 3
|
||||||
|
encrypted_data, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp], T, 23, 17, 4
|
||||||
|
disabling_analyzer, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp], Analyzer::ANALYZER_SSL, 3
|
||||||
|
allowing disabling_analyzer, [orig_h=10.0.0.80, orig_p=56637/tcp, resp_h=68.233.76.12, resp_p=443/tcp], Analyzer::ANALYZER_SSL, 3
|
45
testing/btest/bifs/disable_analyzer-hook.zeek
Normal file
45
testing/btest/bifs/disable_analyzer-hook.zeek
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# @TEST-DOC: Disable the analyzer if 5 or more messages have been seen on a connection.
|
||||||
|
# @TEST-EXEC: zeek -b -r $TRACES/http/pipelined-requests.trace %INPUT >out
|
||||||
|
# @TEST-EXEC: btest-diff out
|
||||||
|
|
||||||
|
@load base/protocols/http
|
||||||
|
|
||||||
|
global msg_count: table[conn_id] of count &default=0;
|
||||||
|
|
||||||
|
event analyzer_confirmation(c: connection, atype: AllAnalyzers::Tag, aid: count) &priority=10
|
||||||
|
{
|
||||||
|
if ( atype != Analyzer::ANALYZER_HTTP )
|
||||||
|
return;
|
||||||
|
|
||||||
|
print "proto confirm", atype;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prevent disabling all analyzers.
|
||||||
|
hook disabling_analyzer(c: connection, atype: AllAnalyzers::Tag, aid: count)
|
||||||
|
{
|
||||||
|
if ( msg_count[c$id] < 4 )
|
||||||
|
{
|
||||||
|
print "preventing disable_analyzer", c$id, atype, aid, msg_count[c$id];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "allowing disable_analyzer", c$id, atype, aid, msg_count[c$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string)
|
||||||
|
{
|
||||||
|
++msg_count[c$id];
|
||||||
|
print "http_request", method, original_URI;
|
||||||
|
print disable_analyzer(c$id, current_analyzer(), T, T);
|
||||||
|
}
|
||||||
|
|
||||||
|
event http_reply(c: connection, version: string, code: count, reason: string)
|
||||||
|
{
|
||||||
|
++msg_count[c$id];
|
||||||
|
print "http_reply", code;
|
||||||
|
}
|
||||||
|
|
||||||
|
event zeek_done()
|
||||||
|
{
|
||||||
|
print "total http messages", msg_count;
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
# @TEST-DOC: Implement disabling_analyzer hook to keep the SSL analyzer enabled for a bit longer.
|
||||||
|
# @TEST-EXEC: zeek -b -C -r $TRACES/tls/tls1.2.trace %INPUT
|
||||||
|
# @TEST-EXEC: btest-diff .stdout
|
||||||
|
|
||||||
|
@load base/protocols/ssl
|
||||||
|
|
||||||
|
# This is the default, but make it explicit.
|
||||||
|
redef SSL::disable_analyzer_after_detection = T;
|
||||||
|
|
||||||
|
redef record SSL::Info += {
|
||||||
|
encrypted_data: count &default=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
# After how many ssl_encrypted_data events to disable the analyzer. The
|
||||||
|
# pcap triggers seven, the handshake is over after the first two.
|
||||||
|
global encrypted_data_wanted = 4;
|
||||||
|
|
||||||
|
# Prevent disabling the SSL analyzer for this connection until we've seen encrypted_data_wanted
|
||||||
|
# encrypted data events on it. Our ssl_encrypted_data event handler has the inverse condition.
|
||||||
|
hook disabling_analyzer(c: connection, atype: AllAnalyzers::Tag, aid: count)
|
||||||
|
{
|
||||||
|
print "disabling_analyzer", c$id, atype, aid;
|
||||||
|
if ( atype != Analyzer::ANALYZER_SSL || ! c?$ssl )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( c$ssl$encrypted_data < encrypted_data_wanted )
|
||||||
|
{
|
||||||
|
print "preventing disabling_analyzer", c$id, atype, aid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "allowing disabling_analyzer", c$id, atype, aid;
|
||||||
|
}
|
||||||
|
|
||||||
|
event ssl_established(c: connection)
|
||||||
|
{
|
||||||
|
print "established", c$id;
|
||||||
|
}
|
||||||
|
|
||||||
|
event analyzer_confirmation(c: connection, atype: AllAnalyzers::Tag, aid: count)
|
||||||
|
{
|
||||||
|
print "analyzer_confirmation", c$id, atype, aid;
|
||||||
|
}
|
||||||
|
|
||||||
|
event ssl_encrypted_data(c: connection, is_client: bool, record_version: count, content_type: count, length: count)
|
||||||
|
{
|
||||||
|
++c$ssl$encrypted_data;
|
||||||
|
print "encrypted_data", c$id, is_client, content_type, length, c$ssl$encrypted_data;
|
||||||
|
|
||||||
|
if ( c$ssl?$analyzer_id && c$ssl$encrypted_data >= encrypted_data_wanted )
|
||||||
|
disable_analyzer(c$id, c$ssl$analyzer_id);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue