From ee645dfce9f0a160880eda2130f58fd25f1e1412 Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Wed, 3 Jun 2015 11:05:04 -0700 Subject: [PATCH] Acld implementation for Pacf - Bro side. Still needs a few small fixes to deal with the fact that acld does not always accept subnets. --- .../base/frameworks/pacf/plugins/__load__.bro | 1 + scripts/base/frameworks/pacf/plugins/acld.bro | 215 ++++++++++++++++++ .../recv.recv.out | 7 + .../send.send.out | 7 + .../scripts/base/frameworks/pacf/acld.bro | 111 +++++++++ 5 files changed, 341 insertions(+) create mode 100644 scripts/base/frameworks/pacf/plugins/acld.bro create mode 100644 testing/btest/Baseline/scripts.base.frameworks.pacf.acld/recv.recv.out create mode 100644 testing/btest/Baseline/scripts.base.frameworks.pacf.acld/send.send.out create mode 100644 testing/btest/scripts/base/frameworks/pacf/acld.bro diff --git a/scripts/base/frameworks/pacf/plugins/__load__.bro b/scripts/base/frameworks/pacf/plugins/__load__.bro index 11e8723478..255cee5f69 100644 --- a/scripts/base/frameworks/pacf/plugins/__load__.bro +++ b/scripts/base/frameworks/pacf/plugins/__load__.bro @@ -2,3 +2,4 @@ @load ./openflow @load ./packetfilter @load ./broker +@load ./acld diff --git a/scripts/base/frameworks/pacf/plugins/acld.bro b/scripts/base/frameworks/pacf/plugins/acld.bro new file mode 100644 index 0000000000..25218ce470 --- /dev/null +++ b/scripts/base/frameworks/pacf/plugins/acld.bro @@ -0,0 +1,215 @@ +# Acld plugin for the pacf framework. + +module Pacf; + +@load ../plugin +@load base/frameworks/broker + +export { + type AclRule : record { + command: string; + cookie: count; + arg: string; + comment: string &optional; + }; + + type AcldConfig: record { + ## The acld topic used to send events to + acld_topic: string; + ## Broker host to connect to + acld_host: addr; + ## Broker port to connect to + acld_port: port; + ## Function that can decide weather to accept add request + add_pred: function(p: PluginState, r: Rule, ar: AclRule): bool &optional &weaken; + }; + + ## Instantiates the acld plugin. + global create_acld: function(config: AcldConfig) : PluginState; + + redef record PluginState += { + acld_config: AcldConfig &optional; + ## The ID of this acld instance - for the mapping to PluginStates + acld_id: count &optional; + }; + + global acld_add_rule: event(id: count, r: Rule, ar: AclRule); + global acld_remove_rule: event(id: count, r: Rule, ar: AclRule); + + global acld_rule_added: event(id: count, r: Rule, msg: string); + global acld_rule_removed: event(id: count, r: Rule, msg: string); + global acld_rule_error: event(id: count, r: Rule, msg: string); +} + +global pacf_acld_topics: set[string] = set(); +global pacf_acld_id: table[count] of PluginState = table(); +global pacf_acld_current_id: count = 0; + +const acld_add_to_remove: table[string] of string = { + ["drop"] = "restore", + ["whitelist"] = "remwhitelist", + ["blockhosthost"] = "restorehosthost", + ["droptcpport"] = "restoretcpport", + ["dropudpport"] = "restoreudpport", + ["droptcpdsthostport"] ="restoretcpdsthostport", + ["dropudpdsthostport"] ="restoreudpdsthostport", + ["permittcpdsthostport"] ="unpermittcpdsthostport", + ["permitudpdsthostport"] ="unpermitudpdsthostport", + ["nullzero "] ="nonullzero" +}; + +event Pacf::acld_rule_added(id: count, r: Rule, msg: string) + { + if ( id !in pacf_acld_id ) + { + Reporter::error(fmt("Pacf acld plugin with id %d not found, aborting", id)); + return; + } + + local p = pacf_acld_id[id]; + + event Pacf::rule_added(r, p, msg); + } + +event Pacf::acld_rule_removed(id: count, r: Rule, msg: string) + { + if ( id !in pacf_acld_id ) + { + Reporter::error(fmt("Pacf acld plugin with id %d not found, aborting", id)); + return; + } + + local p = pacf_acld_id[id]; + + event Pacf::rule_removed(r, p, msg); + } + +event Pacf::acld_rule_error(id: count, r: Rule, msg: string) + { + if ( id !in pacf_acld_id ) + { + Reporter::error(fmt("Pacf acld plugin with id %d not found, aborting", id)); + return; + } + + local p = pacf_acld_id[id]; + + event Pacf::rule_error(r, p, msg); + } + +function acld_name(p: PluginState) : string + { + return fmt("PACF acld plugin - using broker topic %s", p$acld_config$acld_topic); + } + +function rule_to_acl_rule(r: Rule) : AclRule + { + local e = r$entity; + + local command: string = ""; + local arg: string = ""; + + if ( e$ty == ADDRESS ) + { + if ( r$ty == DROP ) + command = "drop"; + else if ( r$ty == WHITELIST ) + command = "whitelist"; + arg = cat(e$ip); + } + else if ( e$ty == FLOW ) + { + local f = e$flow; + if ( ( ! f?$src_h ) && ( ! f?$src_p ) && f?$dst_h && f?$dst_p && ( ! f?$src_m ) && ( ! f?$dst_m ) ) + { + # fixme - check if address is not a subnet + if ( is_tcp_port(f$dst_p) && r$ty == DROP ) + command = "droptcpdsthostport"; + else if ( is_tcp_port(f$dst_p) && r$ty == WHITELIST ) + command = "permittcpdsthostport"; + else if ( is_udp_port(f$dst_p) && r$ty == DROP) + command = "dropucpdsthostport"; + else if ( is_udp_port(f$dst_p) && r$ty == WHITELIST) + command = "permitucpdsthostport"; + + arg = fmt("%s %d", f$dst_h, f$dst_p); + } + else if ( f?$src_h && ( ! f?$src_p ) && f?$dst_h && ( ! f?$dst_p ) && ( ! f?$src_m ) && ( ! f?$dst_m ) ) + { + if ( r$ty == DROP ) + command = "blockhosthost"; + arg = fmt("%s %s", f$src_h, f$dst_h); + } + else if ( ( ! f?$src_h ) && ( ! f?$src_p ) && ( ! f?$dst_h ) && f?$dst_p && ( ! f?$src_m ) && ( ! f?$dst_m ) ) + { + if ( is_tcp_port(f$dst_p) && r$ty == DROP ) + command = "droptcpport"; + else if ( is_udp_port(f$dst_p) && r$ty == DROP ) + command = "dropudpport"; + arg = fmt("%d", f$dst_p); + } + } + + local ar = AclRule($command=command, $cookie=r$cid, $arg=arg); + if ( r?$location ) + ar$comment = r$location; + return ar; + } + +function acld_add_rule_fun(p: PluginState, r: Rule) : bool + { + local ar = rule_to_acl_rule(r); + + if ( p$acld_config?$add_pred ) + if ( ! p$acld_config$add_pred(p, r, ar) ) + return F; + + if ( ar$command == "" ) + return F; + + BrokerComm::event(p$acld_config$acld_topic, BrokerComm::event_args(acld_add_rule, p$acld_id, r, ar)); + return T; + } + +function acld_remove_rule_fun(p: PluginState, r: Rule) : bool + { + local ar = rule_to_acl_rule(r); + if ( ar$command in acld_add_to_remove ) + ar$command = acld_add_to_remove[ar$command]; + else + return F; + + BrokerComm::event(p$acld_config$acld_topic, BrokerComm::event_args(acld_remove_rule, p$acld_id, r, ar)); + return T; + } + +function acld_init(p: PluginState) + { + BrokerComm::enable(); + BrokerComm::connect(cat(p$acld_config$acld_host), p$acld_config$acld_port, 1sec); + BrokerComm::subscribe_to_events(p$acld_config$acld_topic); + } + +global acld_plugin = Plugin( + $name=acld_name, + $can_expire = F, + $add_rule = acld_add_rule_fun, + $remove_rule = acld_remove_rule_fun, + $init = acld_init + ); + +function create_acld(config: AcldConfig) : PluginState + { + if ( config$acld_topic in pacf_acld_topics ) + Reporter::warning(fmt("Topic %s was added to Pacf acld plugin twice. Possible duplication of commands", config$acld_topic)); + else + add pacf_acld_topics[config$acld_topic]; + + local p: PluginState = [$acld_config=config, $plugin=acld_plugin, $acld_id=pacf_acld_current_id]; + + pacf_acld_id[pacf_acld_current_id] = p; + ++pacf_acld_current_id; + + return p; + } + diff --git a/testing/btest/Baseline/scripts.base.frameworks.pacf.acld/recv.recv.out b/testing/btest/Baseline/scripts.base.frameworks.pacf.acld/recv.recv.out new file mode 100644 index 0000000000..92ccbc6257 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.pacf.acld/recv.recv.out @@ -0,0 +1,7 @@ +BrokerComm::incoming_connection_established +add_rule, 0, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::FLOW, conn=, flow=[src_h=10.10.1.4/32, src_p=, dst_h=74.53.140.153/32, dst_p=, src_m=, dst_m=], ip=, mac=], expire=36000.0, priority=0, location=here, c=, i=, d=, s=, mod=, id=2, cid=2, _plugin_id=1], [command=blockhosthost, cookie=2, arg=10.10.1.4/32 74.53.140.153/32, comment=here] +add_rule, 0, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::FLOW, conn=, flow=[src_h=, src_p=, dst_h=, dst_p=25/tcp, src_m=, dst_m=], ip=, mac=], expire=36000.0, priority=0, location=here, c=, i=, d=, s=, mod=, id=3, cid=3, _plugin_id=1], [command=droptcpport, cookie=3, arg=25, comment=here] +add_rule, 0, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=36000.0, priority=0, location=, c=, i=, d=, s=, mod=, id=4, cid=4, _plugin_id=1], [command=drop, cookie=4, arg=10.10.1.4/32, comment=] +remove_rule, 0, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::FLOW, conn=, flow=[src_h=10.10.1.4/32, src_p=, dst_h=74.53.140.153/32, dst_p=, src_m=, dst_m=], ip=, mac=], expire=36000.0, priority=0, location=here, c=, i=, d=, s=, mod=, id=2, cid=2, _plugin_id=1], [command=restorehosthost, cookie=2, arg=10.10.1.4/32 74.53.140.153/32, comment=here] +remove_rule, 0, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::FLOW, conn=, flow=[src_h=, src_p=, dst_h=, dst_p=25/tcp, src_m=, dst_m=], ip=, mac=], expire=36000.0, priority=0, location=here, c=, i=, d=, s=, mod=, id=3, cid=3, _plugin_id=1], [command=restoretcpport, cookie=3, arg=25, comment=here] +remove_rule, 0, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=36000.0, priority=0, location=, c=, i=, d=, s=, mod=, id=4, cid=4, _plugin_id=1], [command=restore, cookie=4, arg=10.10.1.4/32, comment=] diff --git a/testing/btest/Baseline/scripts.base.frameworks.pacf.acld/send.send.out b/testing/btest/Baseline/scripts.base.frameworks.pacf.acld/send.send.out new file mode 100644 index 0000000000..1f7e952357 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.pacf.acld/send.send.out @@ -0,0 +1,7 @@ +BrokerComm::outgoing_connection_established, 127.0.0.1, 9999/tcp +rule added, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::FLOW, conn=, flow=[src_h=10.10.1.4/32, src_p=, dst_h=74.53.140.153/32, dst_p=, src_m=, dst_m=], ip=, mac=], expire=36000.0, priority=0, location=here, c=, i=, d=, s=, mod=, id=2, cid=2, _plugin_id=1] +rule added, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::FLOW, conn=, flow=[src_h=, src_p=, dst_h=, dst_p=25/tcp, src_m=, dst_m=], ip=, mac=], expire=36000.0, priority=0, location=here, c=, i=, d=, s=, mod=, id=3, cid=3, _plugin_id=1] +rule added, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=36000.0, priority=0, location=, c=, i=, d=, s=, mod=, id=4, cid=4, _plugin_id=1] +rule removed, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::FLOW, conn=, flow=[src_h=10.10.1.4/32, src_p=, dst_h=74.53.140.153/32, dst_p=, src_m=, dst_m=], ip=, mac=], expire=36000.0, priority=0, location=here, c=, i=, d=, s=, mod=, id=2, cid=2, _plugin_id=1] +rule removed, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::FLOW, conn=, flow=[src_h=, src_p=, dst_h=, dst_p=25/tcp, src_m=, dst_m=], ip=, mac=], expire=36000.0, priority=0, location=here, c=, i=, d=, s=, mod=, id=3, cid=3, _plugin_id=1] +rule removed, [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=36000.0, priority=0, location=, c=, i=, d=, s=, mod=, id=4, cid=4, _plugin_id=1] diff --git a/testing/btest/scripts/base/frameworks/pacf/acld.bro b/testing/btest/scripts/base/frameworks/pacf/acld.bro new file mode 100644 index 0000000000..bf10138a6a --- /dev/null +++ b/testing/btest/scripts/base/frameworks/pacf/acld.bro @@ -0,0 +1,111 @@ +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro broker_port=$BROKER_PORT >recv.out" +# @TEST-EXEC: btest-bg-run send "bro -b -r $TRACES/smtp.trace --pseudo-realtime ../send.bro broker_port=$BROKER_PORT >send.out" + +# @TEST-EXEC: btest-bg-wait 20 +# @TEST-EXEC: btest-diff recv/recv.out +# @TEST-EXEC: btest-diff send/send.out + +@TEST-START-FILE send.bro + +@load base/frameworks/pacf + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +event bro_init() + { + suspend_processing(); + local pacf_acld = Pacf::create_acld(Pacf::AcldConfig($acld_host=127.0.0.1, $acld_port=broker_port, $acld_topic="bro/event/pacftest")); + Pacf::activate(pacf_acld, 0); + } + +event BrokerComm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + print "BrokerComm::outgoing_connection_established", peer_address, peer_port; + continue_processing(); + } + +event BrokerComm::outgoing_connection_broken(peer_address: string, + peer_port: port) + { + terminate(); + } + +event connection_established(c: connection) + { + local id = c$id; + + local flow1 = Pacf::Flow( + $src_h=addr_to_subnet(c$id$orig_h), + $dst_h=addr_to_subnet(c$id$resp_h) + ); + local e1: Pacf::Entity = [$ty=Pacf::FLOW, $flow=flow1]; + local r1: Pacf::Rule = [$ty=Pacf::DROP, $target=Pacf::FORWARD, $entity=e1, $expire=10hrs, $location="here"]; + + local flow2 = Pacf::Flow( + $dst_p=c$id$resp_p + ); + local e2: Pacf::Entity = [$ty=Pacf::FLOW, $flow=flow2]; + local r2: Pacf::Rule = [$ty=Pacf::DROP, $target=Pacf::FORWARD, $entity=e2, $expire=10hrs, $location="here"]; + + Pacf::add_rule(r1); + Pacf::add_rule(r2); + Pacf::drop_address(id$orig_h, 10hrs); + } + +event Pacf::rule_added(r: Pacf::Rule, p: Pacf::PluginState, msg: string) + { + print "rule added", r; + Pacf::remove_rule(r$id); + } + +event Pacf::rule_removed(r: Pacf::Rule, p: Pacf::PluginState, msg: string) + { + print "rule removed", r; + } + +@TEST-END-FILE + +@TEST-START-FILE recv.bro + +@load base/frameworks/pacf +@load base/frameworks/broker + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +event bro_init() + { + BrokerComm::enable(); + BrokerComm::subscribe_to_events("bro/event/pacftest"); + BrokerComm::listen(broker_port, "127.0.0.1"); + } + +event BrokerComm::incoming_connection_established(peer_name: string) + { + print "BrokerComm::incoming_connection_established"; + } + +event Pacf::acld_add_rule(id: count, r: Pacf::Rule, ar: Pacf::AclRule) + { + print "add_rule", id, r, ar; + + BrokerComm::event("bro/event/pacftest", BrokerComm::event_args(Pacf::acld_rule_added, id, r, ar$command)); + } + +event Pacf::acld_remove_rule(id: count, r: Pacf::Rule, ar: Pacf::AclRule) + { + print "remove_rule", id, r, ar; + + BrokerComm::event("bro/event/pacftest", BrokerComm::event_args(Pacf::acld_rule_removed, id, r, ar$command)); + + if ( r$cid == 4 ) + terminate(); + } + +@TEST-END-FILE +