Merge remote-tracking branch 'origin/master' into topic/vladg/kerberos

This commit is contained in:
Vlad Grigorescu 2015-04-17 20:29:34 -04:00
commit 1ff45c9fe1
547 changed files with 20267 additions and 4059 deletions

3
.gitmodules vendored
View file

@ -22,3 +22,6 @@
[submodule "aux/plugins"]
path = aux/plugins
url = git://git.bro.org/bro-plugins
[submodule "aux/broker"]
path = aux/broker
url = git://git.bro.org/broker

455
CHANGES
View file

@ -1,4 +1,459 @@
2.3-720 | 2015-04-17 14:18:26 -0700
* Updating NEWS.
2.3-716 | 2015-04-17 13:06:37 -0700
* Add seeking functionality to raw reader. One can now add an option
"offset" to the config map. Positive offsets are interpreted to be
from the beginning of the file, negative from the end of the file
(-1 is end of file). Only works for raw reader in streaming or
manual mode. Does not work with executables. Addresses BIT-985.
(Johanna Amann)
* Allow setting packet and byte thresholds for connections. (Johanna Amann)
This extends the ConnSize analyzer to be able to raise events when
each direction of a connection crosses a certain amount of bytes
or packets.
Thresholds are set using:
- set_conn_bytes_threshold(c$id, [num-bytes], [direction]);
- set_conn_packets_threshold(c$id, [num-packets], [direction]);
They raise the events, respectively:
- event conn_bytes_threshold_crossed(c: connection, threshold: count, is_orig: bool)
- event conn_packets_threshold_crossed(c: connection, threshold: count, is_orig: bool)
Current thresholds can be examined using get_conn_bytes_threshold()
and get_conn_packets_threshold().
Only one threshold can be set per connection.
* Add high-level API for packet/bytes thresholding in
base/protocols/conn/thresholds.bro that holds lists of thresholds
and raises an event for each threshold exactly once. (Johanna
Amann)
* Fix a bug where child packet analyzers of the TCP analyzer
where not found using FindChild.
* Update GridFTP analyzer to use connection thresholding instead of
polling. (Johanna Amann)
2.3-709 | 2015-04-17 12:37:32 -0700
* Fix addressing the dreaded "internal error: unknown msg type 115
in Poll()". (Jon Siwek)
This patch removes the error handling code for overload conditions
in the main process that could cause trouble down the road. The
"chunked_io_buffer_soft_cap" script variable can now tune when the
client process begins shutting down peer connections, and the
default setting is now double what it used to be. Addresses
BIT-1376.
2.3-707 | 2015-04-17 10:57:59 -0500
* Add more info about Broker to NEWS. (Jon Siwek)
2.3-705 | 2015-04-16 08:16:45 -0700
* Update Mozilla CA list. (Johanna Amann)
* Update tests to have them keep using older certificates where
appropiate. (Johanna Amann)
2.3-699 | 2015-04-16 09:51:58 -0500
* Fix the to_count function to use strtoull versus strtoll.
(Jon Siwek)
2.3-697 | 2015-04-15 09:51:15 -0700
* Removing error check verifying that an ASCII writer has been
properly finished. Instead of aborting, we now just clean up in
that case and proceed. Addresses BIT-1331. (Robin Sommer)
2.3-696 | 2015-04-14 15:56:36 -0700
* Update sqlite to 3.8.9
2.3-695 | 2015-04-13 10:34:42 -0500
* Fix iterator invalidation in broker::Manager dtor. (Jon Siwek)
* Add paragraph to plugin documentation. (Robin Sommer)
2.3-693 | 2015-04-11 10:56:31 -0700
* BIT-1367: improve coercion of anonymous records in set constructor.
(Jon Siwek)
* Allow to specify ports for sftp log rotator. (Johanna Amann)
2.3-690 | 2015-04-10 21:51:10 -0700
* Make sure to always delete the remote serializer. Addresses
BIT-1306 and probably also BIT-1356. (Robin Sommer)
* Cleaning up --help. -D and -Y/y were still listed, even though
they had no effect anymore. Removing some dead code along with -D.
Addresses BIT-1372. (Robin Sommer)
2.3-688 | 2015-04-10 08:10:44 -0700
* Update SQLite to 3.8.8.3.
2.3-687 | 2015-04-10 07:32:52 -0700
* Remove stale signature benchmarking code (-L command-line option).
(Jon Siwek)
* BIT-844: fix UDP payload signatures to match packet-wise. (Jon
Siwek)
2.3-682 | 2015-04-09 12:07:00 -0700
* Fixing input readers' component type. (Robin Sommer)
* Tiny spelling correction. (Seth Hall)
2.3-680 | 2015-04-06 16:02:43 -0500
* BIT-1371: remove CMake version check from binary package scripts.
(Jon Siwek)
2.3-679 | 2015-04-06 10:16:36 -0500
* Increase some unit test timeouts. (Jon Siwek)
* Fix Coverity warning in RDP analyzer. (Jon Siwek)
2.3-676 | 2015-04-02 10:10:39 -0500
* BIT-1366: improve checksum offloading warning.
(Frank Meier, Jon Siwek)
2.3-675 | 2015-03-30 17:05:05 -0500
* Add an RDP analyzer. (Josh Liburdi, Seth Hall, Johanna Amann)
2.3-640 | 2015-03-30 13:51:51 -0500
* BIT-1359: Limit maximum number of DTLS fragments to 30. (Johanna Amann)
2.3-637 | 2015-03-30 12:02:07 -0500
* Increase timeout duration in some broker tests. (Jon Siwek)
2.3-636 | 2015-03-30 11:26:32 -0500
* Updates related to SSH analysis. (Jon Siwek)
- Some scripts used wrong SSH module/namespace scoping on events.
- Fix outdated notice documentation related to SSH password guessing.
- Add a unit test for SSH pasword guessing notice.
2.3-635 | 2015-03-30 11:02:45 -0500
* Fix outdated documentation unit tests. (Jon Siwek)
2.3-634 | 2015-03-30 10:22:45 -0500
* Add a canonifier to a unit test's output. (Jon Siwek)
2.3-633 | 2015-03-25 18:32:59 -0700
* Log::write in signature framework was missing timestamp.
(Andrew Benson/Michel Laterman)
2.3-631 | 2015-03-25 11:03:12 -0700
* New SSH analyzer. (Vlad Grigorescu)
2.3-600 | 2015-03-25 10:23:46 -0700
* Add defensive checks in code to calculate log rotation intervals.
(Pete Nelson).
2.3-597 | 2015-03-23 12:50:04 -0700
* DTLS analyzer. (Johanna Amann)
* Implement correct parsing of TLS record fragmentation. (Johanna
Amann)
2.3-582 | 2015-03-23 11:34:25 -0700
* BIT-1313: In debug builds, "bro -B <x>" now supports "all" and
"help" for "<x>". "all" enables all debug streams. "help" prints a
list of available debug streams. (John Donnelly/Robin Sommer).
* BIT-1324: Allow logging filters to inherit default path from
stream. This allows the path for the default filter to be
specified explicitly through $path="..." when creating a stream.
Adapted the existing Log::create_stream calls to explicitly
specify a path value. (Jon Siwek)
* BIT-1199: Change the way the input framework deals with values it
cannot convert into BroVals, raising error messages instead of
aborting execution. (Johanna Amann)
* BIT-788: Use DNS QR field to better identify flow direction. (Jon
Siwek)
2.3-572 | 2015-03-23 13:04:53 -0500
* BIT-1226: Fix an example in quickstart docs. (Jon siwek)
2.3-570 | 2015-03-23 09:51:20 -0500
* Correct a spelling error (Daniel Thayer)
* Improvement to SSL analyzer failure mode. (Johanna Amann)
2.3-565 | 2015-03-20 16:27:41 -0500
* BIT-978: Improve documentation of 'for' loop iterator invalidation.
(Jon Siwek)
2.3-564 | 2015-03-20 11:12:02 -0500
* BIT-725: Remove "unmatched_HTTP_reply" weird. (Jon Siwek)
2.3-562 | 2015-03-20 10:31:02 -0500
* BIT-1207: Add unit test to catch breaking changes to local.bro
(Jon Siwek)
* Fix failing sqlite leak test (Johanna Amann)
2.3-560 | 2015-03-19 13:17:39 -0500
* BIT-1255: Increase default values of
"tcp_max_above_hole_without_any_acks" and "tcp_max_initial_window"
from 4096 to 16384 bytes. (Jon Siwek)
2.3-559 | 2015-03-19 12:14:33 -0500
* BIT-849: turn SMTP reporter warnings into weirds,
"smtp_nested_mail_transaction" and "smtp_unmatched_end_of_data".
(Jon Siwek)
2.3-558 | 2015-03-18 22:50:55 -0400
* DNS: Log the type number for the DNS_RR_unknown_type weird. (Vlad Grigorescu)
2.3-555 | 2015-03-17 15:57:13 -0700
* Splitting test-all Makefile target into Bro tests and test-aux.
(Robin Sommer)
2.3-554 | 2015-03-17 15:40:39 -0700
* Deprecate &rotate_interval, &rotate_size, &encrypt. Addresses
BIT-1305. (Jon Siwek)
2.3-549 | 2015-03-17 09:12:18 -0700
* BIT-1077: Fix HTTP::log_server_header_names. Before, it just
re-logged fields from the client side. (Jon Siwek)
2.3-547 | 2015-03-17 09:07:51 -0700
* Update certificate validation script to cache valid intermediate
chains that it encounters on the wire and use those to try to
validate chains that might be missing intermediate certificates.
(Johanna Amann)
2.3-541 | 2015-03-13 15:44:08 -0500
* Make INSTALL a symlink to doc/install/install.rst (Jon siwek)
* Fix Broxygen coverage. (Jon Siwek)
2.3-539 | 2015-03-13 14:19:27 -0500
* BIT-1335: Include timestamp in default extracted file names.
And add a policy script to extract all files. (Jon Siwek)
* BIT-1311: Identify GRE tunnels as Tunnel::GRE, not Tunnel::IP.
(Jon Siwek)
* BIT-1309: Add Connection class getter methods for flow labels.
(Jon Siwek)
2.3-536 | 2015-03-12 16:16:24 -0500
* Fix Broker leak tests. (Jon Siwek)
2.3-534 | 2015-03-12 10:59:49 -0500
* Update NEWS file. (Jon Siwek)
2.3-533 | 2015-03-12 10:18:53 -0500
* Give broker python bindings default install path within --prefix.
(Jon Siwek)
2.3-530 | 2015-03-10 13:22:39 -0500
* Fix broker data stores in absence of --enable-debug. (Jon Siwek)
2.3-529 | 2015-03-09 13:14:27 -0500
* Fix format specifier in SSL protocol violation. (Jon Siwek)
2.3-526 | 2015-03-06 12:48:49 -0600
* Fix build warnings, clarify broker requirements, update submodule.
(Jon Siwek)
* Rename comm/ directories to broker/ (Jon Siwek)
* Rename broker-related namespaces. (Jon Siwek)
* Improve remote logging via broker by only sending fields w/ &log.
(Jon Siwek)
* Disable a stream's remote logging via broker if it fails. (Jon Siwek)
* Improve some broker communication unit tests. (Jon Siwek)
2.3-518 | 2015-03-04 13:13:50 -0800
* Add bytes_recvd to stats.log recording the number of bytes
received, according to packet headers. (Mike Smiley)
2.3-516 | 2015-03-04 12:30:06 -0800
* Extract most specific Common Name from SSL certificates (Johanna
Amann)
* Send CN and SAN fields of SSL certificates to the Intel framework.
(Johanna Amann)
2.3-511 | 2015-03-02 18:07:17 -0800
* Changes to plugin meta hooks for function calls. (Gilbert Clark)
- Add frame argument.
- Change return value to tuple unambigiously whether hook
returned a result.
2.3-493 | 2015-03-02 17:17:32 -0800
* Extend the SSL weak-keys policy file to also alert when
encountering SSL connections with old versions as well as unsafe
cipher suites. (Johanna Amann)
* Make the notice suppression handling of other SSL policy files a
tad more robust. (Johanna Amann)
2.3-491 | 2015-03-02 17:12:56 -0800
* Updating docs for recent addition of local_resp. (Robin Sommer)
2.3-489 | 2015-03-02 15:29:30 -0800
* Integrate Broker, Bro's new communication library. (Jon Siwek)
See aux/broker/README for more information on Broker, and
doc/frameworks/comm.rst for the corresponding Bro script API.
Broker support is by default off for now; it can be enabled at
configure time with --enable-broker. It requires CAF
(https://github.com/actor-framework/actor-framework); for now iot
needs CAF's "develop" branch. Broker also requires a C++11
compiler.
Broker will become a mandatory dependency in future Bro versions.
* Add --enable-c++11 configure flag to compile Bro's source code in
C++11 mode with a corresponding compiler. (Jon Siwek)
2.3-451 | 2015-02-24 16:37:08 -0800
* Updating submodule(s).
2.3-448 | 2015-02-23 16:58:10 -0800
* Updating NEWS. (Robin Sommer)
2.3-447 | 2015-02-23 16:28:30 -0800
* Fix potential crash in logging framework when deserializing
WriterInfo from remote. where config is present. Testcase crashes
on unpatched versions of Bro. (Aaron Eppert)
* Fix wrong value test in WriterBackend. (Aaron Eppert)
2.3-442 | 2015-02-23 13:29:30 -0800
* Add a "local_resp" field to conn.log, along the lines of the
existing "local_orig". (Mike Smiley)
2.3-440 | 2015-02-23 11:39:17 -0600
* Updating plugin docs to recent changes. (Robin Sommer)
* Updating plugin tests to recent changes. (Robin Sommer)
* Making plugin names case-insensitive for some internal comparisions.
Makes plugin system more tolerant against spelling inconsistencies
are hard to catch otherwise. (Robin Sommer)
* Explicitly removing some old scripts on install that have moved
into plugins to prevent them causing confusion. (Robin Sommer)
* BIT-1312: Removing setting installation plugin path from
bro-path-dev.sh. Also, adding to existing BRO_PLUGIN_PATH rather
than replacing. (Robin Sommer)
* Creating the installation directory for plugins at install time.
(Robin Sommer)
2.3-427 | 2015-02-20 13:49:33 -0800
* Removing dependency on PCAP_NETMASK_UNKNOWN to compile with
libpcap < 1.1.1. (Robin Sommer)
2.3-426 | 2015-02-20 12:45:51 -0800
* Add 'while' statement to Bro language. Really. (Jon Siwek)
2.3-424 | 2015-02-20 12:39:10 -0800
* Add the ability to remove surrounding braces from the JSON
formatter. (Seth Hall)
2.3-419 | 2015-02-13 09:10:44 -0600
* BIT-1011: Update the SOCKS analyzer to support user/pass login.
(Nicolas Retrain, Seth Hall, Jon Siwek)
- Add a new field to socks.log: "password".
- Two new events: "socks_login_userpass_request" and
"socks_login_userpass_reply".
- Two new weirds for unsupported SOCKS authentication method or
version.
- A new test for authenticated socks traffic.
2.3-416 | 2015-02-12 12:18:42 -0600
* Submodule update - newest sqlite version (Johanna Amann)
* Fix use of deprecated gperftools headers. (Jon Siwek)
2.3-413 | 2015-02-08 18:23:05 -0800
* Fixing analyzer tag types for some Files::* functions. (Robin Sommer)
* Changing load order for plugin scripts. (Robin Sommer)
2.3-411 | 2015-02-05 10:05:48 -0600
* Fix file analysis of files with total size below the bof_buffer size

View file

@ -31,12 +31,12 @@ configure_file(bro-path-dev.in ${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev.sh
"export BROPATH=`${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev`\n"
"export BRO_PLUGIN_PATH=\"${CMAKE_CURRENT_BINARY_DIR}/src:${BRO_PLUGIN_INSTALL_PATH}\"\n"
"export BRO_PLUGIN_PATH=\"${CMAKE_CURRENT_BINARY_DIR}/src\":${BRO_PLUGIN_PATH}\n"
"export PATH=\"${CMAKE_CURRENT_BINARY_DIR}/src\":$PATH\n")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev.csh
"setenv BROPATH `${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev`\n"
"setenv BRO_PLUGIN_PATH \"${CMAKE_CURRENT_BINARY_DIR}/src:${BRO_PLUGIN_INSTALL_PATH}\"\n"
"setenv BRO_PLUGIN_PATH \"${CMAKE_CURRENT_BINARY_DIR}/src\":${BRO_PLUGIN_PATH}\n"
"setenv PATH \"${CMAKE_CURRENT_BINARY_DIR}/src\":$PATH\n")
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" VERSION LIMIT_COUNT 1)
@ -177,6 +177,17 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
########################################################################
## Recurse on sub-directories
if ( ENABLE_CXX11 )
include(RequireCXX11)
endif ()
if ( ENABLE_BROKER )
add_subdirectory(aux/broker)
set(brodeps ${brodeps} broker)
add_definitions(-DENABLE_BROKER)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/aux/broker)
endif ()
add_subdirectory(src)
add_subdirectory(scripts)
add_subdirectory(doc)
@ -224,6 +235,7 @@ message(
"\nCXXFLAGS: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BuildType}}"
"\nCPP: ${CMAKE_CXX_COMPILER}"
"\n"
"\nBroker: ${ENABLE_BROKER}"
"\nBroccoli: ${INSTALL_BROCCOLI}"
"\nBroctl: ${INSTALL_BROCTL}"
"\nAux. Tools: ${INSTALL_AUX_TOOLS}"

View file

@ -1,3 +0,0 @@
See doc/install/install.rst for installation instructions.

1
INSTALL Symbolic link
View file

@ -0,0 +1 @@
doc/install/install.rst

View file

@ -51,13 +51,15 @@ distclean:
$(MAKE) -C testing $@
test:
@( cd testing && make )
-@( cd testing && make )
test-all: test
test -d aux/broctl && ( cd aux/broctl && make test-all )
test -d aux/btest && ( cd aux/btest && make test )
test -d aux/bro-aux && ( cd aux/bro-aux && make test )
test -d aux/plugins && ( cd aux/plugins && make test-all )
test-aux:
-test -d aux/broctl && ( cd aux/broctl && make test-all )
-test -d aux/btest && ( cd aux/btest && make test )
-test -d aux/bro-aux && ( cd aux/bro-aux && make test )
-test -d aux/plugins && ( cd aux/plugins && make test-all )
test-all: test test-aux
configured:
@test -d $(BUILD) || ( echo "Error: No build/ directory found. Did you run configure?" && exit 1 )

136
NEWS
View file

@ -7,9 +7,6 @@ their own ``CHANGES``.)
Bro 2.4 (in progress)
=====================
Dependencies
------------
New Functionality
-----------------
@ -17,20 +14,98 @@ New Functionality
functionality, like protocol/file analysis, via shared libraries.
Plugins can be developed and distributed externally, and will be
pulled in dynamically at startup. Currently, a plugin can provide
custom protocol analyzers, file analyzers, log writers[TODO], input
readers[TODO], packet sources[TODO], and new built-in functions. A
plugin can furthermore hook into Bro's processing a number of places
to add custom logic.
custom protocol analyzers, file analyzers, log writers, input
readers, packet sources and dumpers, and new built-in functions. A
plugin can furthermore hook into Bro's processing at a number of
places to add custom logic.
See https://www.bro.org/sphinx-git/devel/plugins.html for more
information on writing plugins.
- Bro now has supoprt for the MySQL wire protocol. Activity gets
- Bro now has support for the MySQL wire protocol. Activity gets
logged into mysql.log.
- Bro now parses DTLS traffic.
- Bro now has an RDP analyzer.
- Bro now features a completely rewritten, enhanced SSH analyzer, with
a set of addedd events being generated. A lot more information about
SSH sessions is logged. The analyzer is able to determine if logins
failed or succeeded in most circumstances.
- Bro's file analysis now supports reassembly of files that are not
transferred/seen sequentially.
- Bro's scripting language now has a ``while`` statement::
while ( i < 5 )
print ++i;
``next`` and ``break`` can be used inside the loop's body just like
with ``for`` loops.
- Bro now integrates Broker, a new communication library. See
aux/broker/README for more information on Broker, and
doc/frameworks/broker.rst for the corresponding Bro script API.
With Broker, Bro has the similar capabilities of exchanging events and
logs with remote peers (either another Bro process or some other
application that uses Broker). It also includes a key-value store
API that can be used to share state between peers and optionally
allow data to persist on disk for longer-term storage.
Broker support is by default off for now; it can be enabled at
configure time with --enable-broker. It requires CAF version 0.13+
(https://github.com/actor-framework/actor-framework) as well as a
C++11 compiler (e.g. GCC 4.8+ or Clang 3.3+).
Broker will become a mandatory dependency in future Bro versions and
replace the current communcation and serialization system.
- Add --enable-c++11 configure flag to compile Bro's source code in
C++11 mode with a corresponding compiler. Note that 2.4 will be the
last version of Bro that compiles without C++11 support.
- The SSL analysis now alerts when encountering SSL connections with
old protocol versions or unsafe cipher suites. It also gained
extended reporting of weak keys, caching of already valdidated
certificates, full support TLS record defragmentation. SSL generally
became much more robust and added several fields to ssl.log (while
removing some other).
- A new icmp_sent_payload event provides access to ICMP payload.
- The input framework's raw reader now supports seeking by adding an
option "offset" to the config map. Positive offsets are interpreted
to be from the beginning of the file, negative from the end of the
file (-1 is end of file).
- One can now raise events when a connection crosses a given size
threshold in terms of packets or bytes. The primary API for that
functionality is in base/protocols/conn/thresholds.bro.
- BroControl now has a new command "deploy" which is equivalent to running
the "check", "install", "stop", and "start" commands (in that order).
- BroControl now has a new option "StatusCmdShowAll" that controls whether
or not the broctl "status" command gathers all of the status information.
This option can be used to make the "status" command run significantly
faster (in this case, the "Peers" column will not be shown in the output).
- BroControl now has a new option "StatsLogEnable" that controls whether
or not broctl will record information to the "stats.log" file. This option
can be used to make the "broctl cron" command run slightly faster (in this
case, "broctl cron" will also no longer send email about not seeing any
packets on the monitoring interfaces).
- BroControl now has a new option "MailHostUpDown" which controls whether or
not the "broctl cron" command will send email when it notices that a host
in the cluster is up or down.
- BroControl now has a new option "CommandTimeout" which specifies the number
of seconds to wait for a command that broctl ran to return results.
Changed Functionality
---------------------
@ -43,6 +118,11 @@ Changed Functionality
have been added which contain the same information. The
``mime_type`` field of ``Files::Info`` also still has this info.
* The earliest point that new mime type information is available is
in the ``file_mime_type`` event which comes after the ``file_new``
and ``file_over_new_connection`` events. Scripts which inspected
mime type info within those events will need to be adapted.
* Removed ``Files::add_analyzers_for_mime_type`` function.
* Removed ``offset`` parameter of the ``file_extraction_limit``
@ -56,6 +136,46 @@ Changed Functionality
- has_valid_octets: now uses a string_vec parameter instead of
string_array.
- conn.log gained a new field local_resp that works like local_orig,
just for the responder address of the connection.
- GRE tunnels are now identified as ``Tunnel::GRE`` instead of
``Tunnel::IP``.
- The default name for extracted files changed from extract-protocol-id
to extract-timestamp-protocol-id.
- The weird named "unmatched_HTTP_reply" has been removed since it can
be detected at the script-layer and is handled correctly by the
default HTTP scripts.
- When adding a logging filter to a stream, the filter can now inherit
a default ``path`` field from the associated ``Log::Stream`` record.
- When adding a logging filter to a stream, the
``Log::default_path_func`` is now only automatically added to the
filter if it has neither a ``path`` nor a ``path_func`` already
explicitly set. Before, the default path function would always be set
for all filters which didn't specify their own ``path_func``.
- BroControl now establishes only one ssh connection from the manager to
each remote host in a cluster configuration (previously, there would be
one ssh connection per remote Bro process).
- BroControl now uses SQLite to record state information instead of a
plain text file (the file "spool/broctl.dat" is no longer used).
On FreeBSD, this means that there is a new dependency on the package
"py27-sqlite3".
- BroControl now records the expected running state of each Bro node right
before each start or stop. The "broctl cron" command uses this info to
either start or stop Bro nodes as needed so that the actual state matches
the expected state (previously, "broctl cron" could only start nodes in
the "crashed" state, and could never stop a node).
- BroControl now sends all normal command output (i.e., not error messages)
to stdout. Error messages are still sent to stderr, however.
Deprecated Functionality
------------------------

View file

@ -1 +1 @@
2.3-411
2.3-720

@ -1 +1 @@
Subproject commit 77a86591dcf89d7252d3676d3f1199d6c927d073
Subproject commit 544330932e7cd4615d6d19f63907e8aa2acebb9e

@ -1 +1 @@
Subproject commit 0b713c027d3efaaca50e5df995c02656175573cd
Subproject commit 462e300bf9c37dcc39b70a4c2d89d19f7351c804

@ -1 +1 @@
Subproject commit d43cc790e5b8709b5e032e52ad0e00936494739b
Subproject commit 45276b39a946d70095c983753cd321ad07dcf285

@ -1 +1 @@
Subproject commit 8c9b87bc73e1ddaa304e3d89028c1e7b95d37a91
Subproject commit e864a0949e52a797f4000194b5c2980cf3618deb

1
aux/broker Submodule

@ -0,0 +1 @@
Subproject commit 0c25c1daa7dcf885dd16cc1b725295dc36decafe

@ -1 +1 @@
Subproject commit 93d4989ed1537e4d143cf09d44077159f869a4b2
Subproject commit d69df586c91531db0c3abe838b10a429dda4fa87

@ -1 +1 @@
Subproject commit ad600b5bdcd56a2723e323c0f2c8e1708956ca4f
Subproject commit 7a14085394e54a950e477eb4fafb3827ff8dbdc3

2
cmake

@ -1 +1 @@
Subproject commit 1316c07f7059647b6c4a496ea36e4b83bb5d8f0f
Subproject commit 2fd35ab6a6245a005828c32f0aa87eb21698c054

26
configure vendored
View file

@ -41,6 +41,9 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
--enable-perftools-debug use Google's perftools for debugging
--enable-jemalloc link against jemalloc
--enable-ruby build ruby bindings for broccoli (deprecated)
--enable-c++11 build using the C++11 standard
--enable-broker enable use of the Broker communication library
(requires C++ Actor Framework and C++11)
--disable-broccoli don't build or install the Broccoli library
--disable-broctl don't install Broctl
--disable-auxtools don't build or install auxiliary tools
@ -55,6 +58,8 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
--with-flex=PATH path to flex executable
--with-bison=PATH path to bison executable
--with-perl=PATH path to perl executable
--with-libcaf=PATH path to C++ Actor Framework installation
(a required Broker dependency)
Optional Packages in Non-Standard Locations:
--with-geoip=PATH path to the libGeoIP install root
@ -67,6 +72,8 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
--with-ruby-lib=PATH path to ruby library
--with-ruby-inc=PATH path to ruby headers
--with-swig=PATH path to SWIG executable
--with-rocksdb=PATH path to RocksDB installation
(an optional Broker dependency)
Packaging Options (for developers):
--binary-package toggle special logic for binary packaging
@ -142,6 +149,10 @@ while [ $# -ne 0 ]; do
append_cache_entry CMAKE_INSTALL_PREFIX PATH $optarg
append_cache_entry BRO_ROOT_DIR PATH $optarg
append_cache_entry PY_MOD_INSTALL_DIR PATH $optarg/lib/broctl
if [ -n "$user_enabled_broker" ]; then
append_cache_entry BROKER_PYTHON_HOME PATH $prefix
fi
;;
--scriptdir=*)
append_cache_entry BRO_SCRIPT_INSTALL_PATH STRING $optarg
@ -176,6 +187,15 @@ while [ $# -ne 0 ]; do
--enable-jemalloc)
append_cache_entry ENABLE_JEMALLOC BOOL true
;;
--enable-c++11)
append_cache_entry ENABLE_CXX11 BOOL true
;;
--enable-broker)
append_cache_entry ENABLE_CXX11 BOOL true
append_cache_entry ENABLE_BROKER BOOL true
append_cache_entry BROKER_PYTHON_HOME PATH $prefix
user_enabled_broker="true"
;;
--disable-broccoli)
append_cache_entry INSTALL_BROCCOLI BOOL false
;;
@ -248,6 +268,12 @@ while [ $# -ne 0 ]; do
--with-swig=*)
append_cache_entry SWIG_EXECUTABLE PATH $optarg
;;
--with-libcaf=*)
append_cache_entry LIBCAF_ROOT_DIR PATH $optarg
;;
--with-rocksdb=*)
append_cache_entry ROCKSDB_ROOT_DIR PATH $optarg
;;
--binary-package)
append_cache_entry BINARY_PACKAGING_MODE BOOL true
;;

View file

@ -0,0 +1 @@
../../../aux/broker/README

View file

@ -0,0 +1 @@
../../../aux/broker/broker-manual.rst

View file

@ -17,6 +17,8 @@ current, independent component releases.
Broccoli - User Manual <broccoli/broccoli-manual>
Broccoli Python Bindings <broccoli-python/README>
Broccoli Ruby Bindings <broccoli-ruby/README>
Broker - Bro's (New) Messaging Library (README) <broker/README>
Broker - User Manual <broker/broker-manual.rst>
BroControl - Interactive Bro management shell <broctl/README>
Bro-Aux - Small auxiliary tools for Bro <bro-aux/README>
BTest - A unit testing framework <btest/README>

View file

@ -3,7 +3,7 @@
Writing Bro Plugins
===================
Bro is internally moving to a plugin structure that enables extending
Bro internally provides plugin API that enables extending
the system dynamically, without modifying the core code base. That way
custom code remains self-contained and can be maintained, compiled,
and installed independently. Currently, plugins can add the following
@ -42,18 +42,17 @@ certain structure. To get started, Bro's distribution provides a
helper script ``aux/bro-aux/plugin-support/init-plugin`` that creates
a skeleton plugin that can then be customized. Let's use that::
# mkdir rot13-plugin
# cd rot13-plugin
# init-plugin Demo Rot13
# init-plugin ./rot13-plugin Demo Rot13
As you can see the script takes two arguments. The first is a
namespace the plugin will live in, and the second a descriptive name
for the plugin itself. Bro uses the combination of the two to identify
a plugin. The namespace serves to avoid naming conflicts between
plugins written by independent developers; pick, e.g., the name of
your organisation. The namespace ``Bro`` is reserved for functionality
distributed by the Bro Project. In our example, the plugin will be
called ``Demo::Rot13``.
As you can see, the script takes three arguments. The first is a
directory inside which the plugin skeleton will be created. The second
is the namespace the plugin will live in, and the third is a descriptive
name for the plugin itself relative to the namespace. Bro uses the
combination of namespace and name to identify a plugin. The namespace
serves to avoid naming conflicts between plugins written by independent
developers; pick, e.g., the name of your organisation. The namespace
``Bro`` is reserved for functionality distributed by the Bro Project. In
our example, the plugin will be called ``Demo::Rot13``.
The ``init-plugin`` script puts a number of files in place. The full
layout is described later. For now, all we need is
@ -61,7 +60,7 @@ layout is described later. For now, all we need is
there as follows::
# cat src/rot13.bif
module CaesarCipher;
module Demo;
function rot13%(s: string%) : string
%{
@ -82,18 +81,22 @@ The syntax of this file is just like any other ``*.bif`` file; we
won't go into it here.
Now we can already compile our plugin, we just need to tell the
configure script put in place by ``init-plugin`` where the Bro source
tree is located (Bro needs to have been built there first)::
configure script that ``init-plugin`` put in place where the Bro
source tree is located (Bro needs to have been built there first)::
# cd rot13-plugin
# ./configure --bro-dist=/path/to/bro/dist && make
[... cmake output ...]
Now our ``rot13-plugin`` directory has everything that it needs
for Bro to recognize it as a dynamic plugin. Once we point Bro to it,
it will pull it in automatically, as we can check with the ``-N``
This builds the plugin in a subdirectory ``build/``. In fact, that
subdirectory *becomes* the plugin: when ``make`` finishes, ``build/``
has everything it needs for Bro to recognize it as a dynamic plugin.
Let's try that. Once we point Bro to the ``build/`` directory, it will
pull in our new plugin automatically, as we can check with the ``-N``
option::
# export BRO_PLUGIN_PATH=/path/to/rot13-plugin
# export BRO_PLUGIN_PATH=/path/to/rot13-plugin/build
# bro -N
[...]
Plugin: Demo::Rot13 - <Insert brief description of plugin> (dynamic, version 1)
@ -127,12 +130,12 @@ more verbose option ``-NN``::
# bro -NN
[...]
Plugin: Demo::Rot13 - Caesar cipher rotating a string's characters by 13 places. (dynamic, version 1)
[Function] CaesarCipher::rot13
[Function] Demo::rot13
[...]
There's our function. Now let's use it::
# bro -e 'print CaesarCipher::rot13("Hello")'
# bro -e 'print Demo::rot13("Hello")'
Uryyb
It works. We next install the plugin along with Bro itself, so that it
@ -141,36 +144,40 @@ environment variable. If we first unset the variable, the function
will no longer be available::
# unset BRO_PLUGIN_PATH
# bro -e 'print CaesarCipher::rot13("Hello")'
error in <command line>, line 1: unknown identifier CaesarCipher::rot13, at or near "CaesarCipher::rot13"
# bro -e 'print Demo::rot13("Hello")'
error in <command line>, line 1: unknown identifier Demo::rot13, at or near "Demo::rot13"
Once we install it, it works again::
# make install
# bro -e 'print CaesarCipher::rot13("Hello")'
# bro -e 'print Demo::rot13("Hello")'
Uryyb
The installed version went into
``<bro-install-prefix>/lib/bro/plugins/Demo_Rot13``.
We can distribute the plugin in either source or binary form by using
the Makefile's ``sdist`` and ``bdist`` target, respectively. Both
create corrsponding tarballs::
One can distribute the plugin independently of Bro for others to use.
To distribute in source form, just remove the ``build/`` (``make
distclean`` does that) and then tar up the whole ``rot13-plugin/``
directory. Others then follow the same process as above after
unpacking. To distribute the plugin in binary form, the build process
conveniently creates a corresponding tarball in ``build/dist/``. In
this case, it's called ``Demo_Rot13-0.1.tar.gz``, with the version
number coming out of the ``VERSION`` file that ``init-plugin`` put
into place. The binary tarball has everything needed to run the
plugin, but no further source files. Optionally, one can include
further files by specifying them in the plugin's ``CMakeLists.txt``
through the ``bro_plugin_dist_files`` macro; the skeleton does that
for ``README``, ``VERSION``, ``CHANGES``, and ``COPYING``. To use the
plugin through the binary tarball, just unpack it and point
``BRO_PLUGIN_PATH`` there; or copy it into
``<bro-install-prefix>/lib/bro/plugins/`` directly.
# make sdist
[...]
Source distribution in build/sdist/Demo_Rot13.tar.gz
# make bdist
[...]
Binary distribution in build/Demo_Rot13-darwin-x86_64.tar.gz
The source archive will contain everything in the plugin directory
except any generated files. The binary archive will contain anything
needed to install and run the plugin, i.e., just what ``make install``
puts into place as well. As the binary distribution is
platform-dependent, its name includes the OS and architecture the
plugin was built on.
Before distributing your plugin, you should edit some of the meta
files that ``init-plugin`` puts in place. Edit ``README`` and
``VERSION``, and update ``CHANGES`` when you make changes. Also put a
license file in place as ``COPYING``; if BSD is fine, you find a
template in ``COPYING.edit-me``.
Plugin Directory Layout
=======================
@ -179,7 +186,7 @@ A plugin's directory needs to follow a set of conventions so that Bro
(1) recognizes it as a plugin, and (2) knows what to load. While
``init-plugin`` takes care of most of this, the following is the full
story. We'll use ``<base>`` to represent a plugin's top-level
directory.
directory. With the skeleton, ``<base>`` corresponds to ``build/``.
``<base>/__bro_plugin__``
A file that marks a directory as containing a Bro plugin. The file
@ -205,6 +212,8 @@ directory.
Directory with auto-generated Bro scripts that declare the plugin's
bif elements. The files here are produced by ``bifcl``.
Any other files in ``<base>`` are ignored by Bro.
By convention, a plugin should put its custom scripts into sub folders
of ``scripts/``, i.e., ``scripts/<script-namespace>/<script>.bro`` to
avoid conflicts. As usual, you can then put a ``__load__.bro`` in
@ -229,15 +238,32 @@ their source directory (after ``make`` and setting Bro's
install``).
``make install`` copies over the ``lib`` and ``scripts`` directories,
as well as the ``__bro_plugin__`` magic file and the ``README`` (which
you should customize). One can add further CMake ``install`` rules to
install additional files if needed.
as well as the ``__bro_plugin__`` magic file and any further
distribution files specified in ``CMakeLists.txt`` (e.g., README,
VERSION). You can find a full list of files installed in
``build/MANIFEST``. Behind the scenes, ``make install`` really just
copies over the binary tarball in ``build/dist``.
``init-plugin`` will never overwrite existing files, so it's safe to
rerun in an existing plugin directory; it only put files in place that
don't exist yet. That also provides a convenient way to revert a file
back to what ``init-plugin`` created originally: just delete it and
rerun.
``init-plugin`` will never overwrite existing files. If its target
directory already exists, it will by default decline to do anything.
You can run it with ``-u`` instead to update an existing plugin,
however it will never overwrite any existing files; it will only put
in place files it doesn't find yet. To revert a file back to what
``init-plugin`` created originally, delete it first and then rerun
with ``-u``.
``init-plugin`` puts a ``configure`` script in place that wraps
``cmake`` with a more familiar configure-style configuration. By
default, the script provides two options for specifying paths to the
Bro source (``--bro-dist``) and to the plugin's installation directory
(``--install-root``). To extend ``configure`` with plugin-specific
options (such as search paths for its dependencies) don't edit the
script directly but instead extend ``configure.plugin``, which
``configure`` includes. That way you will be able to more easily
update ``configure`` in the future when the distribution version
changes. In ``configure.plugin`` you can use the predefined shell
function ``append_cache_entry`` to seed values into the CMake cache;
see the installed skeleton version and existing plugins for examples.
Activating a Plugin
===================
@ -355,7 +381,7 @@ let's get that in place::
% cat .diag
== File ===============================
Demo::Rot13 - Caesar cipher rotating a string's characters by 13 places. (dynamic, version 1.0)
[Function] CaesarCipher::rot13
[Function] Demo::rot13
== Error ===============================
test-diff: no baseline found.
@ -375,14 +401,14 @@ Now let's add a custom test that ensures that our bif works
correctly::
# cd tests
# cat >plugin/rot13.bro
# cat >rot13/bif-rot13.bro
# @TEST-EXEC: bro %INPUT >output
# @TEST-EXEC: btest-diff output
event bro_init()
{
print CaesarCipher::rot13("Hello");
print Demo::rot13("Hello");
}
Check the output::
@ -415,7 +441,7 @@ Debugging Plugins
=================
If your plugin isn't loading as expected, Bro's debugging facilities
can help to illuminate what's going on. To enable, recompile Bro
can help illuminate what's going on. To enable, recompile Bro
with debugging support (``./configure --enable-debug``), and
afterwards rebuild your plugin as well. If you then run Bro with ``-B
plugins``, it will produce a file ``debug.log`` that records details
@ -435,7 +461,6 @@ replaced with a simple dash. Example: If the plugin is called
output will be recorded to ``debug.log`` if Bro's compiled in debug
mode.
Documenting Plugins
===================

202
doc/frameworks/broker.rst Normal file
View file

@ -0,0 +1,202 @@
.. _brokercomm-framework:
======================================
Broker-Enabled Communication Framework
======================================
.. rst-class:: opening
Bro can now use the `Broker Library
<../components/broker/README.html>`_ to exchange information with
other Bro processes. To enable it run Bro's ``configure`` script
with the ``--enable-broker`` option. Note that a C++11 compatible
compiler (e.g. GCC 4.8+ or Clang 3.3+) is required as well as the
`C++ Actor Framework <http://actor-framework.org/>`_.
.. contents::
Connecting to Peers
===================
Communication via Broker must first be turned on via
:bro:see:`BrokerComm::enable`.
Bro can accept incoming connections by calling :bro:see:`BrokerComm::listen`
and then monitor connection status updates via
:bro:see:`BrokerComm::incoming_connection_established` and
:bro:see:`BrokerComm::incoming_connection_broken`.
.. btest-include:: ${DOC_ROOT}/frameworks/broker/connecting-listener.bro
Bro can initiate outgoing connections by calling :bro:see:`BrokerComm::connect`
and then monitor connection status updates via
:bro:see:`BrokerComm::outgoing_connection_established`,
:bro:see:`BrokerComm::outgoing_connection_broken`, and
:bro:see:`BrokerComm::outgoing_connection_incompatible`.
.. btest-include:: ${DOC_ROOT}/frameworks/broker/connecting-connector.bro
Remote Printing
===============
To receive remote print messages, first use
:bro:see:`BrokerComm::subscribe_to_prints` to advertise to peers a topic
prefix of interest and then create an event handler for
:bro:see:`BrokerComm::print_handler` to handle any print messages that are
received.
.. btest-include:: ${DOC_ROOT}/frameworks/broker/printing-listener.bro
To send remote print messages, just call :bro:see:`BrokerComm::print`.
.. btest-include:: ${DOC_ROOT}/frameworks/broker/printing-connector.bro
Notice that the subscriber only used the prefix "bro/print/", but is
able to receive messages with full topics of "bro/print/hi",
"bro/print/stuff", and "bro/print/bye". The model here is that the
publisher of a message checks for all subscribers who advertised
interest in a prefix of that message's topic and sends it to them.
Message Format
--------------
For other applications that want to exchange print messages with Bro,
the Broker message format is simply:
.. code:: c++
broker::message{std::string{}};
Remote Events
=============
Receiving remote events is similar to remote prints. Just use
:bro:see:`BrokerComm::subscribe_to_events` and possibly define any new events
along with handlers that peers may want to send.
.. btest-include:: ${DOC_ROOT}/frameworks/broker/events-listener.bro
To send events, there are two choices. The first is to use call
:bro:see:`BrokerComm::event` directly. The second option is to use
:bro:see:`BrokerComm::auto_event` to make it so a particular event is
automatically sent to peers whenever it is called locally via the normal
event invocation syntax.
.. btest-include:: ${DOC_ROOT}/frameworks/broker/events-connector.bro
Again, the subscription model is prefix-based.
Message Format
--------------
For other applications that want to exchange event messages with Bro,
the Broker message format is:
.. code:: c++
broker::message{std::string{}, ...};
The first parameter is the name of the event and the remaining ``...``
are its arguments, which are any of the support Broker data types as
they correspond to the Bro types for the event named in the first
parameter of the message.
Remote Logging
==============
.. btest-include:: ${DOC_ROOT}/frameworks/broker/testlog.bro
Use :bro:see:`BrokerComm::subscribe_to_logs` to advertise interest in logs
written by peers. The topic names that Bro uses are implicitly of the
form "bro/log/<stream-name>".
.. btest-include:: ${DOC_ROOT}/frameworks/broker/logs-listener.bro
To send remote logs either use :bro:see:`Log::enable_remote_logging` or
:bro:see:`BrokerComm::enable_remote_logs`. The former allows any log stream
to be sent to peers while the later toggles remote logging for
particular streams.
.. btest-include:: ${DOC_ROOT}/frameworks/broker/logs-connector.bro
Message Format
--------------
For other applications that want to exchange logs messages with Bro,
the Broker message format is:
.. code:: c++
broker::message{broker::enum_value{}, broker::record{}};
The enum value corresponds to the stream's :bro:see:`Log::ID` value, and
the record corresponds to a single entry of that log's columns record,
in this case a ``Test::INFO`` value.
Tuning Access Control
=====================
By default, endpoints do not restrict the message topics that it sends
to peers and do not restrict what message topics and data store
identifiers get advertised to peers. These are the default
:bro:see:`BrokerComm::EndpointFlags` supplied to :bro:see:`BrokerComm::enable`.
If not using the ``auto_publish`` flag, one can use the
:bro:see:`BrokerComm::publish_topic` and :bro:see:`BrokerComm::unpublish_topic`
functions to manipulate the set of message topics (must match exactly)
that are allowed to be sent to peer endpoints. These settings take
precedence over the per-message ``peers`` flag supplied to functions
that take a :bro:see:`BrokerComm::SendFlags` such as :bro:see:`BrokerComm::print`,
:bro:see:`BrokerComm::event`, :bro:see:`BrokerComm::auto_event` or
:bro:see:`BrokerComm::enable_remote_logs`.
If not using the ``auto_advertise`` flag, one can use the
:bro:see:`BrokerComm::advertise_topic` and :bro:see:`BrokerComm::unadvertise_topic`
to manupulate the set of topic prefixes that are allowed to be
advertised to peers. If an endpoint does not advertise a topic prefix,
the only way a peers can send messages to it is via the ``unsolicited``
flag of :bro:see:`BrokerComm::SendFlags` and choosing a topic with a matching
prefix (i.e. full topic may be longer than receivers prefix, just the
prefix needs to match).
Distributed Data Stores
=======================
There are three flavors of key-value data store interfaces: master,
clone, and frontend.
A frontend is the common interface to query and modify data stores.
That is, a clone is a specific type of frontend and a master is also a
specific type of frontend, but a standalone frontend can also exist to
e.g. query and modify the contents of a remote master store without
actually "owning" any of the contents itself.
A master data store can be be cloned from remote peers which may then
perform lightweight, local queries against the clone, which
automatically stays synchronized with the master store. Clones cannot
modify their content directly, instead they send modifications to the
centralized master store which applies them and then broadcasts them to
all clones.
Master and clone stores get to choose what type of storage backend to
use. E.g. In-memory versus SQLite for persistence. Note that if clones
are used, data store sizes should still be able to fit within memory
regardless of the storage backend as a single snapshot of the master
store is sent in a single chunk to initialize the clone.
Data stores also support expiration on a per-key basis either using an
absolute point in time or a relative amount of time since the entry's
last modification time.
.. btest-include:: ${DOC_ROOT}/frameworks/broker/stores-listener.bro
.. btest-include:: ${DOC_ROOT}/frameworks/broker/stores-connector.bro
In the above example, if a local copy of the store contents isn't
needed, just replace the :bro:see:`BrokerStore::create_clone` call with
:bro:see:`BrokerStore::create_frontend`. Queries will then be made against
the remote master store instead of the local clone.
Note that all queries are made within Bro's asynchrounous ``when``
statements and must specify a timeout block.

View file

@ -0,0 +1,19 @@
const broker_port: port &redef;
redef exit_only_after_terminate = T;
redef BrokerComm::endpoint_name = "connector";
event bro_init()
{
BrokerComm::enable();
BrokerComm::connect("127.0.0.1", broker_port, 1sec);
}
event BrokerComm::outgoing_connection_established(peer_address: string,
peer_port: port,
peer_name: string)
{
print "BrokerComm::outgoing_connection_established",
peer_address, peer_port, peer_name;
terminate();
}

View file

@ -0,0 +1,21 @@
const broker_port: port &redef;
redef exit_only_after_terminate = T;
redef BrokerComm::endpoint_name = "listener";
event bro_init()
{
BrokerComm::enable();
BrokerComm::listen(broker_port, "127.0.0.1");
}
event BrokerComm::incoming_connection_established(peer_name: string)
{
print "BrokerComm::incoming_connection_established", peer_name;
}
event BrokerComm::incoming_connection_broken(peer_name: string)
{
print "BrokerComm::incoming_connection_broken", peer_name;
terminate();
}

View file

@ -0,0 +1,31 @@
const broker_port: port &redef;
redef exit_only_after_terminate = T;
redef BrokerComm::endpoint_name = "connector";
global my_event: event(msg: string, c: count);
global my_auto_event: event(msg: string, c: count);
event bro_init()
{
BrokerComm::enable();
BrokerComm::connect("127.0.0.1", broker_port, 1sec);
BrokerComm::auto_event("bro/event/my_auto_event", my_auto_event);
}
event BrokerComm::outgoing_connection_established(peer_address: string,
peer_port: port,
peer_name: string)
{
print "BrokerComm::outgoing_connection_established",
peer_address, peer_port, peer_name;
BrokerComm::event("bro/event/my_event", BrokerComm::event_args(my_event, "hi", 0));
event my_auto_event("stuff", 88);
BrokerComm::event("bro/event/my_event", BrokerComm::event_args(my_event, "...", 1));
event my_auto_event("more stuff", 51);
BrokerComm::event("bro/event/my_event", BrokerComm::event_args(my_event, "bye", 2));
}
event BrokerComm::outgoing_connection_broken(peer_address: string,
peer_port: port)
{
terminate();
}

View file

@ -0,0 +1,37 @@
const broker_port: port &redef;
redef exit_only_after_terminate = T;
redef BrokerComm::endpoint_name = "listener";
global msg_count = 0;
global my_event: event(msg: string, c: count);
global my_auto_event: event(msg: string, c: count);
event bro_init()
{
BrokerComm::enable();
BrokerComm::subscribe_to_events("bro/event/");
BrokerComm::listen(broker_port, "127.0.0.1");
}
event BrokerComm::incoming_connection_established(peer_name: string)
{
print "BrokerComm::incoming_connection_established", peer_name;
}
event my_event(msg: string, c: count)
{
++msg_count;
print "got my_event", msg, c;
if ( msg_count == 5 )
terminate();
}
event my_auto_event(msg: string, c: count)
{
++msg_count;
print "got my_auto_event", msg, c;
if ( msg_count == 5 )
terminate();
}

View file

@ -0,0 +1,40 @@
@load ./testlog
const broker_port: port &redef;
redef exit_only_after_terminate = T;
redef BrokerComm::endpoint_name = "connector";
redef Log::enable_local_logging = F;
redef Log::enable_remote_logging = F;
global n = 0;
event bro_init()
{
BrokerComm::enable();
BrokerComm::enable_remote_logs(Test::LOG);
BrokerComm::connect("127.0.0.1", broker_port, 1sec);
}
event do_write()
{
if ( n == 6 )
return;
Log::write(Test::LOG, [$msg = "ping", $num = n]);
++n;
event do_write();
}
event BrokerComm::outgoing_connection_established(peer_address: string,
peer_port: port,
peer_name: string)
{
print "BrokerComm::outgoing_connection_established",
peer_address, peer_port, peer_name;
event do_write();
}
event BrokerComm::outgoing_connection_broken(peer_address: string,
peer_port: port)
{
terminate();
}

View file

@ -0,0 +1,25 @@
@load ./testlog
const broker_port: port &redef;
redef exit_only_after_terminate = T;
redef BrokerComm::endpoint_name = "listener";
event bro_init()
{
BrokerComm::enable();
BrokerComm::subscribe_to_logs("bro/log/Test::LOG");
BrokerComm::listen(broker_port, "127.0.0.1");
}
event BrokerComm::incoming_connection_established(peer_name: string)
{
print "BrokerComm::incoming_connection_established", peer_name;
}
event Test::log_test(rec: Test::Info)
{
print "wrote log", rec;
if ( rec$num == 5 )
terminate();
}

View file

@ -0,0 +1,26 @@
const broker_port: port &redef;
redef exit_only_after_terminate = T;
redef BrokerComm::endpoint_name = "connector";
event bro_init()
{
BrokerComm::enable();
BrokerComm::connect("127.0.0.1", broker_port, 1sec);
}
event BrokerComm::outgoing_connection_established(peer_address: string,
peer_port: port,
peer_name: string)
{
print "BrokerComm::outgoing_connection_established",
peer_address, peer_port, peer_name;
BrokerComm::print("bro/print/hi", "hello");
BrokerComm::print("bro/print/stuff", "...");
BrokerComm::print("bro/print/bye", "goodbye");
}
event BrokerComm::outgoing_connection_broken(peer_address: string,
peer_port: port)
{
terminate();
}

View file

@ -0,0 +1,26 @@
const broker_port: port &redef;
redef exit_only_after_terminate = T;
redef BrokerComm::endpoint_name = "listener";
global msg_count = 0;
event bro_init()
{
BrokerComm::enable();
BrokerComm::subscribe_to_prints("bro/print/");
BrokerComm::listen(broker_port, "127.0.0.1");
}
event BrokerComm::incoming_connection_established(peer_name: string)
{
print "BrokerComm::incoming_connection_established", peer_name;
}
event BrokerComm::print_handler(msg: string)
{
++msg_count;
print "got print message", msg;
if ( msg_count == 3 )
terminate();
}

View file

@ -0,0 +1,53 @@
const broker_port: port &redef;
redef exit_only_after_terminate = T;
global h: opaque of BrokerStore::Handle;
function dv(d: BrokerComm::Data): BrokerComm::DataVector
{
local rval: BrokerComm::DataVector;
rval[0] = d;
return rval;
}
global ready: event();
event BrokerComm::outgoing_connection_broken(peer_address: string,
peer_port: port)
{
terminate();
}
event BrokerComm::outgoing_connection_established(peer_address: string,
peer_port: port,
peer_name: string)
{
local myset: set[string] = {"a", "b", "c"};
local myvec: vector of string = {"alpha", "beta", "gamma"};
h = BrokerStore::create_master("mystore");
BrokerStore::insert(h, BrokerComm::data("one"), BrokerComm::data(110));
BrokerStore::insert(h, BrokerComm::data("two"), BrokerComm::data(223));
BrokerStore::insert(h, BrokerComm::data("myset"), BrokerComm::data(myset));
BrokerStore::insert(h, BrokerComm::data("myvec"), BrokerComm::data(myvec));
BrokerStore::increment(h, BrokerComm::data("one"));
BrokerStore::decrement(h, BrokerComm::data("two"));
BrokerStore::add_to_set(h, BrokerComm::data("myset"), BrokerComm::data("d"));
BrokerStore::remove_from_set(h, BrokerComm::data("myset"), BrokerComm::data("b"));
BrokerStore::push_left(h, BrokerComm::data("myvec"), dv(BrokerComm::data("delta")));
BrokerStore::push_right(h, BrokerComm::data("myvec"), dv(BrokerComm::data("omega")));
when ( local res = BrokerStore::size(h) )
{
print "master size", res;
event ready();
}
timeout 10sec
{ print "timeout"; }
}
event bro_init()
{
BrokerComm::enable();
BrokerComm::connect("127.0.0.1", broker_port, 1secs);
BrokerComm::auto_event("bro/event/ready", ready);
}

View file

@ -0,0 +1,43 @@
const broker_port: port &redef;
redef exit_only_after_terminate = T;
global h: opaque of BrokerStore::Handle;
global expected_key_count = 4;
global key_count = 0;
function do_lookup(key: string)
{
when ( local res = BrokerStore::lookup(h, BrokerComm::data(key)) )
{
++key_count;
print "lookup", key, res;
if ( key_count == expected_key_count )
terminate();
}
timeout 10sec
{ print "timeout", key; }
}
event ready()
{
h = BrokerStore::create_clone("mystore");
when ( local res = BrokerStore::keys(h) )
{
print "clone keys", res;
do_lookup(BrokerComm::refine_to_string(BrokerComm::vector_lookup(res$result, 0)));
do_lookup(BrokerComm::refine_to_string(BrokerComm::vector_lookup(res$result, 1)));
do_lookup(BrokerComm::refine_to_string(BrokerComm::vector_lookup(res$result, 2)));
do_lookup(BrokerComm::refine_to_string(BrokerComm::vector_lookup(res$result, 3)));
}
timeout 10sec
{ print "timeout"; }
}
event bro_init()
{
BrokerComm::enable();
BrokerComm::subscribe_to_events("bro/event/ready");
BrokerComm::listen(broker_port, "127.0.0.1");
}

View file

@ -0,0 +1,19 @@
module Test;
export {
redef enum Log::ID += { LOG };
type Info: record {
msg: string &log;
num: count &log;
};
global log_test: event(rec: Test::Info);
}
event bro_init() &priority=5
{
BrokerComm::enable();
Log::create_stream(Test::LOG, [$columns=Test::Info, $ev=log_test, $path="test"]);
}

View file

@ -14,4 +14,4 @@ Frameworks
notice
signatures
sumstats
broker

View file

@ -344,7 +344,7 @@ example for the ``Foo`` module:
event bro_init() &priority=5
{
# Create the stream. This also adds a default filter automatically.
Log::create_stream(Foo::LOG, [$columns=Info, $ev=log_foo]);
Log::create_stream(Foo::LOG, [$columns=Info, $ev=log_foo, $path="foo"]);
}
You can also add the state to the :bro:type:`connection` record to make

View file

@ -88,15 +88,15 @@ directly make modifications to the :bro:see:`Notice::Info` record
given as the argument to the hook.
Here's a simple example which tells Bro to send an email for all notices of
type :bro:see:`SSH::Password_Guessing` if the server is 10.0.0.1:
type :bro:see:`SSH::Password_Guessing` if the guesser attempted to log in to
the server at 192.168.56.103:
.. code:: bro
.. btest-include:: ${DOC_ROOT}/frameworks/notice_ssh_guesser.bro
hook Notice::policy(n: Notice::Info)
{
if ( n$note == SSH::Password_Guessing && n$id$resp_h == 10.0.0.1 )
add n$actions[Notice::ACTION_EMAIL];
}
.. btest:: notice_ssh_guesser.bro
@TEST-EXEC: btest-rst-cmd bro -C -r ${TRACES}/ssh/sshguess.pcap ${DOC_ROOT}/frameworks/notice_ssh_guesser.bro
@TEST-EXEC: btest-rst-cmd cat notice.log
.. note::
@ -112,8 +112,7 @@ a hook body to run before default hook bodies might look like this:
hook Notice::policy(n: Notice::Info) &priority=5
{
if ( n$note == SSH::Password_Guessing && n$id$resp_h == 10.0.0.1 )
add n$actions[Notice::ACTION_EMAIL];
# Insert your code here.
}
Hooks can also abort later hook bodies with the ``break`` keyword. This

View file

@ -0,0 +1,10 @@
@load protocols/ssh/detect-bruteforcing
redef SSH::password_guesses_limit=10;
hook Notice::policy(n: Notice::Info)
{
if ( n$note == SSH::Password_Guessing && /192\.168\.56\.103/ in n$sub )
add n$actions[Notice::ACTION_EMAIL];
}

View file

@ -30,7 +30,7 @@ export {
event bro_init() &priority=3
{
Log::create_stream(MimeMetrics::LOG, [$columns=Info]);
Log::create_stream(MimeMetrics::LOG, [$columns=Info, $path="mime_metrics"]);
local r1: SumStats::Reducer = [$stream="mime.bytes",
$apply=set(SumStats::SUM)];
local r2: SumStats::Reducer = [$stream="mime.hits",

View file

@ -0,0 +1,24 @@
@load protocols/ssl/expiring-certs
const watched_servers: set[addr] = {
87.98.220.10,
} &redef;
# Site::local_nets usually isn't something you need to modify if
# BroControl automatically sets it up from networks.cfg. It's
# shown here for completeness.
redef Site::local_nets += {
87.98.0.0/16,
};
hook Notice::policy(n: Notice::Info)
{
if ( n$note != SSL::Certificate_Expired )
return;
if ( n$id$resp_h !in watched_servers )
return;
add n$actions[Notice::ACTION_EMAIL];
}

View file

@ -156,9 +156,11 @@ changes we want to make:
notice that means an SSL connection was established and the server's
certificate couldn't be validated using Bro's default trust roots, but
we want to ignore it.
2) ``SSH::Login`` is a notice type that is triggered when an SSH connection
attempt looks like it may have been successful, and we want email when
that happens, but only for certain servers.
2) ``SSL::Certificate_Expired`` is a notice type that is triggered when
an SSL connection was established using an expired certificate. We
want email when that happens, but only for certain servers on the
local network (Bro can also proactively monitor for certs that will
soon expire, but this is just for demonstration purposes).
We've defined *what* we want to do, but need to know *where* to do it.
The answer is to use a script written in the Bro programming language, so
@ -203,7 +205,7 @@ the variable's value may not change at run-time, but whose initial value can be
modified via the ``redef`` operator at parse-time.
Let's continue on our path to modify the behavior for the two SSL
and SSH notices. Looking at :doc:`/scripts/base/frameworks/notice/main.bro`,
notices. Looking at :doc:`/scripts/base/frameworks/notice/main.bro`,
we see that it advertises:
.. code:: bro
@ -216,7 +218,7 @@ we see that it advertises:
const ignored_types: set[Notice::Type] = {} &redef;
}
That's exactly what we want to do for the SSL notice. Add to ``local.bro``:
That's exactly what we want to do for the first notice. Add to ``local.bro``:
.. code:: bro
@ -248,38 +250,30 @@ is valid before installing it and then restarting the Bro instance:
stopping bro ...
starting bro ...
Now that the SSL notice is ignored, let's look at how to send an email on
the SSH notice. The notice framework has a similar option called
``emailed_types``, but using that would generate email for all SSH servers and
we only want email for logins to certain ones. There is a ``policy`` hook
that is actually what is used to implement the simple functionality of
``ignored_types`` and
``emailed_types``, but it's extensible such that the condition and action taken
on notices can be user-defined.
Now that the SSL notice is ignored, let's look at how to send an email
on the other notice. The notice framework has a similar option called
``emailed_types``, but using that would generate email for all SSL
servers with expired certificates and we only want email for connections
to certain ones. There is a ``policy`` hook that is actually what is
used to implement the simple functionality of ``ignored_types`` and
``emailed_types``, but it's extensible such that the condition and
action taken on notices can be user-defined.
In ``local.bro``, let's define a new ``policy`` hook handler body
that takes the email action for SSH logins only for a defined set of servers:
In ``local.bro``, let's define a new ``policy`` hook handler body:
.. code:: bro
.. btest-include:: ${DOC_ROOT}/quickstart/conditional-notice.bro
const watched_servers: set[addr] = {
192.168.1.100,
192.168.1.101,
192.168.1.102,
} &redef;
.. btest:: conditional-notice
hook Notice::policy(n: Notice::Info)
{
if ( n$note == SSH::SUCCESSFUL_LOGIN && n$id$resp_h in watched_servers )
add n$actions[Notice::ACTION_EMAIL];
}
@TEST-EXEC: btest-rst-cmd bro -r ${TRACES}/tls/tls-expired-cert.trace ${DOC_ROOT}/quickstart/conditional-notice.bro
@TEST-EXEC: btest-rst-cmd cat notice.log
You'll just have to trust the syntax for now, but what we've done is
first declare our own variable to hold a set of watched addresses,
``watched_servers``; then added a hook handler body to the policy that will
generate an email whenever the notice type is an SSH login and the responding
host stored
inside the ``Info`` record's connection field is in the set of watched servers.
``watched_servers``; then added a hook handler body to the policy that
will generate an email whenever the notice type is an SSL expired
certificate and the responding host stored inside the ``Info`` record's
connection field is in the set of watched servers.
.. note:: Record field member access is done with the '$' character
instead of a '.' as might be expected from other languages, in

View file

@ -43,8 +43,6 @@ The Bro scripting language supports the following attributes.
+-----------------------------+-----------------------------------------------+
| :bro:attr:`&mergeable` |Prefer set union for synchronized state. |
+-----------------------------+-----------------------------------------------+
| :bro:attr:`&group` |Group event handlers to activate/deactivate. |
+-----------------------------+-----------------------------------------------+
| :bro:attr:`&error_handler` |Used internally for reporter framework events. |
+-----------------------------+-----------------------------------------------+
| :bro:attr:`&type_column` |Used by input framework for "port" type. |
@ -198,11 +196,6 @@ Here is a more detailed explanation of each attribute:
inconsistencies and can be avoided by unifying the two sets, rather
than merely overwriting the old value.
.. bro:attr:: &group
Groups event handlers such that those in the same group can be
jointly activated or deactivated.
.. bro:attr:: &error_handler
Internally set on the events that are associated with the reporter

View file

@ -45,8 +45,11 @@ Statements
| | file |
+----------------------------+------------------------+
| :bro:keyword:`for`, | Loop over each |
| :bro:keyword:`next`, | element in a container |
| :bro:keyword:`break` | object |
| :bro:keyword:`while`, | element in a container |
| :bro:keyword:`next`, | object (``for``), or |
| :bro:keyword:`break` | as long as a condition |
| | evaluates to true |
| | (``while``). |
+----------------------------+------------------------+
| :bro:keyword:`if` | Evaluate boolean |
| | expression and if true,|
@ -291,7 +294,10 @@ Here are the statements that the Bro scripting language supports.
.. bro:keyword:: for
A "for" loop iterates over each element in a string, set, vector, or
table and executes a statement for each iteration.
table and executes a statement for each iteration. Currently,
modifying a container's membership while iterating over it may
result in undefined behavior, so avoid adding or removing elements
inside the loop.
For each iteration of the loop, a loop variable will be assigned to an
element if the expression evaluates to a string or set, or an index if
@ -563,6 +569,36 @@ Here are the statements that the Bro scripting language supports.
See the :bro:keyword:`return` statement for an explanation of how to
create an asynchronous function in a Bro script.
.. bro:keyword:: while
A "while" loop iterates over a body statement as long a given
condition remains true.
A :bro:keyword:`break` statement can be used at any time to immediately
terminate the "while" loop, and a :bro:keyword:`next` statement can be
used to skip to the next loop iteration.
Example::
local i = 0;
while ( i < 5 )
print ++i;
while ( some_cond() )
{
local finish_up = F;
if ( skip_ahead() )
next;
[...]
if ( finish_up )
break;
[...]
}
.. _compound statement:

View file

@ -23,7 +23,7 @@ function factorial(n: count): count
event bro_init()
{
# Create the logging stream.
Log::create_stream(LOG, [$columns=Info]);
Log::create_stream(LOG, [$columns=Info, $path="factor"]);
}
event bro_done()

View file

@ -37,7 +37,7 @@ function mod5(id: Log::ID, path: string, rec: Factor::Info) : string
event bro_init()
{
Log::create_stream(LOG, [$columns=Info]);
Log::create_stream(LOG, [$columns=Info, $path="factor"]);
local filter: Log::Filter = [$name="split-mod5s", $path_func=mod5];
Log::add_filter(Factor::LOG, filter);

View file

@ -22,7 +22,7 @@ function factorial(n: count): count
event bro_init()
{
Log::create_stream(LOG, [$columns=Info, $ev=log_factor]);
Log::create_stream(LOG, [$columns=Info, $ev=log_factor, $path="factor"]);
}
event bro_done()

View file

@ -826,7 +826,7 @@ example of the ``record`` data type in the earlier sections, the
``conn.log``, is shown by the excerpt below.
.. btest-include:: ${BRO_SRC_ROOT}/scripts/base/protocols/conn/main.bro
:lines: 10-12,16-17,19,21,23,25,28,31,35,38,57,63,69,92,95,99,102,106,110-111,116
:lines: 10-12,16-17,19,21,23,25,28,31,35,38,57,63,69,75,98,101,105,108,112,116-117,122
Looking at the structure of the definition, a new collection of data
types is being defined as a type called ``Info``. Since this type

View file

@ -1,14 +0,0 @@
#!/bin/sh
# CMake/CPack versions before 2.8.3 have bugs that can create bad packages
# Since packages will be built on several different systems, a single
# version of CMake is required to obtain consistency, but can be increased
# as new versions of CMake come out that also produce working packages.
CMAKE_PACK_REQ="cmake version 2.8.6"
CMAKE_VER=`cmake -version`
if [ "${CMAKE_VER}" != "${CMAKE_PACK_REQ}" ]; then
echo "Package creation requires ${CMAKE_PACK_REQ}" >&2
exit 1
fi

View file

@ -3,8 +3,6 @@
# This script generates binary DEB packages.
# They can be found in ../build/ after running.
./check-cmake || { exit 1; }
# The DEB CPack generator depends on `dpkg-shlibdeps` to automatically
# determine what dependencies to set for the packages
type dpkg-shlibdeps > /dev/null 2>&1 || {

View file

@ -3,14 +3,6 @@
# This script creates binary packages for Mac OS X.
# They can be found in ../build/ after running.
cmake -P /dev/stdin << "EOF"
if ( ${CMAKE_VERSION} VERSION_LESS 2.8.9 )
message(FATAL_ERROR "CMake >= 2.8.9 required to build package")
endif ()
EOF
[ $? -ne 0 ] && exit 1;
type sw_vers > /dev/null 2>&1 || {
echo "Unable to get Mac OS X version" >&2;
exit 1;

View file

@ -3,8 +3,6 @@
# This script generates binary RPM packages.
# They can be found in ../build/ after running.
./check-cmake || { exit 1; }
# The RPM CPack generator depends on `rpmbuild` to create packages
type rpmbuild > /dev/null 2>&1 || {
echo "\

View file

@ -53,7 +53,8 @@ function set_limit(f: fa_file, args: Files::AnalyzerArgs, n: count): bool
function on_add(f: fa_file, args: Files::AnalyzerArgs)
{
if ( ! args?$extract_filename )
args$extract_filename = cat("extract-", f$source, "-", f$id);
args$extract_filename = cat("extract-", f$last_active, "-", f$source,
"-", f$id);
f$info$extracted = args$extract_filename;
args$extract_filename = build_path_compressed(prefix, args$extract_filename);

View file

@ -195,7 +195,7 @@ event Input::end_of_data(name: string, source: string)
event bro_init() &priority=5
{
Log::create_stream(Unified2::LOG, [$columns=Info, $ev=log_unified2]);
Log::create_stream(Unified2::LOG, [$columns=Info, $ev=log_unified2, $path="unified2"]);
if ( sid_msg == "" )
{

View file

@ -36,7 +36,7 @@ export {
event bro_init() &priority=5
{
Log::create_stream(X509::LOG, [$columns=Info, $ev=log_x509]);
Log::create_stream(X509::LOG, [$columns=Info, $ev=log_x509, $path="x509"]);
}
redef record Files::Info += {

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,103 @@
##! Various data structure definitions for use with Bro's communication system.
module BrokerComm;
export {
## A name used to identify this endpoint to peers.
## .. bro:see:: BrokerComm::connect BrokerComm::listen
const endpoint_name = "" &redef;
## Change communication behavior.
type EndpointFlags: record {
## Whether to restrict message topics that can be published to peers.
auto_publish: bool &default = T;
## Whether to restrict what message topics or data store identifiers
## the local endpoint advertises to peers (e.g. subscribing to
## events or making a master data store available).
auto_advertise: bool &default = T;
};
## Fine-grained tuning of communication behavior for a particular message.
type SendFlags: record {
## Send the message to the local endpoint.
self: bool &default = F;
## Send the message to peer endpoints that advertise interest in
## the topic associated with the message.
peers: bool &default = T;
## Send the message to peer endpoints even if they don't advertise
## interest in the topic associated with the message.
unsolicited: bool &default = F;
};
## Opaque communication data.
type Data: record {
d: opaque of BrokerComm::Data &optional;
};
## Opaque communication data.
type DataVector: vector of BrokerComm::Data;
## Opaque event communication data.
type EventArgs: record {
## The name of the event. Not set if invalid event or arguments.
name: string &optional;
## The arguments to the event.
args: DataVector;
};
## Opaque communication data used as a convenient way to wrap key-value
## pairs that comprise table entries.
type TableItem : record {
key: BrokerComm::Data;
val: BrokerComm::Data;
};
}
module BrokerStore;
export {
## Whether a data store query could be completed or not.
type QueryStatus: enum {
SUCCESS,
FAILURE,
};
## An expiry time for a key-value pair inserted in to a data store.
type ExpiryTime: record {
## Absolute point in time at which to expire the entry.
absolute: time &optional;
## A point in time relative to the last modification time at which
## to expire the entry. New modifications will delay the expiration.
since_last_modification: interval &optional;
};
## The result of a data store query.
type QueryResult: record {
## Whether the query completed or not.
status: BrokerStore::QueryStatus;
## The result of the query. Certain queries may use a particular
## data type (e.g. querying store size always returns a count, but
## a lookup may return various data types).
result: BrokerComm::Data;
};
## Options to tune the SQLite storage backend.
type SQLiteOptions: record {
## File system path of the database.
path: string &default = "store.sqlite";
};
## Options to tune the RocksDB storage backend.
type RocksDBOptions: record {
## File system path of the database.
path: string &default = "store.rocksdb";
};
## Options to tune the particular storage backends.
type BackendOptions: record {
sqlite: SQLiteOptions &default = SQLiteOptions();
rocksdb: RocksDBOptions &default = RocksDBOptions();
};
}

View file

@ -159,5 +159,5 @@ event bro_init() &priority=5
terminate();
}
Log::create_stream(Cluster::LOG, [$columns=Info]);
Log::create_stream(Cluster::LOG, [$columns=Info, $path="cluster"]);
}

View file

@ -164,7 +164,7 @@ const src_names = {
event bro_init() &priority=5
{
Log::create_stream(Communication::LOG, [$columns=Info]);
Log::create_stream(Communication::LOG, [$columns=Info, $path="communication"]);
}
function do_script_log_common(level: count, src: count, msg: string)

View file

@ -38,7 +38,7 @@ redef record connection += {
event bro_init() &priority=5
{
Log::create_stream(DPD::LOG, [$columns=Info]);
Log::create_stream(DPD::LOG, [$columns=Info, $path="dpd"]);
}
event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) &priority=10

View file

@ -267,7 +267,7 @@ export {
## mts: The set of MIME types, each in the form "foo/bar" (case-insensitive).
##
## Returns: True if the MIME types were successfully registered.
global register_for_mime_types: function(tag: Analyzer::Tag, mts: set[string]) : bool;
global register_for_mime_types: function(tag: Files::Tag, mts: set[string]) : bool;
## Registers a MIME type for an analyzer. If a future file with this type is seen,
## the analyzer will be automatically assigned to parsing it. The function *adds*
@ -278,20 +278,20 @@ export {
## mt: The MIME type in the form "foo/bar" (case-insensitive).
##
## Returns: True if the MIME type was successfully registered.
global register_for_mime_type: function(tag: Analyzer::Tag, mt: string) : bool;
global register_for_mime_type: function(tag: Files::Tag, mt: string) : bool;
## Returns a set of all MIME types currently registered for a specific analyzer.
##
## tag: The tag of the analyzer.
##
## Returns: The set of MIME types.
global registered_mime_types: function(tag: Analyzer::Tag) : set[string];
global registered_mime_types: function(tag: Files::Tag) : set[string];
## Returns a table of all MIME-type-to-analyzer mappings currently registered.
##
## Returns: A table mapping each analyzer to the set of MIME types
## registered for it.
global all_registered_mime_types: function() : table[Analyzer::Tag] of set[string];
global all_registered_mime_types: function() : table[Files::Tag] of set[string];
## Event that can be handled to access the Info record as it is sent on
## to the logging framework.
@ -306,14 +306,14 @@ redef record fa_file += {
global registered_protocols: table[Analyzer::Tag] of ProtoRegistration = table();
# Store the MIME type to analyzer mappings.
global mime_types: table[Analyzer::Tag] of set[string];
global mime_type_to_analyzers: table[string] of set[Analyzer::Tag];
global mime_types: table[Files::Tag] of set[string];
global mime_type_to_analyzers: table[string] of set[Files::Tag];
global analyzer_add_callbacks: table[Files::Tag] of function(f: fa_file, args: AnalyzerArgs) = table();
event bro_init() &priority=5
{
Log::create_stream(Files::LOG, [$columns=Info, $ev=log_files]);
Log::create_stream(Files::LOG, [$columns=Info, $ev=log_files, $path="files"]);
}
function set_info(f: fa_file)
@ -401,7 +401,7 @@ function register_protocol(tag: Analyzer::Tag, reg: ProtoRegistration): bool
return result;
}
function register_for_mime_types(tag: Analyzer::Tag, mime_types: set[string]) : bool
function register_for_mime_types(tag: Files::Tag, mime_types: set[string]) : bool
{
local rc = T;
@ -414,7 +414,7 @@ function register_for_mime_types(tag: Analyzer::Tag, mime_types: set[string]) :
return rc;
}
function register_for_mime_type(tag: Analyzer::Tag, mt: string) : bool
function register_for_mime_type(tag: Files::Tag, mt: string) : bool
{
if ( tag !in mime_types )
{
@ -431,12 +431,12 @@ function register_for_mime_type(tag: Analyzer::Tag, mt: string) : bool
return T;
}
function registered_mime_types(tag: Analyzer::Tag) : set[string]
function registered_mime_types(tag: Files::Tag) : set[string]
{
return tag in mime_types ? mime_types[tag] : set();
}
function all_registered_mime_types(): table[Analyzer::Tag] of set[string]
function all_registered_mime_types(): table[Files::Tag] of set[string]
{
return mime_types;
}
@ -451,7 +451,7 @@ function describe(f: fa_file): string
return handler$describe(f);
}
event get_file_handle(tag: Analyzer::Tag, c: connection, is_orig: bool) &priority=5
event get_file_handle(tag: Files::Tag, c: connection, is_orig: bool) &priority=5
{
if ( tag !in registered_protocols )
return;

View file

@ -32,6 +32,8 @@ export {
FILE_NAME,
## Certificate SHA-1 hash.
CERT_HASH,
## Public key MD5 hash. (SSH server host keys are a good example.)
PUBKEY_HASH,
};
## Data about an :bro:type:`Intel::Item`.
@ -174,7 +176,7 @@ global min_data_store: MinDataStore &redef;
event bro_init() &priority=5
{
Log::create_stream(LOG, [$columns=Info, $ev=log_intel]);
Log::create_stream(LOG, [$columns=Info, $ev=log_intel, $path="intel"]);
}
function find(s: Seen): bool

View file

@ -50,11 +50,17 @@ export {
## The event receives a single same parameter, an instance of
## type ``columns``.
ev: any &optional;
## A path that will be inherited by any filters added to the
## stream which do not already specify their own path.
path: string &optional;
};
## Builds the default path values for log filters if not otherwise
## specified by a filter. The default implementation uses *id*
## to derive a name.
## to derive a name. Upon adding a filter to a stream, if neither
## ``path`` nor ``path_func`` is explicitly set by them, then
## this function is used as the ``path_func``.
##
## id: The ID associated with the log stream.
##
@ -143,7 +149,9 @@ export {
## to compute the string dynamically. It is ok to return
## different strings for separate calls, but be careful: it's
## easy to flood the disk by returning a new string for each
## connection.
## connection. Upon adding a filter to a stream, if neither
## ``path`` nor ``path_func`` is explicitly set by them, then
## :bro:see:`default_path_func` is used.
##
## id: The ID associated with the log stream.
##
@ -379,6 +387,8 @@ export {
global active_streams: table[ID] of Stream = table();
}
global all_streams: table[ID] of Stream = table();
# We keep a script-level copy of all filters so that we can manipulate them.
global filters: table[ID, string] of Filter;
@ -463,6 +473,7 @@ function create_stream(id: ID, stream: Stream) : bool
return F;
active_streams[id] = stream;
all_streams[id] = stream;
return add_default_filter(id);
}
@ -470,6 +481,7 @@ function create_stream(id: ID, stream: Stream) : bool
function remove_stream(id: ID) : bool
{
delete active_streams[id];
delete all_streams[id];
return __remove_stream(id);
}
@ -482,10 +494,12 @@ function disable_stream(id: ID) : bool
function add_filter(id: ID, filter: Filter) : bool
{
# This is a work-around for the fact that we can't forward-declare
# the default_path_func and then use it as &default in the record
# definition.
if ( ! filter?$path_func )
local stream = all_streams[id];
if ( stream?$path && ! filter?$path )
filter$path = stream$path;
if ( ! filter?$path && ! filter?$path_func )
filter$path_func = default_path_func;
filters[id, filter$name] = filter;

View file

@ -37,6 +37,8 @@ export {
user: string;
## The remote host to which to transfer logs.
host: string;
## The port to connect to. Defaults to 22
host_port: count &default=22;
## The path/directory on the remote host to send logs.
path: string;
};
@ -63,8 +65,8 @@ function sftp_postprocessor(info: Log::RotationInfo): bool
{
local dst = fmt("%s/%s.%s.log", d$path, info$path,
strftime(Log::sftp_rotation_date_format, info$open));
command += fmt("echo put %s %s | sftp -b - %s@%s;", info$fname, dst,
d$user, d$host);
command += fmt("echo put %s %s | sftp -P %d -b - %s@%s;", info$fname, dst,
d$host_port, d$user, d$host);
}
command += fmt("/bin/rm %s", info$fname);

View file

@ -21,7 +21,7 @@ export {
## underscores and using leading capitals on each word except for
## abbreviations which are kept in all capitals. For example,
## SSH::Password_Guessing is for hosts that have crossed a threshold of
## heuristically determined failed SSH logins.
## failed SSH logins.
type Type: enum {
## Notice reporting a count of how often a notice occurred.
Tally,
@ -349,9 +349,9 @@ function log_mailing_postprocessor(info: Log::RotationInfo): bool
event bro_init() &priority=5
{
Log::create_stream(Notice::LOG, [$columns=Info, $ev=log_notice]);
Log::create_stream(Notice::LOG, [$columns=Info, $ev=log_notice, $path="notice"]);
Log::create_stream(Notice::ALARM_LOG, [$columns=Notice::Info]);
Log::create_stream(Notice::ALARM_LOG, [$columns=Notice::Info, $path="notice_alarm"]);
# If Bro is configured for mailing notices, set up mailing for alarms.
# Make sure that this alarm log is also output as text so that it can
# be packaged up and emailed later.

View file

@ -294,7 +294,7 @@ global current_conn: connection;
event bro_init() &priority=5
{
Log::create_stream(Weird::LOG, [$columns=Info, $ev=log_weird]);
Log::create_stream(Weird::LOG, [$columns=Info, $ev=log_weird, $path="weird"]);
}
function flow_id_string(src: addr, dst: addr): string

View file

@ -159,7 +159,7 @@ event filter_change_tracking()
event bro_init() &priority=5
{
Log::create_stream(PacketFilter::LOG, [$columns=Info]);
Log::create_stream(PacketFilter::LOG, [$columns=Info, $path="packet_filter"]);
# Preverify the capture and restrict filters to give more granular failure messages.
for ( id in capture_filters )

View file

@ -45,7 +45,7 @@ export {
event bro_init() &priority=5
{
Log::create_stream(Reporter::LOG, [$columns=Info]);
Log::create_stream(Reporter::LOG, [$columns=Info, $path="reporter"]);
}
event reporter_info(t: time, msg: string, location: string) &priority=-5

View file

@ -142,7 +142,7 @@ global did_sig_log: set[string] &read_expire = 1 hr;
event bro_init()
{
Log::create_stream(Signatures::LOG, [$columns=Info, $ev=log_signature]);
Log::create_stream(Signatures::LOG, [$columns=Info, $ev=log_signature, $path="signatures"]);
}
# Returns true if the given signature has already been triggered for the given
@ -277,7 +277,7 @@ event signature_match(state: signature_state, msg: string, data: string)
orig, sig_id, hcount);
Log::write(Signatures::LOG,
[$note=Multiple_Sig_Responders,
[$ts=network_time(), $note=Multiple_Sig_Responders,
$src_addr=orig, $sig_id=sig_id, $event_msg=msg,
$host_count=hcount, $sub_msg=horz_scan_msg]);

View file

@ -105,7 +105,7 @@ export {
event bro_init() &priority=5
{
Log::create_stream(Software::LOG, [$columns=Info, $ev=log_software]);
Log::create_stream(Software::LOG, [$columns=Info, $ev=log_software, $path="software"]);
}
type Description: record {

View file

@ -89,7 +89,7 @@ redef likely_server_ports += { ayiya_ports, teredo_ports, gtpv1_ports };
event bro_init() &priority=5
{
Log::create_stream(Tunnel::LOG, [$columns=Info]);
Log::create_stream(Tunnel::LOG, [$columns=Info, $path="tunnel"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_AYIYA, ayiya_ports);
Analyzer::register_for_ports(Analyzer::ANALYZER_TEREDO, teredo_ports);

View file

@ -440,6 +440,7 @@ type NetStats: record {
## packet capture system, this value may not be available and will then
## be always set to zero.
pkts_link: count &default=0;
bytes_recvd: count &default=0; ##< Bytes received by Bro.
};
## Statistics about Bro's resource consumption.
@ -928,7 +929,7 @@ const tcp_storm_interarrival_thresh = 1 sec &redef;
## seeing our peer's ACKs. Set to zero to turn off this determination.
##
## .. bro:see:: tcp_max_above_hole_without_any_acks tcp_excessive_data_without_further_acks
const tcp_max_initial_window = 4096 &redef;
const tcp_max_initial_window = 16384 &redef;
## If we're not seeing our peer's ACKs, the maximum volume of data above a
## sequence hole that we'll tolerate before assuming that there's been a packet
@ -936,7 +937,7 @@ const tcp_max_initial_window = 4096 &redef;
## don't ever give up.
##
## .. bro:see:: tcp_max_initial_window tcp_excessive_data_without_further_acks
const tcp_max_above_hole_without_any_acks = 4096 &redef;
const tcp_max_above_hole_without_any_acks = 16384 &redef;
## If we've seen this much data without any of it being acked, we give up
## on that connection to avoid memory exhaustion due to buffering all that
@ -2215,6 +2216,41 @@ export {
const heartbeat_interval = 1.0 secs &redef;
}
module SSH;
export {
## The client and server each have some preferences for the algorithms used
## in each direction.
type Algorithm_Prefs: record {
## The algorithm preferences for client to server communication
client_to_server: vector of string &optional;
## The algorithm preferences for server to client communication
server_to_client: vector of string &optional;
};
## This record lists the preferences of an SSH endpoint for
## algorithm selection. During the initial :abbr:`SSH (Secure Shell)`
## key exchange, each endpoint lists the algorithms
## that it supports, in order of preference. See
## :rfc:`4253#section-7.1` for details.
type Capabilities: record {
## Key exchange algorithms
kex_algorithms: string_vec;
## The algorithms supported for the server host key
server_host_key_algorithms: string_vec;
## Symmetric encryption algorithm preferences
encryption_algorithms: Algorithm_Prefs;
## Symmetric MAC algorithm preferences
mac_algorithms: Algorithm_Prefs;
## Compression algorithm preferences
compression_algorithms: Algorithm_Prefs;
## Language preferences
languages: Algorithm_Prefs &optional;
## Are these the capabilities of the server?
is_server: bool;
};
}
module GLOBAL;
## An NTP message.
@ -2774,19 +2810,20 @@ export {
module X509;
export {
type Certificate: record {
version: count; ##< Version number.
serial: string; ##< Serial number.
subject: string; ##< Subject.
issuer: string; ##< Issuer.
not_valid_before: time; ##< Timestamp before when certificate is not valid.
not_valid_after: time; ##< Timestamp after when certificate is not valid.
key_alg: string; ##< Name of the key algorithm
sig_alg: string; ##< Name of the signature algorithm
key_type: string &optional; ##< Key type, if key parseable by openssl (either rsa, dsa or ec)
key_length: count &optional; ##< Key length in bits
exponent: string &optional; ##< Exponent, if RSA-certificate
curve: string &optional; ##< Curve, if EC-certificate
} &log;
version: count &log; ##< Version number.
serial: string &log; ##< Serial number.
subject: string &log; ##< Subject.
issuer: string &log; ##< Issuer.
cn: string &optional; ##< Last (most specific) common name.
not_valid_before: time &log; ##< Timestamp before when certificate is not valid.
not_valid_after: time &log; ##< Timestamp after when certificate is not valid.
key_alg: string &log; ##< Name of the key algorithm
sig_alg: string &log; ##< Name of the signature algorithm
key_type: string &optional &log; ##< Key type, if key parseable by openssl (either rsa, dsa or ec)
key_length: count &optional &log; ##< Key length in bits
exponent: string &optional &log; ##< Exponent, if RSA-certificate
curve: string &optional &log; ##< Curve, if EC-certificate
};
type Extension: record {
name: string; ##< Long name of extension. oid if name not known
@ -2847,7 +2884,44 @@ export {
attributes : RADIUS::Attributes &optional;
};
}
module GLOBAL;
module RDP;
export {
type RDP::EarlyCapabilityFlags: record {
support_err_info_pdu: bool;
want_32bpp_session: bool;
support_statusinfo_pdu: bool;
strong_asymmetric_keys: bool;
support_monitor_layout_pdu: bool;
support_netchar_autodetect: bool;
support_dynvc_gfx_protocol: bool;
support_dynamic_time_zone: bool;
support_heartbeat_pdu: bool;
};
type RDP::ClientCoreData: record {
version_major: count;
version_minor: count;
desktop_width: count;
desktop_height: count;
color_depth: count;
sas_sequence: count;
keyboard_layout: count;
client_build: count;
client_name: string;
keyboard_type: count;
keyboard_sub: count;
keyboard_function_key: count;
ime_file_name: string;
post_beta2_color_depth: count &optional;
client_product_id: string &optional;
serial_number: count &optional;
high_color_depth: count &optional;
supported_color_depths: count &optional;
ec_flags: RDP::EarlyCapabilityFlags &optional;
dig_product_id: string &optional;
};
}
@load base/bif/plugins/Bro_SNMP.types.bif
@ -3315,6 +3389,11 @@ const forward_remote_events = F &redef;
## more sophisticated script-level communication framework.
const forward_remote_state_changes = F &redef;
## The number of IO chunks allowed to be buffered between the child
## and parent process of remote communication before Bro starts dropping
## connections to remote peers in an attempt to catch up.
const chunked_io_buffer_soft_cap = 800000 &redef;
## Place-holder constant indicating "no peer".
const PEER_ID_NONE = 0;
@ -3540,6 +3619,7 @@ const bits_per_uid: count = 96 &redef;
# Load these frameworks here because they use fairly deep integration with
# BiFs and script-land defined types.
@load base/frameworks/broker
@load base/frameworks/logging
@load base/frameworks/input
@load base/frameworks/analyzer

View file

@ -50,6 +50,7 @@
@load base/protocols/mysql
@load base/protocols/pop3
@load base/protocols/radius
@load base/protocols/rdp
@load base/protocols/snmp
@load base/protocols/smtp
@load base/protocols/socks

View file

@ -50,7 +50,7 @@ event ChecksumOffloading::check()
bad_checksum_msg += "UDP";
}
local message = fmt("Your %s invalid %s checksums, most likely from NIC checksum offloading.", packet_src, bad_checksum_msg);
local message = fmt("Your %s invalid %s checksums, most likely from NIC checksum offloading. By default, packets with invalid checksums are discarded by Bro unless using the -C command-line option or toggling the 'ignore_checksums' variable. Alternatively, disable checksum offloading by the network adapter to ensure Bro analyzes the actual checksums that are transmitted.", packet_src, bad_checksum_msg);
Reporter::warning(message);
done = T;
}

View file

@ -2,3 +2,4 @@
@load ./contents
@load ./inactivity
@load ./polling
@load ./thresholds

View file

@ -62,6 +62,12 @@ export {
## field will be left empty at all times.
local_orig: bool &log &optional;
## If the connection is responded to locally, this value will be T.
## If it was responded to remotely it will be F. In the case that
## the :bro:id:`Site::local_nets` variable is undefined, this
## field will be left empty at all times.
local_resp: bool &log &optional;
## Indicates the number of bytes missed in content gaps, which
## is representative of packet loss. A value other than zero
## will normally cause protocol analysis to fail but some
@ -121,7 +127,7 @@ redef record connection += {
event bro_init() &priority=5
{
Log::create_stream(Conn::LOG, [$columns=Info, $ev=log_conn]);
Log::create_stream(Conn::LOG, [$columns=Info, $ev=log_conn, $path="conn"]);
}
function conn_state(c: connection, trans: transport_proto): string
@ -201,7 +207,10 @@ function set_conn(c: connection, eoc: bool)
add c$conn$tunnel_parents[c$tunnel[|c$tunnel|-1]$uid];
c$conn$proto=get_port_transport_proto(c$id$resp_p);
if( |Site::local_nets| > 0 )
{
c$conn$local_orig=Site::is_local_addr(c$id$orig_h);
c$conn$local_resp=Site::is_local_addr(c$id$resp_h);
}
if ( eoc )
{

View file

@ -0,0 +1,274 @@
##! Implements a generic API to throw events when a connection crosses a
##! fixed threshold of bytes or packets.
module ConnThreshold;
export {
type Thresholds: record {
orig_byte: set[count] &default=count_set(); ##< current originator byte thresholds we watch for
resp_byte: set[count] &default=count_set(); ##< current responder byte thresholds we watch for
orig_packet: set[count] &default=count_set(); ##< corrent originator packet thresholds we watch for
resp_packet: set[count] &default=count_set(); ##< corrent responder packet thresholds we watch for
};
## Sets a byte threshold for connection sizes, adding it to potentially already existing thresholds.
## conn_bytes_threshold_crossed will be raised for each set threshold.
##
## cid: The connection id.
##
## threshold: Threshold in bytes.
##
## is_orig: If true, threshold is set for bytes from originator, otherwise for bytes from responder.
##
## Returns: T on success, F on failure.
##
## .. bro:see:: bytes_threshold_crossed packets_threshold_crossed set_packets_threshold
## delete_bytes_threshold delete_packets_threshold
global set_bytes_threshold: function(c: connection, threshold: count, is_orig: bool): bool;
## Sets a packet threshold for connection sizes, adding it to potentially already existing thresholds.
## conn_packets_threshold_crossed will be raised for each set threshold.
##
## cid: The connection id.
##
## threshold: Threshold in packets.
##
## is_orig: If true, threshold is set for packets from originator, otherwise for packets from responder.
##
## Returns: T on success, F on failure.
##
## .. bro:see:: bytes_threshold_crossed packets_threshold_crossed set_bytes_threshold
## delete_bytes_threshold delete_packets_threshold
global set_packets_threshold: function(c: connection, threshold: count, is_orig: bool): bool;
## Deletes a byte threshold for connection sizes.
##
## cid: The connection id.
##
## threshold: Threshold in bytes to remove.
##
## is_orig: If true, threshold is removed for packets from originator, otherwhise for packets from responder.
##
## Returns: T on success, F on failure.
##
## .. bro:see:: bytes_threshold_crossed packets_threshold_crossed set_bytes_threshold set_packets_threshold
## delete_packets_threshold
global delete_bytes_threshold: function(c: connection, threshold: count, is_orig: bool): bool;
## Deletes a packet threshold for connection sizes.
##
## cid: The connection id.
##
## threshold: Threshold in packets.
##
## is_orig: If true, threshold is removed for packets from originator, otherwise for packets from responder.
##
## Returns: T on success, F on failure.
##
## .. bro:see:: bytes_threshold_crossed packets_threshold_crossed set_bytes_threshold set_packets_threshold
## delete_bytes_threshold
global delete_packets_threshold: function(c: connection, threshold: count, is_orig: bool): bool;
## Generated for a connection that crossed a set byte threshold
##
## c: the connection
##
## threshold: the threshold that was set
##
## is_orig: True if the threshold was crossed by the originator of the connection
##
## .. bro:see:: packets_threshold_crossed set_bytes_threshold set_packets_threshold
## delete_bytes_threshold delete_packets_threshold
global bytes_threshold_crossed: event(c: connection, threshold: count, is_orig: bool);
## Generated for a connection that crossed a set byte threshold
##
## c: the connection
##
## threshold: the threshold that was set
##
## is_orig: True if the threshold was crossed by the originator of the connection
##
## .. bro:see:: bytes_threshold_crossed set_bytes_threshold set_packets_threshold
## delete_bytes_threshold delete_packets_threshold
global packets_threshold_crossed: event(c: connection, threshold: count, is_orig: bool);
}
redef record connection += {
thresholds: ConnThreshold::Thresholds &optional;
};
function set_conn(c: connection)
{
if ( c?$thresholds )
return;
c$thresholds = Thresholds();
}
function find_min_threshold(t: set[count]): count
{
if ( |t| == 0 )
return 0;
local first = T;
local min: count = 0;
for ( i in t )
{
if ( first )
{
min = i;
first = F;
}
else
{
if ( i < min )
min = i;
}
}
return min;
}
function set_current_threshold(c: connection, bytes: bool, is_orig: bool): bool
{
local t: count = 0;
local cur: count = 0;
if ( bytes && is_orig )
{
t = find_min_threshold(c$thresholds$orig_byte);
cur = get_current_conn_bytes_threshold(c$id, is_orig);
}
else if ( bytes && ! is_orig )
{
t = find_min_threshold(c$thresholds$resp_byte);
cur = get_current_conn_bytes_threshold(c$id, is_orig);
}
else if ( ! bytes && is_orig )
{
t = find_min_threshold(c$thresholds$orig_packet);
cur = get_current_conn_packets_threshold(c$id, is_orig);
}
else if ( ! bytes && ! is_orig )
{
t = find_min_threshold(c$thresholds$resp_packet);
cur = get_current_conn_packets_threshold(c$id, is_orig);
}
if ( t == cur )
return T;
if ( bytes && is_orig )
return set_current_conn_bytes_threshold(c$id, t, T);
else if ( bytes && ! is_orig )
return set_current_conn_bytes_threshold(c$id, t, F);
else if ( ! bytes && is_orig )
return set_current_conn_packets_threshold(c$id, t, T);
else if ( ! bytes && ! is_orig )
return set_current_conn_packets_threshold(c$id, t, F);
}
function set_bytes_threshold(c: connection, threshold: count, is_orig: bool): bool
{
set_conn(c);
if ( threshold == 0 )
return F;
if ( is_orig )
add c$thresholds$orig_byte[threshold];
else
add c$thresholds$resp_byte[threshold];
return set_current_threshold(c, T, is_orig);
}
function set_packets_threshold(c: connection, threshold: count, is_orig: bool): bool
{
set_conn(c);
if ( threshold == 0 )
return F;
if ( is_orig )
add c$thresholds$orig_packet[threshold];
else
add c$thresholds$resp_packet[threshold];
return set_current_threshold(c, F, is_orig);
}
function delete_bytes_threshold(c: connection, threshold: count, is_orig: bool): bool
{
set_conn(c);
if ( is_orig && threshold in c$thresholds$orig_byte )
{
delete c$thresholds$orig_byte[threshold];
set_current_threshold(c, T, is_orig);
return T;
}
else if ( ! is_orig && threshold in c$thresholds$resp_byte )
{
delete c$thresholds$resp_byte[threshold];
set_current_threshold(c, T, is_orig);
return T;
}
return F;
}
function delete_packets_threshold(c: connection, threshold: count, is_orig: bool): bool
{
set_conn(c);
if ( is_orig && threshold in c$thresholds$orig_packet )
{
delete c$thresholds$orig_packet[threshold];
set_current_threshold(c, F, is_orig);
return T;
}
else if ( ! is_orig && threshold in c$thresholds$resp_packet )
{
delete c$thresholds$resp_packet[threshold];
set_current_threshold(c, F, is_orig);
return T;
}
return F;
}
event conn_bytes_threshold_crossed(c: connection, threshold: count, is_orig: bool) &priority=5
{
if ( is_orig && threshold in c$thresholds$orig_byte )
{
delete c$thresholds$orig_byte[threshold];
event ConnThreshold::bytes_threshold_crossed(c, threshold, is_orig);
}
else if ( ! is_orig && threshold in c$thresholds$resp_byte )
{
delete c$thresholds$resp_byte[threshold];
event ConnThreshold::bytes_threshold_crossed(c, threshold, is_orig);
}
set_current_threshold(c, T, is_orig);
}
event conn_packets_threshold_crossed(c: connection, threshold: count, is_orig: bool) &priority=5
{
if ( is_orig && threshold in c$thresholds$orig_packet )
{
delete c$thresholds$orig_packet[threshold];
event ConnThreshold::packets_threshold_crossed(c, threshold, is_orig);
}
else if ( ! is_orig && threshold in c$thresholds$resp_packet )
{
delete c$thresholds$resp_packet[threshold];
event ConnThreshold::packets_threshold_crossed(c, threshold, is_orig);
}
set_current_threshold(c, F, is_orig);
}

View file

@ -49,7 +49,7 @@ redef likely_server_ports += { 67/udp };
event bro_init() &priority=5
{
Log::create_stream(DHCP::LOG, [$columns=Info, $ev=log_dhcp]);
Log::create_stream(DHCP::LOG, [$columns=Info, $ev=log_dhcp, $path="dhcp"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_DHCP, ports);
}

View file

@ -36,7 +36,7 @@ redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(DNP3::LOG, [$columns=Info, $ev=log_dnp3]);
Log::create_stream(DNP3::LOG, [$columns=Info, $ev=log_dnp3, $path="dnp3"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_DNP3_TCP, ports);
}

View file

@ -150,7 +150,7 @@ redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(DNS::LOG, [$columns=Info, $ev=log_dns]);
Log::create_stream(DNS::LOG, [$columns=Info, $ev=log_dns, $path="dns"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_DNS, ports);
}
@ -305,6 +305,9 @@ hook DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string)
if ( ans$answer_type == DNS_ANS )
{
if ( ! c$dns?$query )
c$dns$query = ans$query;
c$dns$AA = msg$AA;
c$dns$RA = msg$RA;

View file

@ -11,13 +11,13 @@
##! GridFTP data channels are identified by a heuristic that relies on
##! the fact that default settings for GridFTP clients typically
##! mutually authenticate the data channel with TLS/SSL and negotiate a
##! NULL bulk cipher (no encryption). Connections with those
##! attributes are then polled for two minutes with decreasing frequency
##! to check if the transfer sizes are large enough to indicate a
##! GridFTP data channel that would be undesirable to analyze further
##! (e.g. stop TCP reassembly). A side effect is that true connection
##! sizes are not logged, but at the benefit of saving CPU cycles that
##! would otherwise go to analyzing the large (and likely benign) connections.
##! NULL bulk cipher (no encryption). Connections with those attributes
##! are marked as GridFTP if the data transfer within the first two minutes
##! is big enough to indicate a GripFTP data channel that would be
##! undesirable to analyze further (e.g. stop TCP reassembly). A side
##! effect is that true connection sizes are not logged, but at the benefit
##! of saving CPU cycles that would otherwise go to analyzing the large
##! (and likely benign) connections.
@load ./info
@load ./main
@ -32,23 +32,14 @@ export {
## GridFTP data channel.
const size_threshold = 1073741824 &redef;
## Max number of times to check whether a connection's size exceeds the
## Time during which we check whether a connection's size exceeds the
## :bro:see:`GridFTP::size_threshold`.
const max_poll_count = 15 &redef;
const max_time = 2 min &redef;
## Whether to skip further processing of the GridFTP data channel once
## detected, which may help performance.
const skip_data = T &redef;
## Base amount of time between checking whether a GridFTP data connection
## has transferred more than :bro:see:`GridFTP::size_threshold` bytes.
const poll_interval = 1sec &redef;
## The amount of time the base :bro:see:`GridFTP::poll_interval` is
## increased by each poll interval. Can be used to make more frequent
## checks at the start of a connection and gradually slow down.
const poll_interval_increase = 1sec &redef;
## Raised when a GridFTP data channel is detected.
##
## c: The connection pertaining to the GridFTP data channel.
@ -79,23 +70,27 @@ event ftp_request(c: connection, command: string, arg: string) &priority=4
c$ftp$last_auth_requested = arg;
}
function size_callback(c: connection, cnt: count): interval
{
if ( c$orig$size > size_threshold || c$resp$size > size_threshold )
event ConnThreshold::bytes_threshold_crossed(c: connection, threshold: count, is_orig: bool)
{
if ( threshold < size_threshold || "gridftp-data" in c$service || c$duration > max_time )
return;
add c$service["gridftp-data"];
event GridFTP::data_channel_detected(c);
if ( skip_data )
skip_further_processing(c$id);
return -1sec;
}
if ( cnt >= max_poll_count )
return -1sec;
return poll_interval + poll_interval_increase * cnt;
event gridftp_possibility_timeout(c: connection)
{
# only remove if we did not already detect it and the connection
# is not yet at its end.
if ( "gridftp-data" !in c$service && ! c$conn?$service )
{
ConnThreshold::delete_bytes_threshold(c, size_threshold, T);
ConnThreshold::delete_bytes_threshold(c, size_threshold, F);
}
}
event ssl_established(c: connection) &priority=5
@ -118,5 +113,9 @@ event ssl_established(c: connection) &priority=-3
# By default GridFTP data channels do mutual authentication and
# negotiate a cipher suite with a NULL bulk cipher.
if ( data_channel_initial_criteria(c) )
ConnPolling::watch(c, size_callback, 0, 0secs);
{
ConnThreshold::set_bytes_threshold(c, size_threshold, T);
ConnThreshold::set_bytes_threshold(c, size_threshold, F);
schedule max_time { gridftp_possibility_timeout(c) };
}
}

View file

@ -52,7 +52,7 @@ redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(FTP::LOG, [$columns=Info, $ev=log_ftp]);
Log::create_stream(FTP::LOG, [$columns=Info, $ev=log_ftp, $path="ftp"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_FTP, ports);
}

View file

@ -135,7 +135,7 @@ redef likely_server_ports += { ports };
# Initialize the HTTP logging stream and ports.
event bro_init() &priority=5
{
Log::create_stream(HTTP::LOG, [$columns=Info, $ev=log_http]);
Log::create_stream(HTTP::LOG, [$columns=Info, $ev=log_http, $path="http"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_HTTP, ports);
}

View file

@ -43,7 +43,7 @@ redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(IRC::LOG, [$columns=Info, $ev=irc_log]);
Log::create_stream(IRC::LOG, [$columns=Info, $ev=irc_log, $path="irc"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_IRC, ports);
}

View file

@ -34,7 +34,7 @@ redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(Modbus::LOG, [$columns=Info, $ev=log_modbus]);
Log::create_stream(Modbus::LOG, [$columns=Info, $ev=log_modbus, $path="modbus"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_MODBUS, ports);
}

View file

@ -39,7 +39,7 @@ const ports = { 1434/tcp, 3306/tcp };
event bro_init() &priority=5
{
Log::create_stream(mysql::LOG, [$columns=Info, $ev=log_mysql]);
Log::create_stream(mysql::LOG, [$columns=Info, $ev=log_mysql, $path="mysql"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_MYSQL, ports);
}

View file

@ -59,7 +59,7 @@ const ports = { 1812/udp };
event bro_init() &priority=5
{
Log::create_stream(RADIUS::LOG, [$columns=Info, $ev=log_radius]);
Log::create_stream(RADIUS::LOG, [$columns=Info, $ev=log_radius, $path="radius"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_RADIUS, ports);
}

View file

@ -0,0 +1,3 @@
@load ./consts
@load ./main
@load-sigs ./dpd.sig

View file

@ -0,0 +1,323 @@
module RDP;
export {
# http://www.c-amie.co.uk/technical/mstsc-versions/
const builds = {
[0419] = "RDP 4.0",
[2195] = "RDP 5.0",
[2221] = "RDP 5.0",
[2600] = "RDP 5.1",
[3790] = "RDP 5.2",
[6000] = "RDP 6.0",
[6001] = "RDP 6.1",
[6002] = "RDP 6.2",
[7600] = "RDP 7.0",
[7601] = "RDP 7.1",
[9200] = "RDP 8.0",
[9600] = "RDP 8.1",
[25189] = "RDP 8.0 (Mac)",
[25282] = "RDP 8.0 (Mac)"
} &default = function(n: count): string { return fmt("client_build-%d", n); };
const security_protocols = {
[0x00] = "RDP",
[0x01] = "SSL",
[0x02] = "HYBRID",
[0x08] = "HYBRID_EX"
} &default = function(n: count): string { return fmt("security_protocol-%d", n); };
const failure_codes = {
[0x01] = "SSL_REQUIRED_BY_SERVER",
[0x02] = "SSL_NOT_ALLOWED_BY_SERVER",
[0x03] = "SSL_CERT_NOT_ON_SERVER",
[0x04] = "INCONSISTENT_FLAGS",
[0x05] = "HYBRID_REQUIRED_BY_SERVER",
[0x06] = "SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER"
} &default = function(n: count): string { return fmt("failure_code-%d", n); };
const cert_types = {
[1] = "RSA",
[2] = "X.509"
} &default = function(n: count): string { return fmt("cert_type-%d", n); };
const encryption_methods = {
[0] = "None",
[1] = "40bit",
[2] = "128bit",
[8] = "56bit",
[10] = "FIPS"
} &default = function(n: count): string { return fmt("encryption_method-%d", n); };
const encryption_levels = {
[0] = "None",
[1] = "Low",
[2] = "Client compatible",
[3] = "High",
[4] = "FIPS"
} &default = function(n: count): string { return fmt("encryption_level-%d", n); };
const high_color_depths = {
[0x0004] = "4bit",
[0x0008] = "8bit",
[0x000F] = "15bit",
[0x0010] = "16bit",
[0x0018] = "24bit"
} &default = function(n: count): string { return fmt("high_color_depth-%d", n); };
const color_depths = {
[0x0001] = "24bit",
[0x0002] = "16bit",
[0x0004] = "15bit",
[0x0008] = "32bit"
} &default = function(n: count): string { return fmt("color_depth-%d", n); };
const results = {
[0] = "Success",
[1] = "User rejected",
[2] = "Resources not available",
[3] = "Rejected for symmetry breaking",
[4] = "Locked conference",
} &default = function(n: count): string { return fmt("result-%d", n); };
# http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
const languages = {
[1078] = "Afrikaans - South Africa",
[1052] = "Albanian - Albania",
[1156] = "Alsatian",
[1118] = "Amharic - Ethiopia",
[1025] = "Arabic - Saudi Arabia",
[5121] = "Arabic - Algeria",
[15361] = "Arabic - Bahrain",
[3073] = "Arabic - Egypt",
[2049] = "Arabic - Iraq",
[11265] = "Arabic - Jordan",
[13313] = "Arabic - Kuwait",
[12289] = "Arabic - Lebanon",
[4097] = "Arabic - Libya",
[6145] = "Arabic - Morocco",
[8193] = "Arabic - Oman",
[16385] = "Arabic - Qatar",
[10241] = "Arabic - Syria",
[7169] = "Arabic - Tunisia",
[14337] = "Arabic - U.A.E.",
[9217] = "Arabic - Yemen",
[1067] = "Armenian - Armenia",
[1101] = "Assamese",
[2092] = "Azeri (Cyrillic)",
[1068] = "Azeri (Latin)",
[1133] = "Bashkir",
[1069] = "Basque",
[1059] = "Belarusian",
[1093] = "Bengali (India)",
[2117] = "Bengali (Bangladesh)",
[5146] = "Bosnian (Bosnia/Herzegovina)",
[1150] = "Breton",
[1026] = "Bulgarian",
[1109] = "Burmese",
[1027] = "Catalan",
[1116] = "Cherokee - United States",
[2052] = "Chinese - People's Republic of China",
[4100] = "Chinese - Singapore",
[1028] = "Chinese - Taiwan",
[3076] = "Chinese - Hong Kong SAR",
[5124] = "Chinese - Macao SAR",
[1155] = "Corsican",
[1050] = "Croatian",
[4122] = "Croatian (Bosnia/Herzegovina)",
[1029] = "Czech",
[1030] = "Danish",
[1164] = "Dari",
[1125] = "Divehi",
[1043] = "Dutch - Netherlands",
[2067] = "Dutch - Belgium",
[1126] = "Edo",
[1033] = "English - United States",
[2057] = "English - United Kingdom",
[3081] = "English - Australia",
[10249] = "English - Belize",
[4105] = "English - Canada",
[9225] = "English - Caribbean",
[15369] = "English - Hong Kong SAR",
[16393] = "English - India",
[14345] = "English - Indonesia",
[6153] = "English - Ireland",
[8201] = "English - Jamaica",
[17417] = "English - Malaysia",
[5129] = "English - New Zealand",
[13321] = "English - Philippines",
[18441] = "English - Singapore",
[7177] = "English - South Africa",
[11273] = "English - Trinidad",
[12297] = "English - Zimbabwe",
[1061] = "Estonian",
[1080] = "Faroese",
[1065] = "Farsi",
[1124] = "Filipino",
[1035] = "Finnish",
[1036] = "French - France",
[2060] = "French - Belgium",
[11276] = "French - Cameroon",
[3084] = "French - Canada",
[9228] = "French - Democratic Rep. of Congo",
[12300] = "French - Cote d'Ivoire",
[15372] = "French - Haiti",
[5132] = "French - Luxembourg",
[13324] = "French - Mali",
[6156] = "French - Monaco",
[14348] = "French - Morocco",
[58380] = "French - North Africa",
[8204] = "French - Reunion",
[10252] = "French - Senegal",
[4108] = "French - Switzerland",
[7180] = "French - West Indies",
[1122] = "French - West Indies",
[1127] = "Fulfulde - Nigeria",
[1071] = "FYRO Macedonian",
[1110] = "Galician",
[1079] = "Georgian",
[1031] = "German - Germany",
[3079] = "German - Austria",
[5127] = "German - Liechtenstein",
[4103] = "German - Luxembourg",
[2055] = "German - Switzerland",
[1032] = "Greek",
[1135] = "Greenlandic",
[1140] = "Guarani - Paraguay",
[1095] = "Gujarati",
[1128] = "Hausa - Nigeria",
[1141] = "Hawaiian - United States",
[1037] = "Hebrew",
[1081] = "Hindi",
[1038] = "Hungarian",
[1129] = "Ibibio - Nigeria",
[1039] = "Icelandic",
[1136] = "Igbo - Nigeria",
[1057] = "Indonesian",
[1117] = "Inuktitut",
[2108] = "Irish",
[1040] = "Italian - Italy",
[2064] = "Italian - Switzerland",
[1041] = "Japanese",
[1158] = "K'iche",
[1099] = "Kannada",
[1137] = "Kanuri - Nigeria",
[2144] = "Kashmiri",
[1120] = "Kashmiri (Arabic)",
[1087] = "Kazakh",
[1107] = "Khmer",
[1159] = "Kinyarwanda",
[1111] = "Konkani",
[1042] = "Korean",
[1088] = "Kyrgyz (Cyrillic)",
[1108] = "Lao",
[1142] = "Latin",
[1062] = "Latvian",
[1063] = "Lithuanian",
[1134] = "Luxembourgish",
[1086] = "Malay - Malaysia",
[2110] = "Malay - Brunei Darussalam",
[1100] = "Malayalam",
[1082] = "Maltese",
[1112] = "Manipuri",
[1153] = "Maori - New Zealand",
[1146] = "Mapudungun",
[1102] = "Marathi",
[1148] = "Mohawk",
[1104] = "Mongolian (Cyrillic)",
[2128] = "Mongolian (Mongolian)",
[1121] = "Nepali",
[2145] = "Nepali - India",
[1044] = "Norwegian (Bokmål)",
[2068] = "Norwegian (Nynorsk)",
[1154] = "Occitan",
[1096] = "Oriya",
[1138] = "Oromo",
[1145] = "Papiamentu",
[1123] = "Pashto",
[1045] = "Polish",
[1046] = "Portuguese - Brazil",
[2070] = "Portuguese - Portugal",
[1094] = "Punjabi",
[2118] = "Punjabi (Pakistan)",
[1131] = "Quecha - Bolivia",
[2155] = "Quecha - Ecuador",
[3179] = "Quecha - Peru CB",
[1047] = "Rhaeto-Romanic",
[1048] = "Romanian",
[2072] = "Romanian - Moldava",
[1049] = "Russian",
[2073] = "Russian - Moldava",
[1083] = "Sami (Lappish)",
[1103] = "Sanskrit",
[1084] = "Scottish Gaelic",
[1132] = "Sepedi",
[3098] = "Serbian (Cyrillic)",
[2074] = "Serbian (Latin)",
[1113] = "Sindhi - India",
[2137] = "Sindhi - Pakistan",
[1115] = "Sinhalese - Sri Lanka",
[1051] = "Slovak",
[1060] = "Slovenian",
[1143] = "Somali",
[1070] = "Sorbian",
[3082] = "Spanish - Spain (Modern Sort)",
[1034] = "Spanish - Spain (Traditional Sort)",
[11274] = "Spanish - Argentina",
[16394] = "Spanish - Bolivia",
[13322] = "Spanish - Chile",
[9226] = "Spanish - Colombia",
[5130] = "Spanish - Costa Rica",
[7178] = "Spanish - Dominican Republic",
[12298] = "Spanish - Ecuador",
[17418] = "Spanish - El Salvador",
[4106] = "Spanish - Guatemala",
[18442] = "Spanish - Honduras",
[22538] = "Spanish - Latin America",
[2058] = "Spanish - Mexico",
[19466] = "Spanish - Nicaragua",
[6154] = "Spanish - Panama",
[15370] = "Spanish - Paraguay",
[10250] = "Spanish - Peru",
[20490] = "Spanish - Puerto Rico",
[21514] = "Spanish - United States",
[14346] = "Spanish - Uruguay",
[8202] = "Spanish - Venezuela",
[1072] = "Sutu",
[1089] = "Swahili",
[1053] = "Swedish",
[2077] = "Swedish - Finland",
[1114] = "Syriac",
[1064] = "Tajik",
[1119] = "Tamazight (Arabic)",
[2143] = "Tamazight (Latin)",
[1097] = "Tamil",
[1092] = "Tatar",
[1098] = "Telugu",
[1054] = "Thai",
[2129] = "Tibetan - Bhutan",
[1105] = "Tibetan - People's Republic of China",
[2163] = "Tigrigna - Eritrea",
[1139] = "Tigrigna - Ethiopia",
[1073] = "Tsonga",
[1074] = "Tswana",
[1055] = "Turkish",
[1090] = "Turkmen",
[1152] = "Uighur - China",
[1058] = "Ukrainian",
[1056] = "Urdu",
[2080] = "Urdu - India",
[2115] = "Uzbek (Cyrillic)",
[1091] = "Uzbek (Latin)",
[1075] = "Venda",
[1066] = "Vietnamese",
[1106] = "Welsh",
[1160] = "Wolof",
[1076] = "Xhosa",
[1157] = "Yakut",
[1144] = "Yi",
[1085] = "Yiddish",
[1130] = "Yoruba",
[1077] = "Zulu",
[1279] = "HID (Human Interface Device)",
} &default = function(n: count): string { return fmt("keyboard-%d", n); };
}

View file

@ -0,0 +1,12 @@
signature dpd_rdp_client {
ip-proto == tcp
# Client request
payload /.*(Cookie: mstshash\=|Duca.*(rdpdr|rdpsnd|drdynvc|cliprdr))/
requires-reverse-signature dpd_rdp_server
enable "rdp"
}
signature dpd_rdp_server {
ip-proto == tcp
payload /(.{5}\xd0|.*McDn)/
}

View file

@ -0,0 +1,269 @@
##! Implements base functionality for RDP analysis. Generates the rdp.log file.
@load ./consts
module RDP;
export {
redef enum Log::ID += { LOG };
type Info: record {
## Timestamp for when the event happened.
ts: time &log;
## Unique ID for the connection.
uid: string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id: conn_id &log;
## Cookie value used by the client machine.
## This is typically a username.
cookie: string &log &optional;
## Status result for the connection. It's a mix between
## RDP negotation failure messages and GCC server create
## response messages.
result: string &log &optional;
## Security protocol chosen by the server.
security_protocol: string &log &optional;
## Keyboard layout (language) of the client machine.
keyboard_layout: string &log &optional;
## RDP client version used by the client machine.
client_build: string &log &optional;
## Name of the client machine.
client_name: string &log &optional;
## Product ID of the client machine.
client_dig_product_id: string &log &optional;
## Desktop width of the client machine.
desktop_width: count &log &optional;
## Desktop height of the client machine.
desktop_height: count &log &optional;
## The color depth requested by the client in
## the high_color_depth field.
requested_color_depth: string &log &optional;
## If the connection is being encrypted with native
## RDP encryption, this is the type of cert
## being used.
cert_type: string &log &optional;
## The number of certs seen. X.509 can transfer an
## entire certificate chain.
cert_count: count &log &default=0;
## Indicates if the provided certificate or certificate
## chain is permanent or temporary.
cert_permanent: bool &log &optional;
## Encryption level of the connection.
encryption_level: string &log &optional;
## Encryption method of the connection.
encryption_method: string &log &optional;
};
## If true, detach the RDP analyzer from the connection to prevent
## continuing to process encrypted traffic.
const disable_analyzer_after_detection = F &redef;
## The amount of time to monitor an RDP session from when it is first
## identified. When this interval is reached, the session is logged.
const rdp_check_interval = 10secs &redef;
## Event that can be handled to access the rdp record as it is sent on
## to the logging framework.
global log_rdp: event(rec: Info);
}
# Internal fields that aren't useful externally
redef record Info += {
## The analyzer ID used for the analyzer instance attached
## to each connection. It is not used for logging since it's a
## meaningless arbitrary number.
analyzer_id: count &optional;
## Track status of logging RDP connections.
done: bool &default=F;
};
redef record connection += {
rdp: Info &optional;
};
const ports = { 3389/tcp };
redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(RDP::LOG, [$columns=RDP::Info, $ev=log_rdp, $path="rdp"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_RDP, ports);
}
function write_log(c: connection)
{
local info = c$rdp;
if ( info$done )
return;
# Mark this record as fully logged and finished.
info$done = T;
# Verify that the RDP session contains
# RDP data before writing it to the log.
if ( info?$cookie || info?$keyboard_layout || info?$result )
Log::write(RDP::LOG, info);
}
event check_record(c: connection)
{
# If the record was logged, then stop processing.
if ( c$rdp$done )
return;
# If the value rdp_check_interval has passed since the
# RDP session was started, then log the record.
local diff = network_time() - c$rdp$ts;
if ( diff > rdp_check_interval )
{
write_log(c);
# Remove the analyzer if it is still attached.
if ( disable_analyzer_after_detection &&
connection_exists(c$id) &&
c$rdp?$analyzer_id )
{
disable_analyzer(c$id, c$rdp$analyzer_id);
}
return;
}
else
{
# If the analyzer is attached and the duration
# to monitor the RDP session was not met, then
# reschedule the logging event.
schedule rdp_check_interval { check_record(c) };
}
}
function set_session(c: connection)
{
if ( ! c?$rdp )
{
c$rdp = [$ts=network_time(),$id=c$id,$uid=c$uid];
# The RDP session is scheduled to be logged from
# the time it is first initiated.
schedule rdp_check_interval { check_record(c) };
}
}
event rdp_connect_request(c: connection, cookie: string) &priority=5
{
set_session(c);
c$rdp$cookie = cookie;
}
event rdp_negotiation_response(c: connection, security_protocol: count) &priority=5
{
set_session(c);
c$rdp$security_protocol = security_protocols[security_protocol];
}
event rdp_negotiation_failure(c: connection, failure_code: count) &priority=5
{
set_session(c);
c$rdp$result = failure_codes[failure_code];
}
event rdp_client_core_data(c: connection, data: RDP::ClientCoreData) &priority=5
{
set_session(c);
c$rdp$keyboard_layout = RDP::languages[data$keyboard_layout];
c$rdp$client_build = RDP::builds[data$client_build];
c$rdp$client_name = data$client_name;
c$rdp$client_dig_product_id = data$dig_product_id;
c$rdp$desktop_width = data$desktop_width;
c$rdp$desktop_height = data$desktop_height;
if ( data?$ec_flags && data$ec_flags$want_32bpp_session )
c$rdp$requested_color_depth = "32bit";
else
c$rdp$requested_color_depth = RDP::high_color_depths[data$high_color_depth];
}
event rdp_gcc_server_create_response(c: connection, result: count) &priority=5
{
set_session(c);
c$rdp$result = RDP::results[result];
}
event rdp_server_security(c: connection, encryption_method: count, encryption_level: count) &priority=5
{
set_session(c);
c$rdp$encryption_method = RDP::encryption_methods[encryption_method];
c$rdp$encryption_level = RDP::encryption_levels[encryption_level];
}
event rdp_server_certificate(c: connection, cert_type: count, permanently_issued: bool) &priority=5
{
set_session(c);
c$rdp$cert_type = RDP::cert_types[cert_type];
# There are no events for proprietary/RSA certs right
# now so we manually count this one.
if ( c$rdp$cert_type == "RSA" )
++c$rdp$cert_count;
c$rdp$cert_permanent = permanently_issued;
}
event rdp_begin_encryption(c: connection, security_protocol: count) &priority=5
{
set_session(c);
if ( ! c$rdp?$result )
{
c$rdp$result = "encrypted";
}
c$rdp$security_protocol = security_protocols[security_protocol];
}
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5
{
if ( c?$rdp && f$source == "RDP" )
{
# Count up X509 certs.
++c$rdp$cert_count;
Files::add_analyzer(f, Files::ANALYZER_X509);
Files::add_analyzer(f, Files::ANALYZER_MD5);
Files::add_analyzer(f, Files::ANALYZER_SHA1);
}
}
event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) &priority=5
{
if ( atype == Analyzer::ANALYZER_RDP )
{
set_session(c);
c$rdp$analyzer_id = aid;
}
}
event protocol_violation(c: connection, atype: Analyzer::Tag, aid: count, reason: string) &priority=5
{
# If a protocol violation occurs, then log the record immediately.
if ( c?$rdp )
write_log(c);
}
event connection_state_remove(c: connection) &priority=-5
{
# If the connection is removed, then log the record immediately.
if ( c?$rdp )
{
write_log(c);
}
}

View file

@ -92,7 +92,7 @@ redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(SMTP::LOG, [$columns=SMTP::Info, $ev=log_smtp]);
Log::create_stream(SMTP::LOG, [$columns=SMTP::Info, $ev=log_smtp, $path="smtp"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_SMTP, ports);
}

View file

@ -66,7 +66,7 @@ redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Analyzer::register_for_ports(Analyzer::ANALYZER_SNMP, ports);
Log::create_stream(SNMP::LOG, [$columns=SNMP::Info, $ev=log_snmp]);
Log::create_stream(SNMP::LOG, [$columns=SNMP::Info, $ev=log_snmp, $path="snmp"]);
}
function init_state(c: connection, h: SNMP::Header): Info

View file

@ -16,8 +16,10 @@ export {
id: conn_id &log;
## Protocol version of SOCKS.
version: count &log;
## Username for the proxy if extracted from the network.
## Username used to request a login to the proxy.
user: string &log &optional;
## Password used to request a login to the proxy.
password: string &log &optional;
## Server status for the attempt at using the proxy.
status: string &log &optional;
## Client requested SOCKS address. Could be an address, a name
@ -41,7 +43,7 @@ redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(SOCKS::LOG, [$columns=Info, $ev=log_socks]);
Log::create_stream(SOCKS::LOG, [$columns=Info, $ev=log_socks, $path="socks"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_SOCKS, ports);
}
@ -91,3 +93,21 @@ event socks_reply(c: connection, version: count, reply: count, sa: SOCKS::Addres
if ( "SOCKS" in c$service )
Log::write(SOCKS::LOG, c$socks);
}
event socks_login_userpass_request(c: connection, user: string, password: string) &priority=5
{
# Authentication only possible with the version 5.
set_session(c, 5);
c$socks$user = user;
c$socks$password = password;
}
event socks_login_userpass_reply(c: connection, code: count) &priority=5
{
# Authentication only possible with the version 5.
set_session(c, 5);
c$socks$status = v5_status[code];
}

View file

@ -1 +0,0 @@
Support for Secure Shell (SSH) protocol analysis.

View file

@ -1,3 +1,2 @@
@load ./main
@load-sigs ./dpd.sig

View file

@ -1,6 +1,6 @@
signature dpd_ssh_client {
ip-proto == tcp
payload /^[sS][sS][hH]-/
payload /^[sS][sS][hH]-[12]\./
requires-reverse-signature dpd_ssh_server
enable "ssh"
tcp-state originator
@ -8,6 +8,6 @@ signature dpd_ssh_client {
signature dpd_ssh_server {
ip-proto == tcp
payload /^[sS][sS][hH]-/
payload /^[sS][sS][hH]-[12]\./
tcp-state responder
}

View file

@ -1,15 +1,5 @@
##! Base SSH analysis script. The heuristic to blindly determine success or
##! failure for SSH connections is implemented here. At this time, it only
##! uses the size of the data being returned from the server to make the
##! heuristic determination about success of the connection.
##! Requires that :bro:id:`use_conn_size_analyzer` is set to T! The heuristic
##! is not attempted if the connection size analyzer isn't enabled.
##! Implements base functionality for SSH analysis. Generates the ssh.log file.
@load base/protocols/conn
@load base/frameworks/notice
@load base/utils/site
@load base/utils/thresholds
@load base/utils/conn-ids
@load base/utils/directions-and-hosts
module SSH;
@ -25,45 +15,63 @@ export {
uid: string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id: conn_id &log;
## Indicates if the login was heuristically guessed to be
## "success", "failure", or "undetermined".
status: string &log &default="undetermined";
## SSH major version (1 or 2)
version: count &log;
## Authentication result (T=success, F=failure, unset=unknown)
auth_success: bool &log &optional;
## Direction of the connection. If the client was a local host
## logging into an external host, this would be OUTBOUND. INBOUND
## would be set for the opposite situation.
# TODO: handle local-local and remote-remote better.
# TODO - handle local-local and remote-remote better.
direction: Direction &log &optional;
## Software string from the client.
## The client's version string
client: string &log &optional;
## Software string from the server.
## The server's version string
server: string &log &optional;
## Indicate if the SSH session is done being watched.
done: bool &default=F;
## The encryption algorithm in use
cipher_alg: string &log &optional;
## The signing (MAC) algorithm in use
mac_alg: string &log &optional;
## The compression algorithm in use
compression_alg: string &log &optional;
## The key exchange algorithm in use
kex_alg: string &log &optional;
## The server host key's algorithm
host_key_alg: string &log &optional;
## The server's key fingerprint
host_key: string &log &optional;
};
## The size in bytes of data sent by the server at which the SSH
## connection is presumed to be successful.
const authentication_data_size = 4000 &redef;
## The set of compression algorithms. We can't accurately determine
## authentication success or failure when compression is enabled.
const compression_algorithms = set("zlib", "zlib@openssh.com") &redef;
## If true, we tell the event engine to not look at further data
## packets after the initial SSH handshake. Helps with performance
## (especially with large file transfers) but precludes some
## kinds of analyses.
const skip_processing_after_detection = F &redef;
## kinds of analyses. Defaults to T.
const skip_processing_after_detection = T &redef;
## Event that is generated when the heuristic thinks that a login
## was successful.
global heuristic_successful_login: event(c: connection);
## Event that is generated when the heuristic thinks that a login
## failed.
global heuristic_failed_login: event(c: connection);
## Event that can be handled to access the :bro:type:`SSH::Info`
## record as it is sent on to the logging framework.
## Event that can be handled to access the SSH record as it is sent on
## to the logging framework.
global log_ssh: event(rec: Info);
## Event that can be handled when the analyzer sees an SSH server host
## key. This abstracts :bro:id:`ssh1_server_host_key` and
## :bro:id:`ssh2_server_host_key`.
global ssh_server_host_key: event(c: connection, hash: string);
}
redef record Info += {
# This connection has been logged (internal use)
logged: bool &default=F;
# Number of failures seen (internal use)
num_failures: count &default=0;
# Store capabilities from the first host for
# comparison with the second (internal use)
capabilities: Capabilities &optional;
};
redef record connection += {
ssh: Info &optional;
};
@ -73,15 +81,15 @@ redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(SSH::LOG, [$columns=Info, $ev=log_ssh]);
Analyzer::register_for_ports(Analyzer::ANALYZER_SSH, ports);
Log::create_stream(SSH::LOG, [$columns=Info, $ev=log_ssh, $path="ssh"]);
}
function set_session(c: connection)
{
if ( ! c?$ssh )
{
local info: Info;
local info: SSH::Info;
info$ts = network_time();
info$uid = c$uid;
info$id = c$id;
@ -89,116 +97,135 @@ function set_session(c: connection)
}
}
function check_ssh_connection(c: connection, done: bool)
{
# If already done watching this connection, just return.
if ( c$ssh$done )
return;
if ( done )
{
# If this connection is done, then we can look to see if
# this matches the conditions for a failed login. Failed
# logins are only detected at connection state removal.
if ( # Require originators and responders to have sent at least 50 bytes.
c$orig$size > 50 && c$resp$size > 50 &&
# Responders must be below 4000 bytes.
c$resp$size < authentication_data_size &&
# Responder must have sent fewer than 40 packets.
c$resp$num_pkts < 40 &&
# If there was a content gap we can't reliably do this heuristic.
c?$conn && c$conn$missed_bytes == 0 )# &&
# Only "normal" connections can count.
#c$conn?$conn_state && c$conn$conn_state in valid_states )
{
c$ssh$status = "failure";
event SSH::heuristic_failed_login(c);
}
if ( c$resp$size >= authentication_data_size )
{
c$ssh$status = "success";
event SSH::heuristic_successful_login(c);
}
}
else
{
# If this connection is still being tracked, then it's possible
# to watch for it to be a successful connection.
if ( c$resp$size >= authentication_data_size )
{
c$ssh$status = "success";
event SSH::heuristic_successful_login(c);
}
else
# This connection must be tracked longer. Let the scheduled
# check happen again.
return;
}
# Set the direction for the log.
c$ssh$direction = Site::is_local_addr(c$id$orig_h) ? OUTBOUND : INBOUND;
# Set the "done" flag to prevent the watching event from rescheduling
# after detection is done.
c$ssh$done=T;
if ( skip_processing_after_detection )
{
# Stop watching this connection, we don't care about it anymore.
skip_further_processing(c$id);
set_record_packets(c$id, F);
}
}
event heuristic_successful_login(c: connection) &priority=-5
{
Log::write(SSH::LOG, c$ssh);
}
event heuristic_failed_login(c: connection) &priority=-5
{
Log::write(SSH::LOG, c$ssh);
}
event connection_state_remove(c: connection) &priority=-5
{
if ( c?$ssh )
{
check_ssh_connection(c, T);
if ( c$ssh$status == "undetermined" )
Log::write(SSH::LOG, c$ssh);
}
}
event ssh_watcher(c: connection)
{
local id = c$id;
# don't go any further if this connection is gone already!
if ( ! connection_exists(id) )
return;
lookup_connection(c$id);
check_ssh_connection(c, F);
if ( ! c$ssh$done )
schedule +15secs { ssh_watcher(c) };
}
event ssh_server_version(c: connection, version: string) &priority=5
event ssh_server_version(c: connection, version: string)
{
set_session(c);
c$ssh$server = version;
}
event ssh_client_version(c: connection, version: string) &priority=5
event ssh_client_version(c: connection, version: string)
{
set_session(c);
c$ssh$client = version;
# The heuristic detection for SSH relies on the ConnSize analyzer.
# Don't do the heuristics if it's disabled.
if ( use_conn_size_analyzer )
schedule +15secs { ssh_watcher(c) };
if ( ( |version| > 3 ) && ( version[4] == "1" ) )
c$ssh$version = 1;
if ( ( |version| > 3 ) && ( version[4] == "2" ) )
c$ssh$version = 2;
}
event ssh_auth_successful(c: connection, auth_method_none: bool)
{
# TODO - what to do here?
if ( !c?$ssh || ( c$ssh?$auth_success && c$ssh$auth_success ) )
return;
# We can't accurately tell for compressed streams
if ( c$ssh?$compression_alg && ( c$ssh$compression_alg in compression_algorithms ) )
return;
c$ssh$auth_success = T;
if ( skip_processing_after_detection)
{
skip_further_processing(c$id);
set_record_packets(c$id, F);
}
}
event ssh_auth_successful(c: connection, auth_method_none: bool) &priority=-5
{
if ( c?$ssh && !c$ssh$logged )
{
c$ssh$logged = T;
Log::write(SSH::LOG, c$ssh);
}
}
event ssh_auth_failed(c: connection)
{
if ( !c?$ssh || ( c$ssh?$auth_success && !c$ssh$auth_success ) )
return;
# We can't accurately tell for compressed streams
if ( c$ssh?$compression_alg && ( c$ssh$compression_alg in compression_algorithms ) )
return;
c$ssh$auth_success = F;
c$ssh$num_failures += 1;
}
# Determine the negotiated algorithm
function find_alg(client_algorithms: vector of string, server_algorithms: vector of string): string
{
for ( i in client_algorithms )
for ( j in server_algorithms )
if ( client_algorithms[i] == server_algorithms[j] )
return client_algorithms[i];
return "Algorithm negotiation failed";
}
# This is a simple wrapper around find_alg for cases where client to server and server to client
# negotiate different algorithms. This is rare, but provided for completeness.
function find_bidirectional_alg(client_prefs: Algorithm_Prefs, server_prefs: Algorithm_Prefs): string
{
local c_to_s = find_alg(client_prefs$client_to_server, server_prefs$client_to_server);
local s_to_c = find_alg(client_prefs$server_to_client, server_prefs$server_to_client);
# Usually these are the same, but if they're not, return the details
return c_to_s == s_to_c ? c_to_s : fmt("To server: %s, to client: %s", c_to_s, s_to_c);
}
event ssh_capabilities(c: connection, cookie: string, capabilities: Capabilities)
{
if ( !c?$ssh || ( c$ssh?$capabilities && c$ssh$capabilities$is_server == capabilities$is_server ) )
return;
if ( !c$ssh?$capabilities )
{
c$ssh$capabilities = capabilities;
return;
}
local client_caps = capabilities$is_server ? c$ssh$capabilities : capabilities;
local server_caps = capabilities$is_server ? capabilities : c$ssh$capabilities;
c$ssh$cipher_alg = find_bidirectional_alg(client_caps$encryption_algorithms,
server_caps$encryption_algorithms);
c$ssh$mac_alg = find_bidirectional_alg(client_caps$mac_algorithms,
server_caps$mac_algorithms);
c$ssh$compression_alg = find_bidirectional_alg(client_caps$compression_algorithms,
server_caps$compression_algorithms);
c$ssh$kex_alg = find_alg(client_caps$kex_algorithms, server_caps$kex_algorithms);
c$ssh$host_key_alg = find_alg(client_caps$server_host_key_algorithms,
server_caps$server_host_key_algorithms);
}
event connection_state_remove(c: connection) &priority=-5
{
if ( c?$ssh && !c$ssh$logged && c$ssh?$client && c$ssh?$server )
{
c$ssh$logged = T;
Log::write(SSH::LOG, c$ssh);
}
}
function generate_fingerprint(c: connection, key: string)
{
if ( !c?$ssh )
return;
local lx = str_split(md5_hash(key), vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30));
lx[0] = "";
c$ssh$host_key = sub(join_string_vec(lx, ":"), /:/, "");
}
event ssh1_server_host_key(c: connection, p: string, e: string) &priority=5
{
generate_fingerprint(c, e + p);
}
event ssh2_server_host_key(c: connection, key: string) &priority=5
{
generate_fingerprint(c, key);
}

View file

@ -6,6 +6,11 @@ export {
const TLSv10 = 0x0301;
const TLSv11 = 0x0302;
const TLSv12 = 0x0303;
const DTLSv10 = 0xFEFF;
# DTLSv11 does not exist
const DTLSv12 = 0xFEFD;
## Mapping between the constants and string values for SSL/TLS versions.
const version_strings: table[count] of string = {
[SSLv2] = "SSLv2",
@ -13,6 +18,8 @@ export {
[TLSv10] = "TLSv10",
[TLSv11] = "TLSv11",
[TLSv12] = "TLSv12",
[DTLSv10] = "DTLSv10",
[DTLSv12] = "DTLSv12"
} &default=function(i: count):string { return fmt("unknown-%d", i); };
## TLS content types:

View file

@ -13,3 +13,10 @@ signature dpd_ssl_client {
payload /^(\x16\x03[\x00\x01\x02\x03]..\x01...\x03[\x00\x01\x02\x03]|...?\x01[\x00\x03][\x00\x01\x02\x03]).*/
tcp-state originator
}
signature dpd_dtls_client {
ip-proto == udp
# Client hello.
payload /^\x16\xfe[\xff\xfd]\x00\x00\x00\x00\x00\x00\x00...\x01...........\xfe[\xff\xfd].*/
enable "dtls"
}

View file

@ -85,6 +85,10 @@ event bro_init() &priority=5
Files::register_protocol(Analyzer::ANALYZER_SSL,
[$get_file_handle = SSL::get_file_handle,
$describe = SSL::describe_file]);
Files::register_protocol(Analyzer::ANALYZER_DTLS,
[$get_file_handle = SSL::get_file_handle,
$describe = SSL::describe_file]);
}
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5

View file

@ -92,16 +92,22 @@ redef record Info += {
delay_tokens: set[string] &optional;
};
const ports = {
const ssl_ports = {
443/tcp, 563/tcp, 585/tcp, 614/tcp, 636/tcp,
989/tcp, 990/tcp, 992/tcp, 993/tcp, 995/tcp, 5223/tcp
};
redef likely_server_ports += { ports };
# There are no well known DTLS ports at the moment. Let's
# just add 443 for now for good measure - who knows :)
const dtls_ports = { 443/udp };
redef likely_server_ports += { ssl_ports, dtls_ports };
event bro_init() &priority=5
{
Log::create_stream(SSL::LOG, [$columns=Info, $ev=log_ssl]);
Analyzer::register_for_ports(Analyzer::ANALYZER_SSL, ports);
Log::create_stream(SSL::LOG, [$columns=Info, $ev=log_ssl, $path="ssl"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_SSL, ssl_ports);
Analyzer::register_for_ports(Analyzer::ANALYZER_DTLS, dtls_ports);
}
function set_session(c: connection)
@ -268,7 +274,7 @@ event connection_state_remove(c: connection) &priority=-5
event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) &priority=5
{
if ( atype == Analyzer::ANALYZER_SSL )
if ( atype == Analyzer::ANALYZER_SSL || atype == Analyzer::ANALYZER_DTLS )
{
set_session(c);
c$ssl$analyzer_id = aid;
@ -278,6 +284,6 @@ event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) &pr
event protocol_violation(c: connection, atype: Analyzer::Tag, aid: count,
reason: string) &priority=5
{
if ( c?$ssl )
if ( c?$ssl && ( atype == Analyzer::ANALYZER_SSL || atype == Analyzer::ANALYZER_DTLS ) )
finish(c, T);
}

Some files were not shown because too many files have changed in this diff Show more