mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Merge remote-tracking branch 'origin/master' into topic/johanna/tls-more-data
This commit is contained in:
commit
b1dbd757a6
1468 changed files with 41493 additions and 19065 deletions
|
@ -65,7 +65,7 @@ export {
|
|||
[9] = "WINDOWS_CE_GUI",
|
||||
[10] = "EFI_APPLICATION",
|
||||
[11] = "EFI_BOOT_SERVICE_DRIVER",
|
||||
[12] = "EFI_RUNTIME_
DRIVER",
|
||||
[12] = "EFI_RUNTIME_DRIVER",
|
||||
[13] = "EFI_ROM",
|
||||
[14] = "XBOX"
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
|
|
@ -126,7 +126,7 @@ event pe_section_header(f: fa_file, h: PE::SectionHeader) &priority=5
|
|||
|
||||
if ( ! f$pe?$section_names )
|
||||
f$pe$section_names = vector();
|
||||
f$pe$section_names[|f$pe$section_names|] = h$name;
|
||||
f$pe$section_names += h$name;
|
||||
}
|
||||
|
||||
event file_state_remove(f: fa_file) &priority=-5
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
Support for X509 certificates with the file analysis framework.
|
||||
Also supports parsing OCSP requests and responses.
|
||||
|
|
|
@ -10,23 +10,17 @@ export {
|
|||
type Info: record {
|
||||
## Current timestamp.
|
||||
ts: time &log;
|
||||
|
||||
## File id of this certificate.
|
||||
id: string &log;
|
||||
|
||||
## Basic information about the certificate.
|
||||
certificate: X509::Certificate &log;
|
||||
|
||||
## The opaque wrapping the certificate. Mainly used
|
||||
## for the verify operations.
|
||||
handle: opaque of x509;
|
||||
|
||||
## All extensions that were encountered in the certificate.
|
||||
extensions: vector of X509::Extension &default=vector();
|
||||
|
||||
## Subject alternative name extension of the certificate.
|
||||
san: X509::SubjectAlternativeName &optional &log;
|
||||
|
||||
## Basic constraints extension of the certificate.
|
||||
basic_constraints: X509::BasicConstraints &optional &log;
|
||||
};
|
||||
|
@ -38,6 +32,24 @@ export {
|
|||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(X509::LOG, [$columns=Info, $ev=log_x509, $path="x509"]);
|
||||
|
||||
# We use MIME types internally to distinguish between user and CA certificates.
|
||||
# The first certificate in a connection always gets tagged as user-cert, all
|
||||
# following certificates get tagged as CA certificates. Certificates gotten via
|
||||
# other means (e.g. identified from HTTP traffic when they are transfered in plain
|
||||
# text) get tagged as application/pkix-cert.
|
||||
Files::register_for_mime_type(Files::ANALYZER_X509, "application/x-x509-user-cert");
|
||||
Files::register_for_mime_type(Files::ANALYZER_X509, "application/x-x509-ca-cert");
|
||||
Files::register_for_mime_type(Files::ANALYZER_X509, "application/pkix-cert");
|
||||
|
||||
# Always calculate hashes. They are not necessary for base scripts
|
||||
# but very useful for identification, and required for policy scripts
|
||||
Files::register_for_mime_type(Files::ANALYZER_MD5, "application/x-x509-user-cert");
|
||||
Files::register_for_mime_type(Files::ANALYZER_MD5, "application/x-x509-ca-cert");
|
||||
Files::register_for_mime_type(Files::ANALYZER_MD5, "application/pkix-cert");
|
||||
Files::register_for_mime_type(Files::ANALYZER_SHA1, "application/x-x509-user-cert");
|
||||
Files::register_for_mime_type(Files::ANALYZER_SHA1, "application/x-x509-ca-cert");
|
||||
Files::register_for_mime_type(Files::ANALYZER_SHA1, "application/pkix-cert");
|
||||
}
|
||||
|
||||
redef record Files::Info += {
|
||||
|
@ -48,16 +60,13 @@ redef record Files::Info += {
|
|||
|
||||
event x509_certificate(f: fa_file, cert_ref: opaque of x509, cert: X509::Certificate) &priority=5
|
||||
{
|
||||
if ( ! f$info?$mime_type )
|
||||
f$info$mime_type = "application/pkix-cert";
|
||||
|
||||
f$info$x509 = [$ts=f$info$ts, $id=f$id, $certificate=cert, $handle=cert_ref];
|
||||
}
|
||||
|
||||
event x509_extension(f: fa_file, ext: X509::Extension) &priority=5
|
||||
{
|
||||
if ( f$info?$x509 )
|
||||
f$info$x509$extensions[|f$info$x509$extensions|] = ext;
|
||||
f$info$x509$extensions += ext;
|
||||
}
|
||||
|
||||
event x509_ext_basic_constraints(f: fa_file, ext: X509::BasicConstraints) &priority=5
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
@load ./main
|
||||
@load ./store
|
||||
@load ./log
|
||||
|
|
80
scripts/base/frameworks/broker/log.bro
Normal file
80
scripts/base/frameworks/broker/log.bro
Normal file
|
@ -0,0 +1,80 @@
|
|||
@load ./main
|
||||
|
||||
module Broker;
|
||||
|
||||
export {
|
||||
## The Broker logging stream identifier.
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
## The type of a Broker activity being logged.
|
||||
type Type: enum {
|
||||
## An informational status update.
|
||||
STATUS,
|
||||
## An error situation.
|
||||
ERROR
|
||||
};
|
||||
|
||||
## A record type containing the column fields of the Broker log.
|
||||
type Info: record {
|
||||
## The network time at which a Broker event occurred.
|
||||
ts: time &log;
|
||||
## The type of the Broker event.
|
||||
ty: Type &log;
|
||||
## The event being logged.
|
||||
ev: string &log;
|
||||
## The peer (if any) with which a Broker event is
|
||||
## concerned.
|
||||
peer: NetworkInfo &log &optional;
|
||||
## An optional message describing the Broker event in more detail
|
||||
message: string &log &optional;
|
||||
};
|
||||
}
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(Broker::LOG, [$columns=Info, $path="broker"]);
|
||||
}
|
||||
|
||||
function log_status(ev: string, endpoint: EndpointInfo, msg: string)
|
||||
{
|
||||
local r: Info;
|
||||
|
||||
r = [$ts = network_time(),
|
||||
$ev = ev,
|
||||
$ty = STATUS,
|
||||
$message = msg];
|
||||
|
||||
if ( endpoint?$network )
|
||||
r$peer = endpoint$network;
|
||||
|
||||
Log::write(Broker::LOG, r);
|
||||
}
|
||||
|
||||
event Broker::peer_added(endpoint: EndpointInfo, msg: string)
|
||||
{
|
||||
log_status("peer-added", endpoint, msg);
|
||||
}
|
||||
|
||||
event Broker::peer_removed(endpoint: EndpointInfo, msg: string)
|
||||
{
|
||||
log_status("peer-removed", endpoint, msg);
|
||||
}
|
||||
|
||||
event Broker::peer_lost(endpoint: EndpointInfo, msg: string)
|
||||
{
|
||||
log_status("connection-terminated", endpoint, msg);
|
||||
}
|
||||
|
||||
event Broker::error(code: ErrorCode, msg: string)
|
||||
{
|
||||
local ev = cat(code);
|
||||
ev = subst_string(ev, "Broker::", "");
|
||||
ev = subst_string(ev, "_", "-");
|
||||
ev = to_lower(ev);
|
||||
|
||||
Log::write(Broker::LOG, [$ts = network_time(),
|
||||
$ev = ev,
|
||||
$ty = ERROR,
|
||||
$message = msg]);
|
||||
}
|
||||
|
|
@ -1,55 +1,182 @@
|
|||
##! Various data structure definitions for use with Bro's communication system.
|
||||
|
||||
module Log;
|
||||
|
||||
export {
|
||||
type Log::ID: enum {
|
||||
## Dummy place-holder.
|
||||
UNKNOWN
|
||||
};
|
||||
}
|
||||
##! The Broker-based communication API and its various options.
|
||||
|
||||
module Broker;
|
||||
|
||||
export {
|
||||
## Default port for Broker communication. Where not specified
|
||||
## otherwise, this is the port to connect to and listen on.
|
||||
const default_port = 9999/tcp &redef;
|
||||
|
||||
## A name used to identify this endpoint to peers.
|
||||
## Default interval to retry listening on a port if it's currently in
|
||||
## use already. Use of the BRO_DEFAULT_LISTEN_RETRY environment variable
|
||||
## (set as a number of seconds) will override this option and also
|
||||
## any values given to :bro:see:`Broker::listen`.
|
||||
const default_listen_retry = 30sec &redef;
|
||||
|
||||
## Default address on which to listen.
|
||||
##
|
||||
## .. bro:see:: Broker::connect Broker::listen
|
||||
const endpoint_name = "" &redef;
|
||||
## .. bro:see:: Broker::listen
|
||||
const default_listen_address = getenv("BRO_DEFAULT_LISTEN_ADDRESS") &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;
|
||||
## Default interval to retry connecting to a peer if it cannot be made to
|
||||
## work initially, or if it ever becomes disconnected. Use of the
|
||||
## BRO_DEFAULT_CONNECT_RETRY environment variable (set as number of
|
||||
## seconds) will override this option and also any values given to
|
||||
## :bro:see:`Broker::peer`.
|
||||
const default_connect_retry = 30sec &redef;
|
||||
|
||||
## If true, do not use SSL for network connections. By default, SSL will
|
||||
## even be used if no certificates / CAs have been configured. In that case
|
||||
## (which is the default) the communication will be encrypted, but not
|
||||
## authenticated.
|
||||
const disable_ssl = F &redef;
|
||||
|
||||
## Path to a file containing concatenated trusted certificates
|
||||
## in PEM format. If set, Bro will require valid certificates for
|
||||
## all peers.
|
||||
const ssl_cafile = "" &redef;
|
||||
|
||||
## Path to an OpenSSL-style directory of trusted certificates.
|
||||
## If set, Bro will require valid certificates for
|
||||
## all peers.
|
||||
const ssl_capath = "" &redef;
|
||||
|
||||
## Path to a file containing a X.509 certificate for this
|
||||
## node in PEM format. If set, Bro will require valid certificates for
|
||||
## all peers.
|
||||
const ssl_certificate = "" &redef;
|
||||
|
||||
## Passphrase to decrypt the private key specified by
|
||||
## :bro:see:`Broker::ssl_keyfile`. If set, Bro will require valid
|
||||
## certificates for all peers.
|
||||
const ssl_passphrase = "" &redef;
|
||||
|
||||
## Path to the file containing the private key for this node's
|
||||
## certificate. If set, Bro will require valid certificates for
|
||||
## all peers.
|
||||
const ssl_keyfile = "" &redef;
|
||||
|
||||
## The number of buffered messages at the Broker/CAF layer after which
|
||||
## a subscriber considers themselves congested (i.e. tune the congestion
|
||||
## control mechanisms).
|
||||
const congestion_queue_size = 200 &redef;
|
||||
|
||||
## Max number of threads to use for Broker/CAF functionality. Setting to
|
||||
## zero implies using the value of BRO_BROKER_MAX_THREADS environment
|
||||
## variable, if set, or else typically defaults to 4 (actually 2 threads
|
||||
## when simply reading offline pcaps as there's not expected to be any
|
||||
## communication and more threads just adds more overhead).
|
||||
const max_threads = 0 &redef;
|
||||
|
||||
## Max number of microseconds for under-utilized Broker/CAF
|
||||
## threads to sleep. Using zero will cause this to be automatically
|
||||
## determined or just use CAF's default setting.
|
||||
const max_sleep = 0 &redef;
|
||||
|
||||
## Forward all received messages to subscribing peers.
|
||||
const forward_messages = F &redef;
|
||||
|
||||
## The default topic prefix where logs will be published. The log's stream
|
||||
## id is appended when writing to a particular stream.
|
||||
const default_log_topic_prefix = "bro/logs/" &redef;
|
||||
|
||||
## The default implementation for :bro:see:`Broker::log_topic`.
|
||||
function default_log_topic(id: Log::ID, path: string): string
|
||||
{
|
||||
return default_log_topic_prefix + cat(id);
|
||||
}
|
||||
|
||||
## A function that will be called for each log entry to determine what
|
||||
## broker topic string will be used for sending it to peers. The
|
||||
## default implementation will return a value based on
|
||||
## :bro:see:`Broker::default_log_topic_prefix`.
|
||||
##
|
||||
## id: the ID associated with the log stream entry that will be sent.
|
||||
##
|
||||
## path: the path to which the log stream entry will be output.
|
||||
##
|
||||
## Returns: a string representing the broker topic to which the log
|
||||
## will be sent.
|
||||
const log_topic: function(id: Log::ID, path: string): string = default_log_topic &redef;
|
||||
|
||||
type ErrorCode: enum {
|
||||
## The unspecified default error code.
|
||||
UNSPECIFIED = 1,
|
||||
## Version incompatibility.
|
||||
PEER_INCOMPATIBLE = 2,
|
||||
## Referenced peer does not exist.
|
||||
PEER_INVALID = 3,
|
||||
## Remote peer not listening.
|
||||
PEER_UNAVAILABLE = 4,
|
||||
## A peering request timed out.
|
||||
PEER_TIMEOUT = 5,
|
||||
## Master with given name already exists.
|
||||
MASTER_EXISTS = 6,
|
||||
## Master with given name does not exist.
|
||||
NO_SUCH_MASTER = 7,
|
||||
## The given data store key does not exist.
|
||||
NO_SUCH_KEY = 8,
|
||||
## The store operation timed out.
|
||||
REQUEST_TIMEOUT = 9,
|
||||
## The operation expected a different type than provided.
|
||||
TYPE_CLASH = 10,
|
||||
## The data value cannot be used to carry out the desired operation.
|
||||
INVALID_DATA = 11,
|
||||
## The storage backend failed to execute the operation.
|
||||
BACKEND_FAILURE = 12,
|
||||
## The storage backend failed to execute the operation.
|
||||
STALE_DATA = 13,
|
||||
## Catch-all for a CAF-level problem.
|
||||
CAF_ERROR = 100
|
||||
};
|
||||
|
||||
## 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;
|
||||
## The possible states of a peer endpoint.
|
||||
type PeerStatus: enum {
|
||||
## The peering process is initiated.
|
||||
INITIALIZING,
|
||||
## Connection establishment in process.
|
||||
CONNECTING,
|
||||
## Connection established, peering pending.
|
||||
CONNECTED,
|
||||
## Successfully peered.
|
||||
PEERED,
|
||||
## Connection to remote peer lost.
|
||||
DISCONNECTED,
|
||||
## Reconnecting to peer after a lost connection.
|
||||
RECONNECTING,
|
||||
};
|
||||
|
||||
type NetworkInfo: record {
|
||||
## The IP address or hostname where the endpoint listens.
|
||||
address: string &log;
|
||||
## The port where the endpoint is bound to.
|
||||
bound_port: port &log;
|
||||
};
|
||||
|
||||
type EndpointInfo: record {
|
||||
## A unique identifier of the node.
|
||||
id: string;
|
||||
## Network-level information.
|
||||
network: NetworkInfo &optional;
|
||||
};
|
||||
|
||||
type PeerInfo: record {
|
||||
peer: EndpointInfo;
|
||||
status: PeerStatus;
|
||||
};
|
||||
|
||||
type PeerInfos: vector of PeerInfo;
|
||||
|
||||
## Opaque communication data.
|
||||
type Data: record {
|
||||
d: opaque of Broker::Data &optional;
|
||||
data: opaque of Broker::Data &optional;
|
||||
};
|
||||
|
||||
## Opaque communication data.
|
||||
## Opaque communication data sequence.
|
||||
type DataVector: vector of Broker::Data;
|
||||
|
||||
## Opaque event communication data.
|
||||
type EventArgs: record {
|
||||
type Event: record {
|
||||
## The name of the event. Not set if invalid event or arguments.
|
||||
name: string &optional;
|
||||
## The arguments to the event.
|
||||
|
@ -63,52 +190,25 @@ export {
|
|||
val: Broker::Data;
|
||||
};
|
||||
|
||||
## Enable use of communication.
|
||||
##
|
||||
## flags: used to tune the local Broker endpoint behavior.
|
||||
##
|
||||
## Returns: true if communication is successfully initialized.
|
||||
global enable: function(flags: EndpointFlags &default = EndpointFlags()): bool;
|
||||
|
||||
## Changes endpoint flags originally supplied to :bro:see:`Broker::enable`.
|
||||
##
|
||||
## flags: the new endpoint behavior flags to use.
|
||||
##
|
||||
## Returns: true if flags were changed.
|
||||
global set_endpoint_flags: function(flags: EndpointFlags &default = EndpointFlags()): bool;
|
||||
|
||||
## Allow sending messages to peers if associated with the given topic.
|
||||
## This has no effect if auto publication behavior is enabled via the flags
|
||||
## supplied to :bro:see:`Broker::enable` or :bro:see:`Broker::set_endpoint_flags`.
|
||||
##
|
||||
## topic: a topic to allow messages to be published under.
|
||||
##
|
||||
## Returns: true if successful.
|
||||
global publish_topic: function(topic: string): bool;
|
||||
|
||||
## Disallow sending messages to peers if associated with the given topic.
|
||||
## This has no effect if auto publication behavior is enabled via the flags
|
||||
## supplied to :bro:see:`Broker::enable` or :bro:see:`Broker::set_endpoint_flags`.
|
||||
##
|
||||
## topic: a topic to disallow messages to be published under.
|
||||
##
|
||||
## Returns: true if successful.
|
||||
global unpublish_topic: function(topic: string): bool;
|
||||
|
||||
## Listen for remote connections.
|
||||
##
|
||||
## p: the TCP port to listen on.
|
||||
##
|
||||
## a: an address string on which to accept connections, e.g.
|
||||
## "127.0.0.1". An empty string refers to @p INADDR_ANY.
|
||||
## "127.0.0.1". An empty string refers to INADDR_ANY.
|
||||
##
|
||||
## reuse: equivalent to behavior of SO_REUSEADDR.
|
||||
## p: the TCP port to listen on. The value 0 means that the OS should choose
|
||||
## the next available free port.
|
||||
##
|
||||
## Returns: true if the local endpoint is now listening for connections.
|
||||
## retry: If non-zero, retries listening in regular intervals if the port cannot be
|
||||
## acquired immediately. 0 disables retries. If the
|
||||
## BRO_DEFAULT_LISTEN_RETRY environment variable is set (as number
|
||||
## of seconds), it overrides any value given here.
|
||||
##
|
||||
## .. bro:see:: Broker::incoming_connection_established
|
||||
global listen: function(p: port, a: string &default = "", reuse: bool &default = T): bool;
|
||||
|
||||
## Returns: the bound port or 0/? on failure.
|
||||
##
|
||||
## .. bro:see:: Broker::status
|
||||
global listen: function(a: string &default = default_listen_address,
|
||||
p: port &default = default_port,
|
||||
retry: interval &default = default_listen_retry): port;
|
||||
## Initiate a remote connection.
|
||||
##
|
||||
## a: an address to connect to, e.g. "localhost" or "127.0.0.1".
|
||||
|
@ -117,69 +217,82 @@ export {
|
|||
##
|
||||
## retry: an interval at which to retry establishing the
|
||||
## connection with the remote peer if it cannot be made initially, or
|
||||
## if it ever becomes disconnected.
|
||||
## if it ever becomes disconnected. If the
|
||||
## BRO_DEFAULT_CONNECT_RETRY environment variable is set (as number
|
||||
## of seconds), it overrides any value given here.
|
||||
##
|
||||
## Returns: true if it's possible to try connecting with the peer and
|
||||
## it's a new peer. The actual connection may not be established
|
||||
## it's a new peer. The actual connection may not be established
|
||||
## until a later point in time.
|
||||
##
|
||||
## .. bro:see:: Broker::outgoing_connection_established
|
||||
global connect: function(a: string, p: port, retry: interval): bool;
|
||||
## .. bro:see:: Broker::status
|
||||
global peer: function(a: string, p: port &default=default_port,
|
||||
retry: interval &default=default_connect_retry): bool;
|
||||
|
||||
## Remove a remote connection.
|
||||
##
|
||||
## a: the address used in previous successful call to :bro:see:`Broker::connect`.
|
||||
## Note that this does not terminate the connection to the peer, it
|
||||
## just means that we won't exchange any further information with it
|
||||
## unless peering resumes later.
|
||||
##
|
||||
## p: the port used in previous successful call to :bro:see:`Broker::connect`.
|
||||
## a: the address used in previous successful call to :bro:see:`Broker::peer`.
|
||||
##
|
||||
## p: the port used in previous successful call to :bro:see:`Broker::peer`.
|
||||
##
|
||||
## Returns: true if the arguments match a previously successful call to
|
||||
## :bro:see:`Broker::connect`.
|
||||
global disconnect: function(a: string, p: port): bool;
|
||||
## :bro:see:`Broker::peer`.
|
||||
##
|
||||
## TODO: We do not have a function yet to terminate a connection.
|
||||
global unpeer: function(a: string, p: port): bool;
|
||||
|
||||
## Print a simple message to any interested peers. The receiver can use
|
||||
## :bro:see:`Broker::print_handler` to handle messages.
|
||||
## Get a list of all peer connections.
|
||||
##
|
||||
## topic: a topic associated with the printed message.
|
||||
## Returns: a list of all peer connections.
|
||||
global peers: function(): vector of PeerInfo;
|
||||
|
||||
## Get a unique identifier for the local broker endpoint.
|
||||
##
|
||||
## msg: the print message to send to peers.
|
||||
## Returns: a unique identifier for the local broker endpoint.
|
||||
global node_id: function(): string;
|
||||
|
||||
## Sends all pending log messages to remote peers. This normally
|
||||
## doesn't need to be used except for test cases that are time-sensitive.
|
||||
global flush_logs: function(): count;
|
||||
|
||||
## Publishes the value of an identifier to a given topic. The subscribers
|
||||
## will update their local value for that identifier on receipt.
|
||||
##
|
||||
## flags: tune the behavior of how the message is sent.
|
||||
## topic: a topic associated with the message.
|
||||
##
|
||||
## id: the identifier to publish.
|
||||
##
|
||||
## Returns: true if the message is sent.
|
||||
global send_print: function(topic: string, msg: string, flags: SendFlags &default = SendFlags()): bool;
|
||||
global publish_id: function(topic: string, id: string): bool;
|
||||
|
||||
## Register interest in all peer print messages that use a certain topic
|
||||
## prefix. Use :bro:see:`Broker::print_handler` to handle received
|
||||
## messages.
|
||||
## Register interest in all peer event messages that use a certain topic
|
||||
## prefix. Note that subscriptions may not be altered immediately after
|
||||
## calling (except during :bro:see:`bro_init`).
|
||||
##
|
||||
## topic_prefix: a prefix to match against remote message topics.
|
||||
## e.g. an empty prefix matches everything and "a" matches
|
||||
## "alice" and "amy" but not "bob".
|
||||
##
|
||||
## Returns: true if it's a new print subscription and it is now registered.
|
||||
global subscribe_to_prints: function(topic_prefix: string): bool;
|
||||
## Returns: true if it's a new event subscription and it is now registered.
|
||||
global subscribe: function(topic_prefix: string): bool;
|
||||
|
||||
## Unregister interest in all peer print messages that use a topic prefix.
|
||||
## Unregister interest in all peer event messages that use a topic prefix.
|
||||
## Note that subscriptions may not be altered immediately after calling
|
||||
## (except during :bro:see:`bro_init`).
|
||||
##
|
||||
## topic_prefix: a prefix previously supplied to a successful call to
|
||||
## :bro:see:`Broker::subscribe_to_prints`.
|
||||
## :bro:see:`Broker::subscribe`.
|
||||
##
|
||||
## Returns: true if interest in the topic prefix is no longer advertised.
|
||||
global unsubscribe_to_prints: function(topic_prefix: string): bool;
|
||||
|
||||
## Send an event to any interested peers.
|
||||
##
|
||||
## topic: a topic associated with the event message.
|
||||
##
|
||||
## args: event arguments as made by :bro:see:`Broker::event_args`.
|
||||
##
|
||||
## flags: tune the behavior of how the message is sent.
|
||||
##
|
||||
## Returns: true if the message is sent.
|
||||
global send_event: function(topic: string, args: EventArgs, flags: SendFlags &default = SendFlags()): bool;
|
||||
global unsubscribe: function(topic_prefix: string): bool;
|
||||
|
||||
## Automatically send an event to any interested peers whenever it is
|
||||
## locally dispatched (e.g. using "event my_event(...);" in a script).
|
||||
## locally dispatched. (For example, using "event my_event(...);" in a
|
||||
## script.)
|
||||
##
|
||||
## topic: a topic string associated with the event message.
|
||||
## Peers advertise interest by registering a subscription to some
|
||||
|
@ -187,83 +300,18 @@ export {
|
|||
##
|
||||
## ev: a Bro event value.
|
||||
##
|
||||
## flags: tune the behavior of how the message is sent.
|
||||
##
|
||||
## Returns: true if automatic event sending is now enabled.
|
||||
global auto_event: function(topic: string, ev: any, flags: SendFlags &default = SendFlags()): bool;
|
||||
global auto_publish: function(topic: string, ev: any): bool;
|
||||
|
||||
## Stop automatically sending an event to peers upon local dispatch.
|
||||
##
|
||||
## topic: a topic originally given to :bro:see:`Broker::auto_event`.
|
||||
## topic: a topic originally given to :bro:see:`Broker::auto_publish`.
|
||||
##
|
||||
## ev: an event originally given to :bro:see:`Broker::auto_event`.
|
||||
## ev: an event originally given to :bro:see:`Broker::auto_publish`.
|
||||
##
|
||||
## Returns: true if automatic events will not occur for the topic/event
|
||||
## pair.
|
||||
global auto_event_stop: function(topic: string, ev: any): bool;
|
||||
|
||||
## Register interest in all peer event messages that use a certain topic
|
||||
## prefix.
|
||||
##
|
||||
## topic_prefix: a prefix to match against remote message topics.
|
||||
## e.g. an empty prefix matches everything and "a" matches
|
||||
## "alice" and "amy" but not "bob".
|
||||
##
|
||||
## Returns: true if it's a new event subscription and it is now registered.
|
||||
global subscribe_to_events: function(topic_prefix: string): bool;
|
||||
|
||||
## Unregister interest in all peer event messages that use a topic prefix.
|
||||
##
|
||||
## topic_prefix: a prefix previously supplied to a successful call to
|
||||
## :bro:see:`Broker::subscribe_to_events`.
|
||||
##
|
||||
## Returns: true if interest in the topic prefix is no longer advertised.
|
||||
global unsubscribe_to_events: function(topic_prefix: string): bool;
|
||||
|
||||
## Enable remote logs for a given log stream.
|
||||
##
|
||||
## id: the log stream to enable remote logs for.
|
||||
##
|
||||
## flags: tune the behavior of how log entry messages are sent.
|
||||
##
|
||||
## Returns: true if remote logs are enabled for the stream.
|
||||
global enable_remote_logs: function(id: Log::ID, flags: SendFlags &default = SendFlags()): bool;
|
||||
|
||||
## Disable remote logs for a given log stream.
|
||||
##
|
||||
## id: the log stream to disable remote logs for.
|
||||
##
|
||||
## Returns: true if remote logs are disabled for the stream.
|
||||
global disable_remote_logs: function(id: Log::ID): bool;
|
||||
|
||||
## Check if remote logs are enabled for a given log stream.
|
||||
##
|
||||
## id: the log stream to check.
|
||||
##
|
||||
## Returns: true if remote logs are enabled for the given stream.
|
||||
global remote_logs_enabled: function(id: Log::ID): bool;
|
||||
|
||||
## Register interest in all peer log messages that use a certain topic
|
||||
## prefix. Logs are implicitly sent with topic "bro/log/<stream-name>" and
|
||||
## the receiving side processes them through the logging framework as usual.
|
||||
##
|
||||
## topic_prefix: a prefix to match against remote message topics.
|
||||
## e.g. an empty prefix matches everything and "a" matches
|
||||
## "alice" and "amy" but not "bob".
|
||||
##
|
||||
## Returns: true if it's a new log subscription and it is now registered.
|
||||
global subscribe_to_logs: function(topic_prefix: string): bool;
|
||||
|
||||
## Unregister interest in all peer log messages that use a topic prefix.
|
||||
## Logs are implicitly sent with topic "bro/log/<stream-name>" and the
|
||||
## receiving side processes them through the logging framework as usual.
|
||||
##
|
||||
## topic_prefix: a prefix previously supplied to a successful call to
|
||||
## :bro:see:`Broker::subscribe_to_logs`.
|
||||
##
|
||||
## Returns: true if interest in the topic prefix is no longer advertised.
|
||||
global unsubscribe_to_logs: function(topic_prefix: string): bool;
|
||||
|
||||
global auto_unpublish: function(topic: string, ev: any): bool;
|
||||
}
|
||||
|
||||
@load base/bif/comm.bif
|
||||
|
@ -271,106 +319,75 @@ export {
|
|||
|
||||
module Broker;
|
||||
|
||||
@ifdef ( Broker::__enable )
|
||||
event retry_listen(a: string, p: port, retry: interval)
|
||||
{
|
||||
listen(a, p, retry);
|
||||
}
|
||||
|
||||
function enable(flags: EndpointFlags &default = EndpointFlags()) : bool
|
||||
{
|
||||
return __enable(flags);
|
||||
}
|
||||
function listen(a: string, p: port, retry: interval): port
|
||||
{
|
||||
local bound = __listen(a, p);
|
||||
|
||||
function set_endpoint_flags(flags: EndpointFlags &default = EndpointFlags()): bool
|
||||
{
|
||||
return __set_endpoint_flags(flags);
|
||||
}
|
||||
if ( bound == 0/tcp )
|
||||
{
|
||||
local e = getenv("BRO_DEFAULT_LISTEN_RETRY");
|
||||
|
||||
function publish_topic(topic: string): bool
|
||||
{
|
||||
return __publish_topic(topic);
|
||||
}
|
||||
if ( e != "" )
|
||||
retry = double_to_interval(to_double(e));
|
||||
|
||||
function unpublish_topic(topic: string): bool
|
||||
{
|
||||
return __unpublish_topic(topic);
|
||||
}
|
||||
if ( retry != 0secs )
|
||||
schedule retry { retry_listen(a, p, retry) };
|
||||
}
|
||||
|
||||
function listen(p: port, a: string &default = "", reuse: bool &default = T): bool
|
||||
{
|
||||
return __listen(p, a, reuse);
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
function connect(a: string, p: port, retry: interval): bool
|
||||
{
|
||||
return __connect(a, p, retry);
|
||||
}
|
||||
function peer(a: string, p: port, retry: interval): bool
|
||||
{
|
||||
return __peer(a, p, retry);
|
||||
}
|
||||
|
||||
function disconnect(a: string, p: port): bool
|
||||
{
|
||||
return __disconnect(a, p);
|
||||
}
|
||||
function unpeer(a: string, p: port): bool
|
||||
{
|
||||
return __unpeer(a, p);
|
||||
}
|
||||
|
||||
function send_print(topic: string, msg: string, flags: SendFlags &default = SendFlags()): bool
|
||||
{
|
||||
return __send_print(topic, msg, flags);
|
||||
}
|
||||
function peers(): vector of PeerInfo
|
||||
{
|
||||
return __peers();
|
||||
}
|
||||
|
||||
function subscribe_to_prints(topic_prefix: string): bool
|
||||
{
|
||||
return __subscribe_to_prints(topic_prefix);
|
||||
}
|
||||
function node_id(): string
|
||||
{
|
||||
return __node_id();
|
||||
}
|
||||
|
||||
function unsubscribe_to_prints(topic_prefix: string): bool
|
||||
{
|
||||
return __unsubscribe_to_prints(topic_prefix);
|
||||
}
|
||||
function flush_logs(): count
|
||||
{
|
||||
return __flush_logs();
|
||||
}
|
||||
|
||||
function send_event(topic: string, args: EventArgs, flags: SendFlags &default = SendFlags()): bool
|
||||
{
|
||||
return __event(topic, args, flags);
|
||||
}
|
||||
function publish_id(topic: string, id: string): bool
|
||||
{
|
||||
return __publish_id(topic, id);
|
||||
}
|
||||
|
||||
function auto_event(topic: string, ev: any, flags: SendFlags &default = SendFlags()): bool
|
||||
{
|
||||
return __auto_event(topic, ev, flags);
|
||||
}
|
||||
function subscribe(topic_prefix: string): bool
|
||||
{
|
||||
return __subscribe(topic_prefix);
|
||||
}
|
||||
|
||||
function auto_event_stop(topic: string, ev: any): bool
|
||||
{
|
||||
return __auto_event_stop(topic, ev);
|
||||
}
|
||||
function unsubscribe(topic_prefix: string): bool
|
||||
{
|
||||
return __unsubscribe(topic_prefix);
|
||||
}
|
||||
|
||||
function subscribe_to_events(topic_prefix: string): bool
|
||||
{
|
||||
return __subscribe_to_events(topic_prefix);
|
||||
}
|
||||
function auto_publish(topic: string, ev: any): bool
|
||||
{
|
||||
return __auto_publish(topic, ev);
|
||||
}
|
||||
|
||||
function unsubscribe_to_events(topic_prefix: string): bool
|
||||
{
|
||||
return __unsubscribe_to_events(topic_prefix);
|
||||
}
|
||||
|
||||
function enable_remote_logs(id: Log::ID, flags: SendFlags &default = SendFlags()): bool
|
||||
{
|
||||
return __enable_remote_logs(id, flags);
|
||||
}
|
||||
|
||||
function disable_remote_logs(id: Log::ID): bool
|
||||
{
|
||||
return __disable_remote_logs(id);
|
||||
}
|
||||
|
||||
function remote_logs_enabled(id: Log::ID): bool
|
||||
{
|
||||
return __remote_logs_enabled(id);
|
||||
}
|
||||
|
||||
function subscribe_to_logs(topic_prefix: string): bool
|
||||
{
|
||||
return __subscribe_to_logs(topic_prefix);
|
||||
}
|
||||
|
||||
function unsubscribe_to_logs(topic_prefix: string): bool
|
||||
{
|
||||
return __unsubscribe_to_logs(topic_prefix);
|
||||
}
|
||||
|
||||
@endif
|
||||
function auto_unpublish(topic: string, ev: any): bool
|
||||
{
|
||||
return __auto_unpublish(topic, ev);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,11 +1,16 @@
|
|||
# Load the core cluster support.
|
||||
@load ./main
|
||||
@load ./pools
|
||||
|
||||
@if ( Cluster::is_enabled() )
|
||||
|
||||
# Give the node being started up it's peer name.
|
||||
redef peer_description = Cluster::node;
|
||||
|
||||
@if ( Cluster::enable_round_robin_logging )
|
||||
redef Broker::log_topic = Cluster::rr_log_topic;
|
||||
@endif
|
||||
|
||||
# Add a cluster prefix.
|
||||
@prefixes += cluster
|
||||
|
||||
|
@ -19,13 +24,6 @@ redef peer_description = Cluster::node;
|
|||
|
||||
@load ./setup-connections
|
||||
|
||||
# Don't load the listening script until we're a bit more sure that the
|
||||
# cluster framework is actually being enabled.
|
||||
@load frameworks/communication/listen
|
||||
|
||||
## Set the port that this node is supposed to listen on.
|
||||
redef Communication::listen_port = Cluster::nodes[Cluster::node]$p;
|
||||
|
||||
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
@load ./nodes/manager
|
||||
# If no logger is defined, then the manager receives logs.
|
||||
|
|
|
@ -7,10 +7,111 @@
|
|||
##! ``@load base/frameworks/cluster``.
|
||||
|
||||
@load base/frameworks/control
|
||||
@load base/frameworks/broker
|
||||
|
||||
module Cluster;
|
||||
|
||||
export {
|
||||
## Whether to distribute log messages among available logging nodes.
|
||||
const enable_round_robin_logging = T &redef;
|
||||
|
||||
## The topic name used for exchanging general messages that are relevant to
|
||||
## any node in a cluster. Used with broker-enabled cluster communication.
|
||||
const broadcast_topic = "bro/cluster/broadcast" &redef;
|
||||
|
||||
## The topic name used for exchanging messages that are relevant to
|
||||
## logger nodes in a cluster. Used with broker-enabled cluster communication.
|
||||
const logger_topic = "bro/cluster/logger" &redef;
|
||||
|
||||
## The topic name used for exchanging messages that are relevant to
|
||||
## manager nodes in a cluster. Used with broker-enabled cluster communication.
|
||||
const manager_topic = "bro/cluster/manager" &redef;
|
||||
|
||||
## The topic name used for exchanging messages that are relevant to
|
||||
## proxy nodes in a cluster. Used with broker-enabled cluster communication.
|
||||
const proxy_topic = "bro/cluster/proxy" &redef;
|
||||
|
||||
## The topic name used for exchanging messages that are relevant to
|
||||
## worker nodes in a cluster. Used with broker-enabled cluster communication.
|
||||
const worker_topic = "bro/cluster/worker" &redef;
|
||||
|
||||
## The topic name used for exchanging messages that are relevant to
|
||||
## time machine nodes in a cluster. Used with broker-enabled cluster communication.
|
||||
const time_machine_topic = "bro/cluster/time_machine" &redef;
|
||||
|
||||
## The topic prefix used for exchanging messages that are relevant to
|
||||
## a named node in a cluster. Used with broker-enabled cluster communication.
|
||||
const node_topic_prefix = "bro/cluster/node/" &redef;
|
||||
|
||||
## Name of the node on which master data stores will be created if no other
|
||||
## has already been specified by the user in :bro:see:`Cluster::stores`.
|
||||
## An empty value means "use whatever name corresponds to the manager
|
||||
## node".
|
||||
const default_master_node = "" &redef;
|
||||
|
||||
## The type of data store backend that will be used for all data stores if
|
||||
## no other has already been specified by the user in :bro:see:`Cluster::stores`.
|
||||
const default_backend = Broker::MEMORY &redef;
|
||||
|
||||
## The type of persistent data store backend that will be used for all data
|
||||
## stores if no other has already been specified by the user in
|
||||
## :bro:see:`Cluster::stores`. This will be used when script authors call
|
||||
## :bro:see:`Cluster::create_store` with the *persistent* argument set true.
|
||||
const default_persistent_backend = Broker::SQLITE &redef;
|
||||
|
||||
## Setting a default dir will, for persistent backends that have not
|
||||
## been given an explicit file path via :bro:see:`Cluster::stores`,
|
||||
## automatically create a path within this dir that is based on the name of
|
||||
## the data store.
|
||||
const default_store_dir = "" &redef;
|
||||
|
||||
## Information regarding a cluster-enabled data store.
|
||||
type StoreInfo: record {
|
||||
## The name of the data store.
|
||||
name: string &optional;
|
||||
## The store handle.
|
||||
store: opaque of Broker::Store &optional;
|
||||
## The name of the cluster node on which the master version of the data
|
||||
## store resides.
|
||||
master_node: string &default=default_master_node;
|
||||
## Whether the data store is the master version or a clone.
|
||||
master: bool &default=F;
|
||||
## The type of backend used for storing data.
|
||||
backend: Broker::BackendType &default=default_backend;
|
||||
## Parameters used for configuring the backend.
|
||||
options: Broker::BackendOptions &default=Broker::BackendOptions();
|
||||
## A resync/reconnect interval to pass through to
|
||||
## :bro:see:`Broker::create_clone`.
|
||||
clone_resync_interval: interval &default=Broker::default_clone_resync_interval;
|
||||
## A staleness duration to pass through to
|
||||
## :bro:see:`Broker::create_clone`.
|
||||
clone_stale_interval: interval &default=Broker::default_clone_stale_interval;
|
||||
## A mutation buffer interval to pass through to
|
||||
## :bro:see:`Broker::create_clone`.
|
||||
clone_mutation_buffer_interval: interval &default=Broker::default_clone_mutation_buffer_interval;
|
||||
};
|
||||
|
||||
## A table of cluster-enabled data stores that have been created, indexed
|
||||
## by their name. This table will be populated automatically by
|
||||
## :bro:see:`Cluster::create_store`, but if you need to customize
|
||||
## the options related to a particular data store, you may redef this
|
||||
## table. Calls to :bro:see:`Cluster::create_store` will first check
|
||||
## the table for an entry of the same name and, if found, will use the
|
||||
## predefined options there when setting up the store.
|
||||
global stores: table[string] of StoreInfo &default=StoreInfo() &redef;
|
||||
|
||||
## Sets up a cluster-enabled data store. They will also still properly
|
||||
## function for uses that are not operating a cluster.
|
||||
##
|
||||
## name: the name of the data store to create.
|
||||
##
|
||||
## persistent: whether the data store must be persistent.
|
||||
##
|
||||
## Returns: the store's information. For master stores, the store will be
|
||||
## ready to use immediately. For clones, the store field will not
|
||||
## be set until the node containing the master store has connected.
|
||||
global create_store: function(name: string, persistent: bool &default=F): StoreInfo;
|
||||
|
||||
## The cluster logging stream identifier.
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
|
@ -18,6 +119,8 @@ export {
|
|||
type Info: record {
|
||||
## The time at which a cluster message was generated.
|
||||
ts: time;
|
||||
## The name of the node that is creating the log record.
|
||||
node: string;
|
||||
## A message indicating information about the cluster's operation.
|
||||
message: string;
|
||||
} &log;
|
||||
|
@ -46,43 +149,6 @@ export {
|
|||
TIME_MACHINE,
|
||||
};
|
||||
|
||||
## Events raised by a manager and handled by the workers.
|
||||
const manager2worker_events = /Drop::.*/ &redef;
|
||||
|
||||
## Events raised by a manager and handled by proxies.
|
||||
const manager2proxy_events = /EMPTY/ &redef;
|
||||
|
||||
## Events raised by a manager and handled by loggers.
|
||||
const manager2logger_events = /EMPTY/ &redef;
|
||||
|
||||
## Events raised by proxies and handled by loggers.
|
||||
const proxy2logger_events = /EMPTY/ &redef;
|
||||
|
||||
## Events raised by proxies and handled by a manager.
|
||||
const proxy2manager_events = /EMPTY/ &redef;
|
||||
|
||||
## Events raised by proxies and handled by workers.
|
||||
const proxy2worker_events = /EMPTY/ &redef;
|
||||
|
||||
## Events raised by workers and handled by loggers.
|
||||
const worker2logger_events = /EMPTY/ &redef;
|
||||
|
||||
## Events raised by workers and handled by a manager.
|
||||
const worker2manager_events = /(TimeMachine::command|Drop::.*)/ &redef;
|
||||
|
||||
## Events raised by workers and handled by proxies.
|
||||
const worker2proxy_events = /EMPTY/ &redef;
|
||||
|
||||
## Events raised by TimeMachine instances and handled by a manager.
|
||||
const tm2manager_events = /EMPTY/ &redef;
|
||||
|
||||
## Events raised by TimeMachine instances and handled by workers.
|
||||
const tm2worker_events = /EMPTY/ &redef;
|
||||
|
||||
## Events sent by the control host (i.e., BroControl) when dynamically
|
||||
## connecting to a running instance to update settings or request data.
|
||||
const control_events = Control::controller_events &redef;
|
||||
|
||||
## Record type to indicate a node in a cluster.
|
||||
type Node: record {
|
||||
## Identifies the type of cluster node in this node's configuration.
|
||||
|
@ -92,22 +158,17 @@ export {
|
|||
## If the *ip* field is a non-global IPv6 address, this field
|
||||
## can specify a particular :rfc:`4007` ``zone_id``.
|
||||
zone_id: string &default="";
|
||||
## The port to which this local node can connect when
|
||||
## establishing communication.
|
||||
## The port that this node will listen on for peer connections.
|
||||
p: port;
|
||||
## Identifier for the interface a worker is sniffing.
|
||||
interface: string &optional;
|
||||
## Name of the logger node this node uses. For manager, proxies and workers.
|
||||
logger: string &optional;
|
||||
## Name of the manager node this node uses. For workers and proxies.
|
||||
manager: string &optional;
|
||||
## Name of the proxy node this node uses. For workers and managers.
|
||||
proxy: string &optional;
|
||||
## Names of worker nodes that this node connects with.
|
||||
## For managers and proxies.
|
||||
workers: set[string] &optional;
|
||||
## Name of a time machine node with which this node connects.
|
||||
time_machine: string &optional;
|
||||
## A unique identifier assigned to the node by the broker framework.
|
||||
## This field is only set while a node is connected.
|
||||
id: string &optional;
|
||||
};
|
||||
|
||||
## This function can be called at any time to determine if the cluster
|
||||
|
@ -134,6 +195,8 @@ export {
|
|||
## named cluster-layout.bro somewhere in the BROPATH. It will be
|
||||
## automatically loaded if the CLUSTER_NODE environment variable is set.
|
||||
## Note that BroControl handles all of this automatically.
|
||||
## The table is typically indexed by node names/labels (e.g. "manager"
|
||||
## or "worker-1").
|
||||
const nodes: table[string] of Node = {} &redef;
|
||||
|
||||
## Indicates whether or not the manager will act as the logger and receive
|
||||
|
@ -147,9 +210,67 @@ export {
|
|||
const node = getenv("CLUSTER_NODE") &redef;
|
||||
|
||||
## Interval for retrying failed connections between cluster nodes.
|
||||
## If set, the BRO_DEFAULT_CONNECT_RETRY (given in number of seconds)
|
||||
## overrides this option.
|
||||
const retry_interval = 1min &redef;
|
||||
|
||||
## When using broker-enabled cluster framework, nodes broadcast this event
|
||||
## to exchange their user-defined name along with a string that uniquely
|
||||
## identifies it for the duration of its lifetime. This string may change
|
||||
## if the node dies and has to reconnect later.
|
||||
global hello: event(name: string, id: string);
|
||||
|
||||
## When using broker-enabled cluster framework, this event will be emitted
|
||||
## locally whenever a cluster node connects or reconnects.
|
||||
global node_up: event(name: string, id: string);
|
||||
|
||||
## When using broker-enabled cluster framework, this event will be emitted
|
||||
## locally whenever a connected cluster node becomes disconnected.
|
||||
global node_down: event(name: string, id: string);
|
||||
|
||||
## Write a message to the cluster logging stream.
|
||||
global log: function(msg: string);
|
||||
|
||||
## Retrieve the topic associated with a specific node in the cluster.
|
||||
##
|
||||
## name: the name of the cluster node (e.g. "manager").
|
||||
##
|
||||
## Returns: a topic string that may used to send a message exclusively to
|
||||
## a given cluster node.
|
||||
global node_topic: function(name: string): string;
|
||||
}
|
||||
|
||||
global active_worker_ids: set[string] = set();
|
||||
|
||||
type NamedNode: record {
|
||||
name: string;
|
||||
node: Node;
|
||||
};
|
||||
|
||||
function nodes_with_type(node_type: NodeType): vector of NamedNode
|
||||
{
|
||||
local rval: vector of NamedNode = vector();
|
||||
local names: vector of string = vector();
|
||||
|
||||
for ( name in Cluster::nodes )
|
||||
names += name;
|
||||
|
||||
names = sort(names, strcmp);
|
||||
|
||||
for ( i in names )
|
||||
{
|
||||
name = names[i];
|
||||
local n = Cluster::nodes[name];
|
||||
|
||||
if ( n$node_type != node_type )
|
||||
next;
|
||||
|
||||
rval += NamedNode($name=name, $node=n);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
function is_enabled(): bool
|
||||
{
|
||||
return (node != "");
|
||||
|
@ -160,16 +281,70 @@ function local_node_type(): NodeType
|
|||
return is_enabled() ? nodes[node]$node_type : NONE;
|
||||
}
|
||||
|
||||
event remote_connection_handshake_done(p: event_peer) &priority=5
|
||||
function node_topic(name: string): string
|
||||
{
|
||||
if ( p$descr in nodes && nodes[p$descr]$node_type == WORKER )
|
||||
++worker_count;
|
||||
return node_topic_prefix + name;
|
||||
}
|
||||
|
||||
event remote_connection_closed(p: event_peer) &priority=5
|
||||
event Cluster::hello(name: string, id: string) &priority=10
|
||||
{
|
||||
if ( p$descr in nodes && nodes[p$descr]$node_type == WORKER )
|
||||
--worker_count;
|
||||
if ( name !in nodes )
|
||||
{
|
||||
Reporter::error(fmt("Got Cluster::hello msg from unexpected node: %s", name));
|
||||
return;
|
||||
}
|
||||
|
||||
local n = nodes[name];
|
||||
|
||||
if ( n?$id )
|
||||
{
|
||||
if ( n$id != id )
|
||||
Reporter::error(fmt("Got Cluster::hello msg from duplicate node:%s",
|
||||
name));
|
||||
}
|
||||
else
|
||||
event Cluster::node_up(name, id);
|
||||
|
||||
n$id = id;
|
||||
Cluster::log(fmt("got hello from %s (%s)", name, id));
|
||||
|
||||
if ( n$node_type == WORKER )
|
||||
{
|
||||
add active_worker_ids[id];
|
||||
worker_count = |active_worker_ids|;
|
||||
}
|
||||
}
|
||||
|
||||
event Broker::peer_added(endpoint: Broker::EndpointInfo, msg: string) &priority=10
|
||||
{
|
||||
if ( ! Cluster::is_enabled() )
|
||||
return;
|
||||
|
||||
local e = Broker::make_event(Cluster::hello, node, Broker::node_id());
|
||||
Broker::publish(Cluster::broadcast_topic, e);
|
||||
}
|
||||
|
||||
event Broker::peer_lost(endpoint: Broker::EndpointInfo, msg: string) &priority=10
|
||||
{
|
||||
for ( node_name in nodes )
|
||||
{
|
||||
local n = nodes[node_name];
|
||||
|
||||
if ( n?$id && n$id == endpoint$id )
|
||||
{
|
||||
Cluster::log(fmt("node down: %s", node_name));
|
||||
delete n$id;
|
||||
|
||||
if ( n$node_type == WORKER )
|
||||
{
|
||||
delete active_worker_ids[endpoint$id];
|
||||
worker_count = |active_worker_ids|;
|
||||
}
|
||||
|
||||
event Cluster::node_down(node_name, endpoint$id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event bro_init() &priority=5
|
||||
|
@ -183,3 +358,90 @@ event bro_init() &priority=5
|
|||
|
||||
Log::create_stream(Cluster::LOG, [$columns=Info, $path="cluster"]);
|
||||
}
|
||||
|
||||
function create_store(name: string, persistent: bool &default=F): Cluster::StoreInfo
|
||||
{
|
||||
local info = stores[name];
|
||||
info$name = name;
|
||||
|
||||
if ( Cluster::default_store_dir != "" )
|
||||
{
|
||||
local default_options = Broker::BackendOptions();
|
||||
local path = Cluster::default_store_dir + "/" + name;
|
||||
|
||||
if ( info$options$sqlite$path == default_options$sqlite$path )
|
||||
info$options$sqlite$path = path + ".sqlite";
|
||||
|
||||
if ( info$options$rocksdb$path == default_options$rocksdb$path )
|
||||
info$options$rocksdb$path = path + ".rocksdb";
|
||||
}
|
||||
|
||||
if ( persistent )
|
||||
{
|
||||
switch ( info$backend ) {
|
||||
case Broker::MEMORY:
|
||||
info$backend = Cluster::default_persistent_backend;
|
||||
break;
|
||||
case Broker::SQLITE:
|
||||
fallthrough;
|
||||
case Broker::ROCKSDB:
|
||||
# no-op: user already asked for a specific persistent backend.
|
||||
break;
|
||||
default:
|
||||
Reporter::error(fmt("unhandled data store type: %s", info$backend));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! Cluster::is_enabled() )
|
||||
{
|
||||
if ( info?$store )
|
||||
{
|
||||
Reporter::warning(fmt("duplicate cluster store creation for %s", name));
|
||||
return info;
|
||||
}
|
||||
|
||||
info$store = Broker::create_master(name, info$backend, info$options);
|
||||
info$master = T;
|
||||
stores[name] = info;
|
||||
return info;
|
||||
}
|
||||
|
||||
if ( info$master_node == "" )
|
||||
{
|
||||
local mgr_nodes = nodes_with_type(Cluster::MANAGER);
|
||||
|
||||
if ( |mgr_nodes| == 0 )
|
||||
Reporter::fatal(fmt("empty master node name for cluster store " +
|
||||
"'%s', but there's no manager node to default",
|
||||
name));
|
||||
|
||||
info$master_node = mgr_nodes[0]$name;
|
||||
}
|
||||
else if ( info$master_node !in Cluster::nodes )
|
||||
Reporter::fatal(fmt("master node '%s' for cluster store '%s' does not exist",
|
||||
info$master_node, name));
|
||||
|
||||
if ( Cluster::node == info$master_node )
|
||||
{
|
||||
info$store = Broker::create_master(name, info$backend, info$options);
|
||||
info$master = T;
|
||||
stores[name] = info;
|
||||
Cluster::log(fmt("created master store: %s", name));
|
||||
return info;
|
||||
}
|
||||
|
||||
info$master = F;
|
||||
stores[name] = info;
|
||||
info$store = Broker::create_clone(info$name,
|
||||
info$clone_resync_interval,
|
||||
info$clone_stale_interval,
|
||||
info$clone_mutation_buffer_interval);
|
||||
Cluster::log(fmt("created clone store: %s", info$name));
|
||||
return info;
|
||||
}
|
||||
|
||||
function log(msg: string)
|
||||
{
|
||||
Log::write(Cluster::LOG, [$ts = network_time(), $node = node, $message = msg]);
|
||||
}
|
||||
|
|
458
scripts/base/frameworks/cluster/pools.bro
Normal file
458
scripts/base/frameworks/cluster/pools.bro
Normal file
|
@ -0,0 +1,458 @@
|
|||
##! Defines an interface for managing pools of cluster nodes. Pools are
|
||||
##! a useful way to distribute work or data among nodes within a cluster.
|
||||
|
||||
@load ./main
|
||||
@load base/utils/hash_hrw
|
||||
|
||||
module Cluster;
|
||||
|
||||
export {
|
||||
## Store state of a cluster within the context of a work pool.
|
||||
type PoolNode: record {
|
||||
## The node name (e.g. "manager").
|
||||
name: string;
|
||||
## An alias of *name* used to prevent hashing collisions when creating
|
||||
## *site_id*.
|
||||
alias: string;
|
||||
## A 32-bit unique identifier for the pool node, derived from name/alias.
|
||||
site_id: count;
|
||||
## Whether the node is currently alive and can receive work.
|
||||
alive: bool &default=F;
|
||||
};
|
||||
|
||||
## A pool specification.
|
||||
type PoolSpec: record {
|
||||
## A topic string that can be used to reach all nodes within a pool.
|
||||
topic: string &default = "";
|
||||
## The type of nodes that are contained within the pool.
|
||||
node_type: Cluster::NodeType &default = Cluster::PROXY;
|
||||
## The maximum number of nodes that may belong to the pool.
|
||||
## If not set, then all available nodes will be added to the pool,
|
||||
## else the cluster framework will automatically limit the pool
|
||||
## membership according to the threshhold.
|
||||
max_nodes: count &optional;
|
||||
## Whether the pool requires exclusive access to nodes. If true,
|
||||
## then *max_nodes* nodes will not be assigned to any other pool.
|
||||
## When using this flag, *max_nodes* must also be set.
|
||||
exclusive: bool &default = F;
|
||||
};
|
||||
|
||||
type PoolNodeTable: table[string] of PoolNode;
|
||||
type RoundRobinTable: table[string] of int;
|
||||
|
||||
## A pool used for distributing data/work among a set of cluster nodes.
|
||||
type Pool: record {
|
||||
## The specification of the pool that was used when registering it.
|
||||
spec: PoolSpec &default = PoolSpec();
|
||||
## Nodes in the pool, indexed by their name (e.g. "manager").
|
||||
nodes: PoolNodeTable &default = PoolNodeTable();
|
||||
## A list of nodes in the pool in a deterministic order.
|
||||
node_list: vector of PoolNode &default = vector();
|
||||
## The Rendezvous hashing structure.
|
||||
hrw_pool: HashHRW::Pool &default = HashHRW::Pool();
|
||||
## Round-Robin table indexed by arbitrary key and storing the next
|
||||
## index of *node_list* that will be eligible to receive work (if it's
|
||||
## alive at the time of next request).
|
||||
rr_key_seq: RoundRobinTable &default = RoundRobinTable();
|
||||
## Number of pool nodes that are currently alive.
|
||||
alive_count: count &default = 0;
|
||||
};
|
||||
|
||||
## The specification for :bro:see:`Cluster::proxy_pool`.
|
||||
global proxy_pool_spec: PoolSpec =
|
||||
PoolSpec($topic = "bro/cluster/pool/proxy",
|
||||
$node_type = Cluster::PROXY) &redef;
|
||||
|
||||
## The specification for :bro:see:`Cluster::worker_pool`.
|
||||
global worker_pool_spec: PoolSpec =
|
||||
PoolSpec($topic = "bro/cluster/pool/worker",
|
||||
$node_type = Cluster::WORKER) &redef;
|
||||
|
||||
## The specification for :bro:see:`Cluster::logger_pool`.
|
||||
global logger_pool_spec: PoolSpec =
|
||||
PoolSpec($topic = "bro/cluster/pool/logger",
|
||||
$node_type = Cluster::LOGGER) &redef;
|
||||
|
||||
## A pool containing all the proxy nodes of a cluster.
|
||||
## The pool's node membership/availability is automatically
|
||||
## maintained by the cluster framework.
|
||||
global proxy_pool: Pool;
|
||||
|
||||
## A pool containing all the worker nodes of a cluster.
|
||||
## The pool's node membership/availability is automatically
|
||||
## maintained by the cluster framework.
|
||||
global worker_pool: Pool;
|
||||
|
||||
## A pool containing all the logger nodes of a cluster.
|
||||
## The pool's node membership/availability is automatically
|
||||
## maintained by the cluster framework.
|
||||
global logger_pool: Pool;
|
||||
|
||||
## Registers and initializes a pool.
|
||||
global register_pool: function(spec: PoolSpec): Pool;
|
||||
|
||||
## Retrieve the topic associated with the node mapped via Rendezvous hash
|
||||
## of an arbitrary key.
|
||||
##
|
||||
## pool: the pool of nodes to consider.
|
||||
##
|
||||
## key: data used for input to the hashing function that will uniformly
|
||||
## distribute keys among available nodes.
|
||||
##
|
||||
## Returns: a topic string associated with a cluster node that is alive
|
||||
## or an empty string if nothing is alive.
|
||||
global hrw_topic: function(pool: Pool, key: any): string;
|
||||
|
||||
## Retrieve the topic associated with the node in a round-robin fashion.
|
||||
##
|
||||
## pool: the pool of nodes to consider.
|
||||
##
|
||||
## key: an arbitrary string to identify the purpose for which you're
|
||||
## requesting the topic. e.g. consider using namespacing of your script
|
||||
## like "Intel::cluster_rr_key".
|
||||
##
|
||||
## Returns: a topic string associated with a cluster node that is alive,
|
||||
## or an empty string if nothing is alive.
|
||||
global rr_topic: function(pool: Pool, key: string): string;
|
||||
|
||||
## Distributes log message topics among logger nodes via round-robin.
|
||||
## This will be automatically assigned to :bro:see:`Broker::log_topic`
|
||||
## if :bro:see:`Cluster::enable_round_robin_logging` is enabled.
|
||||
## If no logger nodes are active, then this will return the value
|
||||
## of :bro:see:`Broker::default_log_topic`.
|
||||
global rr_log_topic: function(id: Log::ID, path: string): string;
|
||||
}
|
||||
|
||||
## Initialize a node as a member of a pool.
|
||||
##
|
||||
## pool: the pool to which the node will belong.
|
||||
##
|
||||
## name: the name of the node (e.g. "manager").
|
||||
##
|
||||
## Returns: F if a node of the same name already exists in the pool, else T.
|
||||
global init_pool_node: function(pool: Pool, name: string): bool;
|
||||
|
||||
## Mark a pool node as alive/online/available. :bro:see:`Cluster::hrw_topic`
|
||||
## will distribute keys to nodes marked as alive.
|
||||
##
|
||||
## pool: the pool to which the node belongs.
|
||||
##
|
||||
## name: the name of the node to mark.
|
||||
##
|
||||
## Returns: F if the node does not exist in the pool, else T.
|
||||
global mark_pool_node_alive: function(pool: Pool, name: string): bool;
|
||||
|
||||
## Mark a pool node as dead/offline/unavailable. :bro:see:`Cluster::hrw_topic`
|
||||
## will not distribute keys to nodes marked as dead.
|
||||
##
|
||||
## pool: the pool to which the node belongs.
|
||||
##
|
||||
## name: the name of the node to mark.
|
||||
##
|
||||
## Returns: F if the node does not exist in the pool, else T.
|
||||
global mark_pool_node_dead: function(pool: Pool, name: string): bool;
|
||||
|
||||
global registered_pools: vector of Pool = vector();
|
||||
|
||||
function register_pool(spec: PoolSpec): Pool
|
||||
{
|
||||
local rval = Pool($spec = spec);
|
||||
registered_pools += rval;
|
||||
return rval;
|
||||
}
|
||||
|
||||
function hrw_topic(pool: Pool, key: any): string
|
||||
{
|
||||
if ( |pool$hrw_pool$sites| == 0 )
|
||||
return "";
|
||||
|
||||
local site = HashHRW::get_site(pool$hrw_pool, key);
|
||||
local pn: PoolNode = site$user_data;
|
||||
return node_topic_prefix + pn$name;
|
||||
}
|
||||
|
||||
function rr_topic(pool: Pool, key: string): string
|
||||
{
|
||||
if ( key !in pool$rr_key_seq )
|
||||
pool$rr_key_seq[key] = 0;
|
||||
|
||||
local next_idx = pool$rr_key_seq[key];
|
||||
local start = next_idx;
|
||||
local rval = "";
|
||||
|
||||
if ( next_idx >= |pool$node_list| )
|
||||
return rval;
|
||||
|
||||
while ( T )
|
||||
{
|
||||
local pn = pool$node_list[next_idx];
|
||||
|
||||
++next_idx;
|
||||
|
||||
if ( next_idx == |pool$node_list| )
|
||||
next_idx = 0;
|
||||
|
||||
if ( pn$alive )
|
||||
{
|
||||
rval = node_topic_prefix + pn$name;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( next_idx == start )
|
||||
# no nodes alive
|
||||
break;
|
||||
}
|
||||
|
||||
pool$rr_key_seq[key] = next_idx;
|
||||
return rval;
|
||||
}
|
||||
|
||||
function rr_log_topic(id: Log::ID, path: string): string
|
||||
{
|
||||
local rval = rr_topic(logger_pool, "Cluster::rr_log_topic");
|
||||
|
||||
if ( rval != "" )
|
||||
return rval;
|
||||
|
||||
rval = Broker::default_log_topic(id, path);
|
||||
return rval;
|
||||
}
|
||||
|
||||
event Cluster::node_up(name: string, id: string) &priority=10
|
||||
{
|
||||
for ( i in registered_pools )
|
||||
{
|
||||
local pool = registered_pools[i];
|
||||
|
||||
if ( name in pool$nodes )
|
||||
mark_pool_node_alive(pool, name);
|
||||
}
|
||||
}
|
||||
|
||||
event Cluster::node_down(name: string, id: string) &priority=10
|
||||
{
|
||||
for ( i in registered_pools )
|
||||
{
|
||||
local pool = registered_pools[i];
|
||||
|
||||
if ( name in pool$nodes )
|
||||
mark_pool_node_dead(pool, name);
|
||||
}
|
||||
}
|
||||
|
||||
function site_id_in_pool(pool: Pool, site_id: count): bool
|
||||
{
|
||||
for ( i in pool$nodes )
|
||||
{
|
||||
local pn = pool$nodes[i];
|
||||
|
||||
if ( pn$site_id == site_id )
|
||||
return T;
|
||||
}
|
||||
|
||||
return F;
|
||||
}
|
||||
|
||||
function init_pool_node(pool: Pool, name: string): bool
|
||||
{
|
||||
if ( name in pool$nodes )
|
||||
return F;
|
||||
|
||||
local loop = T;
|
||||
local c = 0;
|
||||
|
||||
while ( loop )
|
||||
{
|
||||
# site id collisions are unlikely, but using aliases handles it...
|
||||
# alternatively could terminate and ask user to pick a new node name
|
||||
# if it ends up colliding.
|
||||
local alias = name + fmt(".%s", c);
|
||||
local site_id = fnv1a32(alias);
|
||||
|
||||
if ( site_id_in_pool(pool, site_id) )
|
||||
++c;
|
||||
else
|
||||
{
|
||||
local pn = PoolNode($name=name, $alias=alias, $site_id=site_id,
|
||||
$alive=Cluster::node == name);
|
||||
pool$nodes[name] = pn;
|
||||
pool$node_list += pn;
|
||||
|
||||
if ( pn$alive )
|
||||
++pool$alive_count;
|
||||
|
||||
loop = F;
|
||||
}
|
||||
}
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
function mark_pool_node_alive(pool: Pool, name: string): bool
|
||||
{
|
||||
if ( name !in pool$nodes )
|
||||
return F;
|
||||
|
||||
local pn = pool$nodes[name];
|
||||
|
||||
if ( ! pn$alive )
|
||||
{
|
||||
pn$alive = T;
|
||||
++pool$alive_count;
|
||||
}
|
||||
|
||||
HashHRW::add_site(pool$hrw_pool, HashHRW::Site($id=pn$site_id, $user_data=pn));
|
||||
return T;
|
||||
}
|
||||
|
||||
function mark_pool_node_dead(pool: Pool, name: string): bool
|
||||
{
|
||||
if ( name !in pool$nodes )
|
||||
return F;
|
||||
|
||||
local pn = pool$nodes[name];
|
||||
|
||||
if ( pn$alive )
|
||||
{
|
||||
pn$alive = F;
|
||||
--pool$alive_count;
|
||||
}
|
||||
|
||||
HashHRW::rem_site(pool$hrw_pool, HashHRW::Site($id=pn$site_id, $user_data=pn));
|
||||
return T;
|
||||
}
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
worker_pool = register_pool(worker_pool_spec);
|
||||
proxy_pool = register_pool(proxy_pool_spec);
|
||||
logger_pool = register_pool(logger_pool_spec);
|
||||
}
|
||||
|
||||
type PoolEligibilityTracking: record {
|
||||
eligible_nodes: vector of NamedNode &default = vector();
|
||||
next_idx: count &default = 0;
|
||||
excluded: count &default = 0;
|
||||
};
|
||||
|
||||
global pool_eligibility: table[Cluster::NodeType] of PoolEligibilityTracking = table();
|
||||
|
||||
function pool_sorter(a: Pool, b: Pool): int
|
||||
{
|
||||
return strcmp(a$spec$topic, b$spec$topic);
|
||||
}
|
||||
|
||||
# Needs to execute before the bro_init in setup-connections
|
||||
event bro_init() &priority=-5
|
||||
{
|
||||
if ( ! Cluster::is_enabled() )
|
||||
return;
|
||||
|
||||
# Sorting now ensures the node distribution process is stable even if
|
||||
# there's a change in the order of time-of-registration between Bro runs.
|
||||
sort(registered_pools, pool_sorter);
|
||||
|
||||
pool_eligibility[Cluster::WORKER] =
|
||||
PoolEligibilityTracking($eligible_nodes = nodes_with_type(Cluster::WORKER));
|
||||
pool_eligibility[Cluster::PROXY] =
|
||||
PoolEligibilityTracking($eligible_nodes = nodes_with_type(Cluster::PROXY));
|
||||
pool_eligibility[Cluster::LOGGER] =
|
||||
PoolEligibilityTracking($eligible_nodes = nodes_with_type(Cluster::LOGGER));
|
||||
|
||||
if ( manager_is_logger )
|
||||
{
|
||||
local mgr = nodes_with_type(Cluster::MANAGER);
|
||||
|
||||
if ( |mgr| > 0 )
|
||||
{
|
||||
local eln = pool_eligibility[Cluster::LOGGER]$eligible_nodes;
|
||||
eln += mgr[0];
|
||||
}
|
||||
}
|
||||
|
||||
local pool: Pool;
|
||||
local pet: PoolEligibilityTracking;
|
||||
local en: vector of NamedNode;
|
||||
|
||||
for ( i in registered_pools )
|
||||
{
|
||||
pool = registered_pools[i];
|
||||
|
||||
if ( pool$spec$node_type !in pool_eligibility )
|
||||
Reporter::fatal(fmt("invalid pool node type: %s", pool$spec$node_type));
|
||||
|
||||
if ( ! pool$spec$exclusive )
|
||||
next;
|
||||
|
||||
if ( ! pool$spec?$max_nodes )
|
||||
Reporter::fatal("Cluster::PoolSpec 'max_nodes' field must be set when using the 'exclusive' flag");
|
||||
|
||||
pet = pool_eligibility[pool$spec$node_type];
|
||||
pet$excluded += pool$spec$max_nodes;
|
||||
}
|
||||
|
||||
for ( nt in pool_eligibility )
|
||||
{
|
||||
pet = pool_eligibility[nt];
|
||||
|
||||
if ( pet$excluded > |pet$eligible_nodes| )
|
||||
Reporter::fatal(fmt("not enough %s nodes to satisfy pool exclusivity requirements: need %d nodes", nt, pet$excluded));
|
||||
}
|
||||
|
||||
for ( i in registered_pools )
|
||||
{
|
||||
pool = registered_pools[i];
|
||||
|
||||
if ( ! pool$spec$exclusive )
|
||||
next;
|
||||
|
||||
pet = pool_eligibility[pool$spec$node_type];
|
||||
|
||||
local e = 0;
|
||||
|
||||
while ( e < pool$spec$max_nodes )
|
||||
{
|
||||
init_pool_node(pool, pet$eligible_nodes[e]$name);
|
||||
++e;
|
||||
}
|
||||
|
||||
local nen: vector of NamedNode = vector();
|
||||
|
||||
for ( j in pet$eligible_nodes )
|
||||
{
|
||||
if ( j < e )
|
||||
next;
|
||||
|
||||
nen += pet$eligible_nodes[j];
|
||||
}
|
||||
|
||||
pet$eligible_nodes = nen;
|
||||
}
|
||||
|
||||
for ( i in registered_pools )
|
||||
{
|
||||
pool = registered_pools[i];
|
||||
|
||||
if ( pool$spec$exclusive )
|
||||
next;
|
||||
|
||||
pet = pool_eligibility[pool$spec$node_type];
|
||||
local nodes_to_init = |pet$eligible_nodes|;
|
||||
|
||||
if ( pool$spec?$max_nodes &&
|
||||
pool$spec$max_nodes < |pet$eligible_nodes| )
|
||||
nodes_to_init = pool$spec$max_nodes;
|
||||
|
||||
local nodes_inited = 0;
|
||||
|
||||
while ( nodes_inited < nodes_to_init )
|
||||
{
|
||||
init_pool_node(pool, pet$eligible_nodes[pet$next_idx]$name);
|
||||
++nodes_inited;
|
||||
++pet$next_idx;
|
||||
|
||||
if ( pet$next_idx == |pet$eligible_nodes| )
|
||||
pet$next_idx = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,142 +2,125 @@
|
|||
##! as defined by :bro:id:`Cluster::nodes`.
|
||||
|
||||
@load ./main
|
||||
@load base/frameworks/communication
|
||||
|
||||
@if ( Cluster::node in Cluster::nodes )
|
||||
@load ./pools
|
||||
@load base/frameworks/broker
|
||||
|
||||
module Cluster;
|
||||
|
||||
event bro_init() &priority=9
|
||||
function connect_peer(node_type: NodeType, node_name: string)
|
||||
{
|
||||
local me = nodes[node];
|
||||
local nn = nodes_with_type(node_type);
|
||||
|
||||
for ( i in Cluster::nodes )
|
||||
for ( i in nn )
|
||||
{
|
||||
local n = nodes[i];
|
||||
local n = nn[i];
|
||||
|
||||
# Connections from the control node for runtime control and update events.
|
||||
# Every node in a cluster is eligible for control from this host.
|
||||
if ( n$node_type == CONTROL )
|
||||
Communication::nodes["control"] = [$host=n$ip, $zone_id=n$zone_id,
|
||||
$connect=F, $class="control",
|
||||
$events=control_events];
|
||||
if ( n$name != node_name )
|
||||
next;
|
||||
|
||||
if ( me$node_type == LOGGER )
|
||||
{
|
||||
if ( n$node_type == MANAGER && n$logger == node )
|
||||
Communication::nodes[i] =
|
||||
[$host=n$ip, $zone_id=n$zone_id, $connect=F,
|
||||
$class=i, $events=manager2logger_events, $request_logs=T];
|
||||
if ( n$node_type == PROXY && n$logger == node )
|
||||
Communication::nodes[i] =
|
||||
[$host=n$ip, $zone_id=n$zone_id, $connect=F,
|
||||
$class=i, $events=proxy2logger_events, $request_logs=T];
|
||||
if ( n$node_type == WORKER && n$logger == node )
|
||||
Communication::nodes[i] =
|
||||
[$host=n$ip, $zone_id=n$zone_id, $connect=F,
|
||||
$class=i, $events=worker2logger_events, $request_logs=T];
|
||||
}
|
||||
else if ( me$node_type == MANAGER )
|
||||
{
|
||||
if ( n$node_type == LOGGER && me$logger == i )
|
||||
Communication::nodes["logger"] =
|
||||
[$host=n$ip, $zone_id=n$zone_id, $p=n$p,
|
||||
$connect=T, $retry=retry_interval,
|
||||
$class=node];
|
||||
|
||||
if ( n$node_type == WORKER && n$manager == node )
|
||||
Communication::nodes[i] =
|
||||
[$host=n$ip, $zone_id=n$zone_id, $connect=F,
|
||||
$class=i, $events=worker2manager_events,
|
||||
$request_logs=Cluster::manager_is_logger];
|
||||
|
||||
if ( n$node_type == PROXY && n$manager == node )
|
||||
Communication::nodes[i] =
|
||||
[$host=n$ip, $zone_id=n$zone_id, $connect=F,
|
||||
$class=i, $events=proxy2manager_events,
|
||||
$request_logs=Cluster::manager_is_logger];
|
||||
|
||||
if ( n$node_type == TIME_MACHINE && me?$time_machine && me$time_machine == i )
|
||||
Communication::nodes["time-machine"] = [$host=nodes[i]$ip,
|
||||
$zone_id=nodes[i]$zone_id,
|
||||
$p=nodes[i]$p,
|
||||
$connect=T, $retry=retry_interval,
|
||||
$events=tm2manager_events];
|
||||
}
|
||||
|
||||
else if ( me$node_type == PROXY )
|
||||
{
|
||||
if ( n$node_type == LOGGER && me$logger == i )
|
||||
Communication::nodes["logger"] =
|
||||
[$host=n$ip, $zone_id=n$zone_id, $p=n$p,
|
||||
$connect=T, $retry=retry_interval,
|
||||
$class=node];
|
||||
|
||||
if ( n$node_type == WORKER && n$proxy == node )
|
||||
Communication::nodes[i] =
|
||||
[$host=n$ip, $zone_id=n$zone_id, $connect=F, $class=i,
|
||||
$sync=T, $auth=T, $events=worker2proxy_events];
|
||||
|
||||
# accepts connections from the previous one.
|
||||
# (This is not ideal for setups with many proxies)
|
||||
# FIXME: Once we're using multiple proxies, we should also figure out some $class scheme ...
|
||||
if ( n$node_type == PROXY )
|
||||
{
|
||||
if ( n?$proxy )
|
||||
Communication::nodes[i]
|
||||
= [$host=n$ip, $zone_id=n$zone_id, $p=n$p,
|
||||
$connect=T, $auth=F, $sync=T, $retry=retry_interval];
|
||||
else if ( me?$proxy && me$proxy == i )
|
||||
Communication::nodes[me$proxy]
|
||||
= [$host=nodes[i]$ip, $zone_id=nodes[i]$zone_id,
|
||||
$connect=F, $auth=T, $sync=T];
|
||||
}
|
||||
|
||||
# Finally the manager, to send it status updates.
|
||||
if ( n$node_type == MANAGER && me$manager == i )
|
||||
Communication::nodes["manager"] = [$host=nodes[i]$ip,
|
||||
$zone_id=nodes[i]$zone_id,
|
||||
$p=nodes[i]$p,
|
||||
$connect=T, $retry=retry_interval,
|
||||
$class=node,
|
||||
$events=manager2proxy_events];
|
||||
}
|
||||
else if ( me$node_type == WORKER )
|
||||
{
|
||||
if ( n$node_type == LOGGER && me$logger == i )
|
||||
Communication::nodes["logger"] =
|
||||
[$host=n$ip, $zone_id=n$zone_id, $p=n$p,
|
||||
$connect=T, $retry=retry_interval,
|
||||
$class=node];
|
||||
|
||||
if ( n$node_type == MANAGER && me$manager == i )
|
||||
Communication::nodes["manager"] = [$host=nodes[i]$ip,
|
||||
$zone_id=nodes[i]$zone_id,
|
||||
$p=nodes[i]$p,
|
||||
$connect=T, $retry=retry_interval,
|
||||
$class=node,
|
||||
$events=manager2worker_events];
|
||||
|
||||
if ( n$node_type == PROXY && me$proxy == i )
|
||||
Communication::nodes["proxy"] = [$host=nodes[i]$ip,
|
||||
$zone_id=nodes[i]$zone_id,
|
||||
$p=nodes[i]$p,
|
||||
$connect=T, $retry=retry_interval,
|
||||
$sync=T, $class=node,
|
||||
$events=proxy2worker_events];
|
||||
|
||||
if ( n$node_type == TIME_MACHINE &&
|
||||
me?$time_machine && me$time_machine == i )
|
||||
Communication::nodes["time-machine"] = [$host=nodes[i]$ip,
|
||||
$zone_id=nodes[i]$zone_id,
|
||||
$p=nodes[i]$p,
|
||||
$connect=T,
|
||||
$retry=retry_interval,
|
||||
$events=tm2worker_events];
|
||||
|
||||
}
|
||||
local status = Broker::peer(cat(n$node$ip), n$node$p,
|
||||
Cluster::retry_interval);
|
||||
Cluster::log(fmt("initiate peering with %s:%s, retry=%s, status=%s",
|
||||
n$node$ip, n$node$p, Cluster::retry_interval,
|
||||
status));
|
||||
}
|
||||
}
|
||||
|
||||
@endif
|
||||
function connect_peers_with_type(node_type: NodeType)
|
||||
{
|
||||
local rval: vector of NamedNode = vector();
|
||||
local nn = nodes_with_type(node_type);
|
||||
|
||||
for ( i in nn )
|
||||
{
|
||||
local n = nn[i];
|
||||
local status = Broker::peer(cat(n$node$ip), n$node$p,
|
||||
Cluster::retry_interval);
|
||||
Cluster::log(fmt("initiate peering with %s:%s, retry=%s, status=%s",
|
||||
n$node$ip, n$node$p, Cluster::retry_interval,
|
||||
status));
|
||||
}
|
||||
}
|
||||
|
||||
event bro_init() &priority=-10
|
||||
{
|
||||
if ( getenv("BROCTL_CHECK_CONFIG") != "" )
|
||||
return;
|
||||
|
||||
local self = nodes[node];
|
||||
|
||||
for ( i in registered_pools )
|
||||
{
|
||||
local pool = registered_pools[i];
|
||||
|
||||
if ( node in pool$nodes )
|
||||
Broker::subscribe(pool$spec$topic);
|
||||
}
|
||||
|
||||
switch ( self$node_type ) {
|
||||
case NONE:
|
||||
return;
|
||||
case CONTROL:
|
||||
break;
|
||||
case LOGGER:
|
||||
Broker::subscribe(Cluster::logger_topic);
|
||||
Broker::subscribe(Broker::default_log_topic_prefix);
|
||||
break;
|
||||
case MANAGER:
|
||||
Broker::subscribe(Cluster::manager_topic);
|
||||
|
||||
if ( Cluster::manager_is_logger )
|
||||
Broker::subscribe(Broker::default_log_topic_prefix);
|
||||
|
||||
break;
|
||||
case PROXY:
|
||||
Broker::subscribe(Cluster::proxy_topic);
|
||||
break;
|
||||
case WORKER:
|
||||
Broker::subscribe(Cluster::worker_topic);
|
||||
break;
|
||||
case TIME_MACHINE:
|
||||
Broker::subscribe(Cluster::time_machine_topic);
|
||||
break;
|
||||
default:
|
||||
Reporter::error(fmt("Unhandled cluster node type: %s", self$node_type));
|
||||
return;
|
||||
}
|
||||
|
||||
Broker::subscribe(Cluster::broadcast_topic);
|
||||
Broker::subscribe(node_topic(node));
|
||||
|
||||
Broker::listen(Broker::default_listen_address,
|
||||
self$p,
|
||||
Broker::default_listen_retry);
|
||||
|
||||
Cluster::log(fmt("listening on %s:%s", Broker::default_listen_address, self$p));
|
||||
|
||||
switch ( self$node_type ) {
|
||||
case MANAGER:
|
||||
connect_peers_with_type(LOGGER);
|
||||
|
||||
if ( self?$time_machine )
|
||||
connect_peer(TIME_MACHINE, self$time_machine);
|
||||
|
||||
break;
|
||||
case PROXY:
|
||||
connect_peers_with_type(LOGGER);
|
||||
|
||||
if ( self?$manager )
|
||||
connect_peer(MANAGER, self$manager);
|
||||
|
||||
break;
|
||||
case WORKER:
|
||||
connect_peers_with_type(LOGGER);
|
||||
connect_peers_with_type(PROXY);
|
||||
|
||||
if ( self?$manager )
|
||||
connect_peer(MANAGER, self$manager);
|
||||
|
||||
if ( self?$time_machine )
|
||||
connect_peer(TIME_MACHINE, self$time_machine);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
The communication framework facilitates connecting to remote Bro or
|
||||
Broccoli instances to share state and transfer events.
|
|
@ -1 +0,0 @@
|
|||
@load ./main
|
|
@ -1,354 +0,0 @@
|
|||
##! Facilitates connecting to remote Bro or Broccoli instances to share state
|
||||
##! and/or transfer events.
|
||||
|
||||
@load base/frameworks/packet-filter
|
||||
@load base/utils/addrs
|
||||
|
||||
module Communication;
|
||||
|
||||
export {
|
||||
|
||||
## The communication logging stream identifier.
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
## Which interface to listen on. The addresses ``0.0.0.0`` and ``[::]``
|
||||
## are wildcards.
|
||||
const listen_interface = 0.0.0.0 &redef;
|
||||
|
||||
## Which port to listen on. Note that BroControl sets this
|
||||
## automatically.
|
||||
const listen_port = 47757/tcp &redef;
|
||||
|
||||
## This defines if a listening socket should use SSL.
|
||||
const listen_ssl = F &redef;
|
||||
|
||||
## Defines if a listening socket can bind to IPv6 addresses.
|
||||
##
|
||||
## Note that this is overridden by the BroControl IPv6Comm option.
|
||||
const listen_ipv6 = F &redef;
|
||||
|
||||
## If :bro:id:`Communication::listen_interface` is a non-global
|
||||
## IPv6 address and requires a specific :rfc:`4007` ``zone_id``,
|
||||
## it can be specified here.
|
||||
const listen_ipv6_zone_id = "" &redef;
|
||||
|
||||
## Defines the interval at which to retry binding to
|
||||
## :bro:id:`Communication::listen_interface` on
|
||||
## :bro:id:`Communication::listen_port` if it's already in use.
|
||||
const listen_retry = 30 secs &redef;
|
||||
|
||||
## Default compression level. Compression level is 0-9, with 0 = no
|
||||
## compression.
|
||||
global compression_level = 0 &redef;
|
||||
|
||||
## A record type containing the column fields of the communication log.
|
||||
type Info: record {
|
||||
## The network time at which a communication event occurred.
|
||||
ts: time &log;
|
||||
## The peer name (if any) with which a communication event is
|
||||
## concerned.
|
||||
peer: string &log &optional;
|
||||
## Where the communication event message originated from, that
|
||||
## is, either from the scripting layer or inside the Bro process.
|
||||
src_name: string &log &optional;
|
||||
## .. todo:: currently unused.
|
||||
connected_peer_desc: string &log &optional;
|
||||
## .. todo:: currently unused.
|
||||
connected_peer_addr: addr &log &optional;
|
||||
## .. todo:: currently unused.
|
||||
connected_peer_port: port &log &optional;
|
||||
## The severity of the communication event message.
|
||||
level: string &log &optional;
|
||||
## A message describing the communication event between Bro or
|
||||
## Broccoli instances.
|
||||
message: string &log;
|
||||
};
|
||||
|
||||
## A remote peer to which we would like to talk.
|
||||
## If there's no entry for a peer, it may still connect
|
||||
## and request state, but not send us any.
|
||||
type Node: record {
|
||||
## Remote address.
|
||||
host: addr;
|
||||
|
||||
## If the *host* field is a non-global IPv6 address, this field
|
||||
## can specify a particular :rfc:`4007` ``zone_id``.
|
||||
zone_id: string &optional;
|
||||
|
||||
## Port of the remote Bro communication endpoint if we are
|
||||
## initiating the connection (based on the *connect* field).
|
||||
p: port &optional;
|
||||
|
||||
## When accepting a connection, the configuration only
|
||||
## applies if the class matches the one transmitted by
|
||||
## the peer.
|
||||
##
|
||||
## When initiating a connection, the class is sent to
|
||||
## the other side.
|
||||
class: string &optional;
|
||||
|
||||
## Events requested from remote side.
|
||||
events: pattern &optional;
|
||||
|
||||
## Whether we are going to connect (rather than waiting
|
||||
## for the other side to connect to us).
|
||||
connect: bool &default = F;
|
||||
|
||||
## If disconnected, reconnect after this many seconds.
|
||||
retry: interval &default = 0 secs;
|
||||
|
||||
## Whether to accept remote events.
|
||||
accept_input: bool &default = T;
|
||||
|
||||
## Whether to perform state synchronization with peer.
|
||||
sync: bool &default = F;
|
||||
|
||||
## Whether to request logs from the peer.
|
||||
request_logs: bool &default = F;
|
||||
|
||||
## When performing state synchronization, whether we consider
|
||||
## our state to be authoritative (only one side can be
|
||||
## authoritative). If so, we will send the peer our current
|
||||
## set when the connection is set up.
|
||||
auth: bool &default = F;
|
||||
|
||||
## If not set, no capture filter is sent.
|
||||
## If set to an empty string, then the default capture filter
|
||||
## is sent.
|
||||
capture_filter: string &optional;
|
||||
|
||||
## Whether to use SSL-based communication.
|
||||
ssl: bool &default = F;
|
||||
|
||||
## Compression level is 0-9, with 0 = no compression.
|
||||
compression: count &default = compression_level;
|
||||
|
||||
## The remote peer.
|
||||
peer: event_peer &optional;
|
||||
|
||||
## Indicates the status of the node.
|
||||
connected: bool &default = F;
|
||||
};
|
||||
|
||||
## The table of Bro or Broccoli nodes that Bro will initiate connections
|
||||
## to or respond to connections from. Note that BroControl sets this
|
||||
## automatically.
|
||||
global nodes: table[string] of Node &redef;
|
||||
|
||||
## A table of peer nodes for which this node issued a
|
||||
## :bro:id:`Communication::connect_peer` call but with which a connection
|
||||
## has not yet been established or with which a connection has been
|
||||
## closed and is currently in the process of retrying to establish.
|
||||
## When a connection is successfully established, the peer is removed
|
||||
## from the table.
|
||||
global pending_peers: table[peer_id] of Node;
|
||||
|
||||
## A table of peer nodes for which this node has an established connection.
|
||||
## Peers are automatically removed if their connection is closed and
|
||||
## automatically added back if a connection is re-established later.
|
||||
global connected_peers: table[peer_id] of Node;
|
||||
|
||||
## Connect to a node in :bro:id:`Communication::nodes` independent
|
||||
## of its "connect" flag.
|
||||
##
|
||||
## peer: the string used to index a particular node within the
|
||||
## :bro:id:`Communication::nodes` table.
|
||||
global connect_peer: function(peer: string);
|
||||
}
|
||||
|
||||
const src_names = {
|
||||
[REMOTE_SRC_CHILD] = "child",
|
||||
[REMOTE_SRC_PARENT] = "parent",
|
||||
[REMOTE_SRC_SCRIPT] = "script",
|
||||
};
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(Communication::LOG, [$columns=Info, $path="communication"]);
|
||||
}
|
||||
|
||||
function do_script_log_common(level: count, src: count, msg: string)
|
||||
{
|
||||
Log::write(Communication::LOG, [$ts = network_time(),
|
||||
$level = (level == REMOTE_LOG_INFO ? "info" : "error"),
|
||||
$src_name = src_names[src],
|
||||
$peer = get_event_peer()$descr,
|
||||
$message = msg]);
|
||||
}
|
||||
|
||||
# This is a core generated event.
|
||||
event remote_log(level: count, src: count, msg: string)
|
||||
{
|
||||
do_script_log_common(level, src, msg);
|
||||
}
|
||||
|
||||
# This is a core generated event.
|
||||
event remote_log_peer(p: event_peer, level: count, src: count, msg: string)
|
||||
{
|
||||
local rmsg = fmt("[#%d/%s:%d] %s", p$id, addr_to_uri(p$host), p$p, msg);
|
||||
do_script_log_common(level, src, rmsg);
|
||||
}
|
||||
|
||||
function do_script_log(p: event_peer, msg: string)
|
||||
{
|
||||
do_script_log_common(REMOTE_LOG_INFO, REMOTE_SRC_SCRIPT, msg);
|
||||
}
|
||||
|
||||
function connect_peer(peer: string)
|
||||
{
|
||||
local node = nodes[peer];
|
||||
local p = listen_port;
|
||||
|
||||
if ( node?$p )
|
||||
p = node$p;
|
||||
|
||||
local class = node?$class ? node$class : "";
|
||||
local zone_id = node?$zone_id ? node$zone_id : "";
|
||||
local id = connect(node$host, zone_id, p, class, node$retry, node$ssl);
|
||||
|
||||
if ( id == PEER_ID_NONE )
|
||||
Log::write(Communication::LOG, [$ts = network_time(),
|
||||
$peer = get_event_peer()$descr,
|
||||
$message = "can't trigger connect"]);
|
||||
pending_peers[id] = node;
|
||||
}
|
||||
|
||||
|
||||
function setup_peer(p: event_peer, node: Node)
|
||||
{
|
||||
if ( node?$events )
|
||||
{
|
||||
do_script_log(p, fmt("requesting events matching %s", node$events));
|
||||
request_remote_events(p, node$events);
|
||||
}
|
||||
|
||||
if ( node?$capture_filter && node$capture_filter != "" )
|
||||
{
|
||||
local filter = node$capture_filter;
|
||||
do_script_log(p, fmt("sending capture_filter: %s", filter));
|
||||
send_capture_filter(p, filter);
|
||||
}
|
||||
|
||||
if ( node$accept_input )
|
||||
{
|
||||
do_script_log(p, "accepting state");
|
||||
set_accept_state(p, T);
|
||||
}
|
||||
|
||||
set_compression_level(p, node$compression);
|
||||
|
||||
if ( node$sync )
|
||||
{
|
||||
do_script_log(p, "requesting synchronized state");
|
||||
request_remote_sync(p, node$auth);
|
||||
}
|
||||
|
||||
if ( node$request_logs )
|
||||
{
|
||||
do_script_log(p, "requesting logs");
|
||||
request_remote_logs(p);
|
||||
}
|
||||
|
||||
node$peer = p;
|
||||
node$connected = T;
|
||||
connected_peers[p$id] = node;
|
||||
}
|
||||
|
||||
event remote_connection_established(p: event_peer)
|
||||
{
|
||||
if ( is_remote_event() )
|
||||
return;
|
||||
|
||||
do_script_log(p, "connection established");
|
||||
|
||||
if ( p$id in pending_peers )
|
||||
{
|
||||
# We issued the connect.
|
||||
local node = pending_peers[p$id];
|
||||
setup_peer(p, node);
|
||||
delete pending_peers[p$id];
|
||||
}
|
||||
else
|
||||
{ # The other side connected to us.
|
||||
local found = F;
|
||||
for ( i in nodes )
|
||||
{
|
||||
node = nodes[i];
|
||||
if ( node$host == p$host )
|
||||
{
|
||||
local c = 0;
|
||||
|
||||
# See if classes match = either both have
|
||||
# the same class, or neither of them has
|
||||
# a class.
|
||||
if ( p?$class && p$class != "" )
|
||||
++c;
|
||||
|
||||
if ( node?$class && node$class != "" )
|
||||
++c;
|
||||
|
||||
if ( c == 1 ||
|
||||
(c == 2 && p$class != node$class) )
|
||||
next;
|
||||
|
||||
found = T;
|
||||
setup_peer(p, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! found )
|
||||
set_compression_level(p, compression_level);
|
||||
}
|
||||
|
||||
complete_handshake(p);
|
||||
}
|
||||
|
||||
event remote_connection_closed(p: event_peer)
|
||||
{
|
||||
if ( is_remote_event() )
|
||||
return;
|
||||
|
||||
do_script_log(p, "connection closed");
|
||||
|
||||
if ( p$id in connected_peers )
|
||||
{
|
||||
local node = connected_peers[p$id];
|
||||
node$connected = F;
|
||||
|
||||
delete connected_peers[p$id];
|
||||
|
||||
if ( node$retry != 0secs )
|
||||
# The core will retry.
|
||||
pending_peers[p$id] = node;
|
||||
}
|
||||
}
|
||||
|
||||
event remote_state_inconsistency(operation: string, id: string,
|
||||
expected_old: string, real_old: string)
|
||||
{
|
||||
if ( is_remote_event() )
|
||||
return;
|
||||
|
||||
local msg = fmt("state inconsistency: %s should be %s but is %s before %s",
|
||||
id, expected_old, real_old, operation);
|
||||
Log::write(Communication::LOG, [$ts = network_time(),
|
||||
$peer = get_event_peer()$descr,
|
||||
$message = msg]);
|
||||
}
|
||||
|
||||
|
||||
# Actually initiate the connections that need to be established.
|
||||
event bro_init() &priority = -10 # let others modify nodes
|
||||
{
|
||||
if ( |nodes| > 0 )
|
||||
enable_communication();
|
||||
|
||||
for ( tag in nodes )
|
||||
{
|
||||
if ( ! nodes[tag]$connect )
|
||||
next;
|
||||
|
||||
connect_peer(tag);
|
||||
}
|
||||
}
|
2
scripts/base/frameworks/config/README
Normal file
2
scripts/base/frameworks/config/README
Normal file
|
@ -0,0 +1,2 @@
|
|||
The configuration framework provides a way to change the Bro configuration
|
||||
in "option" values at run-time.
|
2
scripts/base/frameworks/config/__load__.bro
Normal file
2
scripts/base/frameworks/config/__load__.bro
Normal file
|
@ -0,0 +1,2 @@
|
|||
@load ./main
|
||||
@load ./input
|
77
scripts/base/frameworks/config/input.bro
Normal file
77
scripts/base/frameworks/config/input.bro
Normal file
|
@ -0,0 +1,77 @@
|
|||
##! File input for the configuration framework using the input framework.
|
||||
|
||||
@load ./main
|
||||
@load base/frameworks/cluster
|
||||
|
||||
module Config;
|
||||
|
||||
export {
|
||||
## Configuration files that will be read off disk. Files are reread
|
||||
## every time they are updated so updates should be atomic with "mv"
|
||||
## instead of writing the file in place.
|
||||
##
|
||||
## If the same configuration option is defined in several files with
|
||||
## different values, behavior is unspecified.
|
||||
const config_files: set[string] = {} &redef;
|
||||
|
||||
## Read specified configuration file and apply values; updates to file
|
||||
## are not tracked.
|
||||
global read_config: function(filename: string);
|
||||
}
|
||||
|
||||
global current_config: table[string] of string = table();
|
||||
|
||||
type ConfigItem: record {
|
||||
option_nv: string;
|
||||
};
|
||||
|
||||
type EventFields: record {
|
||||
option_name: string;
|
||||
option_val: string;
|
||||
};
|
||||
|
||||
event config_line(description: Input::EventDescription, tpe: Input::Event, p: EventFields)
|
||||
{
|
||||
}
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
|
||||
return;
|
||||
|
||||
for ( fi in config_files )
|
||||
Input::add_table([$reader=Input::READER_CONFIG,
|
||||
$mode=Input::REREAD,
|
||||
$source=fi,
|
||||
$name=cat("config-", fi),
|
||||
$idx=ConfigItem,
|
||||
$val=ConfigItem,
|
||||
$want_record=F,
|
||||
$destination=current_config]);
|
||||
}
|
||||
|
||||
event InputConfig::new_value(name: string, source: string, id: string, value: any)
|
||||
{
|
||||
if ( sub_bytes(name, 1, 15) != "config-oneshot-" && source !in config_files )
|
||||
return;
|
||||
|
||||
Config::set_value(id, value, source);
|
||||
}
|
||||
|
||||
function read_config(filename: string)
|
||||
{
|
||||
# Only read the configuration on the manager. The other nodes are being fed from
|
||||
# the manager.
|
||||
if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
|
||||
return;
|
||||
|
||||
local iname = cat("config-oneshot-", filename);
|
||||
|
||||
Input::add_event([$reader=Input::READER_CONFIG,
|
||||
$mode=Input::MANUAL,
|
||||
$source=filename,
|
||||
$name=iname,
|
||||
$fields=EventFields,
|
||||
$ev=config_line]);
|
||||
Input::remove(iname);
|
||||
}
|
165
scripts/base/frameworks/config/main.bro
Normal file
165
scripts/base/frameworks/config/main.bro
Normal file
|
@ -0,0 +1,165 @@
|
|||
##! The configuration framework provides a way to change Bro options
|
||||
##! (as specified by the "option" keyword) at runtime. It also logs runtime
|
||||
##! changes to options to config.log.
|
||||
|
||||
@load base/frameworks/cluster
|
||||
|
||||
module Config;
|
||||
|
||||
export {
|
||||
## The config logging stream identifier.
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
## Represents the data in config.log.
|
||||
type Info: record {
|
||||
## Timestamp at which the configuration change occured.
|
||||
ts: time &log;
|
||||
## ID of the value that was changed.
|
||||
id: string &log;
|
||||
## Value before the change.
|
||||
old_value: string &log;
|
||||
## Value after the change.
|
||||
new_value: string &log;
|
||||
## Optional location that triggered the change.
|
||||
location: string &optional &log;
|
||||
};
|
||||
|
||||
## Event that can be handled to access the :bro:type:`Config::Info`
|
||||
## record as it is sent on to the logging framework.
|
||||
global log_config: event(rec: Info);
|
||||
|
||||
## Broker topic for announcing new configuration value. Sending new_value,
|
||||
## peers can send configuration changes that will be distributed accross
|
||||
## the entire cluster.
|
||||
const change_topic = "bro/config/change";
|
||||
|
||||
## This function is the config framework layer around the lower-level
|
||||
## :bro:see:`Option::set` call. Config::set_value will set the configuration
|
||||
## value for all nodes in the cluster, no matter where it was called. Note
|
||||
## that `bro:see:`Option::set` does not distribute configuration changes
|
||||
## to other nodes.
|
||||
##
|
||||
## ID: The ID of the option to update.
|
||||
##
|
||||
## val: The new value of the option.
|
||||
##
|
||||
## location: Optional parameter detailing where this change originated from.
|
||||
##
|
||||
## Returns: true on success, false when an error ocured.
|
||||
global set_value: function(ID: string, val: any, location: string &default = "" &optional): bool;
|
||||
}
|
||||
|
||||
@if ( Cluster::is_enabled() )
|
||||
type OptionCacheValue: record {
|
||||
val: any;
|
||||
location: string;
|
||||
};
|
||||
|
||||
global option_cache: table[string] of OptionCacheValue;
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
Broker::subscribe(change_topic);
|
||||
}
|
||||
|
||||
event Config::cluster_set_option(ID: string, val: any, location: string)
|
||||
{
|
||||
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
option_cache[ID] = OptionCacheValue($val=val, $location=location);
|
||||
@endif
|
||||
Option::set(ID, val, location);
|
||||
}
|
||||
|
||||
function set_value(ID: string, val: any, location: string &default = "" &optional): bool
|
||||
{
|
||||
local cache_val: any;
|
||||
# first cache value in case setting it succeeds and we have to store it.
|
||||
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
cache_val = copy(val);
|
||||
# First try setting it locally - abort if not possible.
|
||||
if ( ! Option::set(ID, val, location) )
|
||||
return F;
|
||||
# If setting worked, copy the new value into the cache on the manager
|
||||
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
option_cache[ID] = OptionCacheValue($val=cache_val, $location=location);
|
||||
|
||||
# If it turns out that it is possible - send it to everyone else to apply.
|
||||
Broker::publish(change_topic, Config::cluster_set_option, ID, val, location);
|
||||
|
||||
if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||
{
|
||||
Broker::relay(change_topic, change_topic, Config::cluster_set_option, ID, val, location);
|
||||
}
|
||||
return T;
|
||||
}
|
||||
@else
|
||||
# Standalone implementation
|
||||
function set_value(ID: string, val: any, location: string &default = "" &optional): bool
|
||||
{
|
||||
return Option::set(ID, val, location);
|
||||
}
|
||||
@endif
|
||||
|
||||
@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER )
|
||||
# Handling of new worker nodes.
|
||||
event Cluster::node_up(name: string, id: string) &priority=-10
|
||||
{
|
||||
# When a node connects, send it all current Option values.
|
||||
if ( name in Cluster::nodes )
|
||||
for ( ID in option_cache )
|
||||
Broker::publish(Cluster::node_topic(name), Config::cluster_set_option, ID, option_cache[ID]$val, option_cache[ID]$location);
|
||||
}
|
||||
@endif
|
||||
|
||||
|
||||
function format_value(value: any) : string
|
||||
{
|
||||
local tn = type_name(value);
|
||||
local part: string_vec = vector();
|
||||
if ( /^set/ in tn )
|
||||
{
|
||||
local it: set[bool] = value;
|
||||
for ( sv in it )
|
||||
part += cat(sv);
|
||||
return join_string_vec(part, ",");
|
||||
}
|
||||
else if ( /^vector/ in tn )
|
||||
{
|
||||
local vit: vector of any = value;
|
||||
for ( i in vit )
|
||||
part += cat(vit[i]);
|
||||
return join_string_vec(part, ",");
|
||||
}
|
||||
else if ( tn == "string" )
|
||||
return value;
|
||||
|
||||
return cat(value);
|
||||
}
|
||||
|
||||
function config_option_changed(ID: string, new_value: any, location: string): any
|
||||
{
|
||||
local log = Info($ts=network_time(), $id=ID, $old_value=format_value(lookup_ID(ID)), $new_value=format_value(new_value));
|
||||
if ( location != "" )
|
||||
log$location = location;
|
||||
Log::write(LOG, log);
|
||||
return new_value;
|
||||
}
|
||||
|
||||
event bro_init() &priority=10
|
||||
{
|
||||
Log::create_stream(LOG, [$columns=Info, $ev=log_config, $path="config"]);
|
||||
|
||||
# Limit logging to the manager - everyone else just feeds off it.
|
||||
@if ( !Cluster::is_enabled() || Cluster::local_node_type() == Cluster::MANAGER )
|
||||
# Iterate over all existing options and add ourselves as change handlers with
|
||||
# a low priority so that we can log the changes.
|
||||
local gids = global_ids();
|
||||
for ( i in gids )
|
||||
{
|
||||
if ( ! gids[i]$option_value )
|
||||
next;
|
||||
|
||||
Option::set_change_handler(i, config_option_changed, -100);
|
||||
}
|
||||
@endif
|
||||
}
|
|
@ -5,6 +5,13 @@
|
|||
module Control;
|
||||
|
||||
export {
|
||||
## The topic prefix used for exchanging control messages via Broker.
|
||||
const topic_prefix = "bro/control";
|
||||
|
||||
## Whether the controllee should call :bro:see:`Broker::listen`.
|
||||
## In a cluster, this isn't needed since the setup process calls it.
|
||||
const controllee_listen = T &redef;
|
||||
|
||||
## The address of the host that will be controlled.
|
||||
const host = 0.0.0.0 &redef;
|
||||
|
||||
|
@ -22,12 +29,6 @@ export {
|
|||
## This can be used by commands that take an argument.
|
||||
const arg = "" &redef;
|
||||
|
||||
## Events that need to be handled by controllers.
|
||||
const controller_events = /Control::.*_request/ &redef;
|
||||
|
||||
## Events that need to be handled by controllees.
|
||||
const controllee_events = /Control::.*_response/ &redef;
|
||||
|
||||
## The commands that can currently be given on the command line for
|
||||
## remote control.
|
||||
const commands: set[string] = {
|
||||
|
@ -73,8 +74,7 @@ export {
|
|||
global shutdown_response: event();
|
||||
}
|
||||
|
||||
|
||||
event terminate_event()
|
||||
{
|
||||
terminate_communication();
|
||||
terminate();
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ signature file-reg-utf16 {
|
|||
|
||||
# Microsoft Registry format (typically DESKTOP.DAT)
|
||||
signature file-regf {
|
||||
file-mime "application vnd.ms-regf", 49
|
||||
file-mime "application/vnd.ms-regf", 49
|
||||
file-magic /^\x72\x65\x67\x66/
|
||||
}
|
||||
|
||||
|
|
|
@ -135,6 +135,20 @@ export {
|
|||
## The default per-file reassembly buffer size.
|
||||
const reassembly_buffer_size = 524288 &redef;
|
||||
|
||||
## Lookup to see if a particular file id exists and is still valid.
|
||||
##
|
||||
## fuid: the file id.
|
||||
##
|
||||
## Returns: T if the file uid is known.
|
||||
global file_exists: function(fuid: string): bool;
|
||||
|
||||
## Lookup an :bro:see:`fa_file` record with the file id.
|
||||
##
|
||||
## fuid: the file id.
|
||||
##
|
||||
## Returns: the associated :bro:see:`fa_file` record.
|
||||
global lookup_file: function(fuid: string): fa_file;
|
||||
|
||||
## Allows the file reassembler to be used if it's necessary because the
|
||||
## file is transferred out of order.
|
||||
##
|
||||
|
@ -338,6 +352,16 @@ function set_info(f: fa_file)
|
|||
f$info$is_orig = f$is_orig;
|
||||
}
|
||||
|
||||
function file_exists(fuid: string): bool
|
||||
{
|
||||
return __file_exists(fuid);
|
||||
}
|
||||
|
||||
function lookup_file(fuid: string): fa_file
|
||||
{
|
||||
return __lookup_file(fuid);
|
||||
}
|
||||
|
||||
function set_timeout_interval(f: fa_file, t: interval): bool
|
||||
{
|
||||
return __set_timeout_interval(f$id, t);
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
@load ./readers/raw
|
||||
@load ./readers/benchmark
|
||||
@load ./readers/binary
|
||||
@load ./readers/config
|
||||
@load ./readers/sqlite
|
||||
|
|
|
@ -9,7 +9,7 @@ export {
|
|||
## Please note that the separator has to be exactly one character long.
|
||||
const separator = Input::separator &redef;
|
||||
|
||||
## Separator between set elements.
|
||||
## Separator between set and vector elements.
|
||||
## Please note that the separator has to be exactly one character long.
|
||||
const set_separator = Input::set_separator &redef;
|
||||
|
||||
|
@ -18,4 +18,33 @@ export {
|
|||
|
||||
## String to use for an unset &optional field.
|
||||
const unset_field = Input::unset_field &redef;
|
||||
|
||||
## Fail on invalid lines. If set to false, the ascii
|
||||
## input reader will jump over invalid lines, reporting
|
||||
## warnings in reporter.log. If set to true, errors in
|
||||
## input lines will be handled as fatal errors for the
|
||||
## reader thread; reading will abort immediately and
|
||||
## an error will be logged to reporter.log.
|
||||
## Individual readers can use a different value using
|
||||
## the $config table.
|
||||
## fail_on_invalid_lines = T was the default behavior
|
||||
## until Bro 2.6.
|
||||
const fail_on_invalid_lines = F &redef;
|
||||
|
||||
## Fail on file read problems. If set to true, the ascii
|
||||
## input reader will fail when encountering any problems
|
||||
## while reading a file different from invalid lines.
|
||||
## Examples of such problems are permission problems, or
|
||||
## missing files.
|
||||
## When set to false, these problems will be ignored. This
|
||||
## has an especially big effect for the REREAD mode, which will
|
||||
## seamlessly recover from read errors when a file is
|
||||
## only temporarily inaccessible. For MANUAL or STREAM files,
|
||||
## errors will most likely still be fatal since no automatic
|
||||
## re-reading of the file is attempted.
|
||||
## Individual readers can use a different value using
|
||||
## the $config table.
|
||||
## fail_on_file_problem = T was the default behavior
|
||||
## until Bro 2.6.
|
||||
const fail_on_file_problem = F &redef;
|
||||
}
|
||||
|
|
44
scripts/base/frameworks/input/readers/config.bro
Normal file
44
scripts/base/frameworks/input/readers/config.bro
Normal file
|
@ -0,0 +1,44 @@
|
|||
##! Interface for the config input reader.
|
||||
|
||||
module InputConfig;
|
||||
|
||||
export {
|
||||
## Separator between set and vector elements.
|
||||
## Please note that the separator has to be exactly one character long.
|
||||
const set_separator = Input::set_separator &redef;
|
||||
|
||||
## String to use for empty fields.
|
||||
## By default this is the empty string, meaning that an empty input field
|
||||
## will result in an empty set.
|
||||
const empty_field = "" &redef;
|
||||
|
||||
## Fail on file read problems. If set to true, the config
|
||||
## input reader will fail when encountering any problems
|
||||
## while reading a file different from invalid lines.
|
||||
## Examples of such problems are permission problems, or
|
||||
## missing files.
|
||||
## When set to false, these problems will be ignored. This
|
||||
## has an especially big effect for the REREAD mode, which will
|
||||
## seamlessly recover from read errors when a file is
|
||||
## only temporarily inaccessible. For MANUAL or STREAM files,
|
||||
## errors will most likely still be fatal since no automatic
|
||||
## re-reading of the file is attempted.
|
||||
## Individual readers can use a different value using
|
||||
## the $config table.
|
||||
const fail_on_file_problem = F &redef;
|
||||
|
||||
## Event that is called when a config option is added or changes.
|
||||
##
|
||||
## Note - this does not track the reason for a change (new, changed),
|
||||
## and also does not track removals. If you need this, combine the event
|
||||
## with a table reader.
|
||||
##
|
||||
## name: Name of the input stream.
|
||||
##
|
||||
## source: Source of the input stream.
|
||||
##
|
||||
## id: ID of the configuration option being set.
|
||||
##
|
||||
## value: New value of the configuration option being set.
|
||||
global new_value: event(name: string, source: string, id: string, value: any);
|
||||
}
|
|
@ -6,69 +6,100 @@
|
|||
|
||||
module Intel;
|
||||
|
||||
redef record Item += {
|
||||
## This field is used internally for cluster transparency to avoid
|
||||
## re-dispatching intelligence items over and over from workers.
|
||||
first_dispatch: bool &default=T;
|
||||
};
|
||||
export {
|
||||
## Broker topic for management of intel items. Sending insert_item and
|
||||
## remove_item events, peers can manage intelligence data.
|
||||
const item_topic = "bro/intel/items" &redef;
|
||||
|
||||
## Broker topic for management of intel indicators as stored on workers
|
||||
## for matching. Sending insert_indicator and remove_indicator events,
|
||||
## the back-end manages indicators.
|
||||
const indicator_topic = "bro/intel/indicators" &redef;
|
||||
|
||||
## Broker topic for matching events, generated by workers and sent to
|
||||
## the back-end for metadata enrichment and logging.
|
||||
const match_topic = "bro/intel/match" &redef;
|
||||
}
|
||||
|
||||
# Internal events for cluster data distribution.
|
||||
global insert_item: event(item: Item);
|
||||
global insert_indicator: event(item: Item);
|
||||
|
||||
# If this process is not a manager process, we don't want the full metadata.
|
||||
@if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||
redef have_full_data = F;
|
||||
@endif
|
||||
|
||||
# Internal event for cluster data distribution.
|
||||
global cluster_new_item: event(item: Item);
|
||||
|
||||
# Primary intelligence management is done by the manager.
|
||||
# The manager informs the workers about new items and item removal.
|
||||
redef Cluster::manager2worker_events += /^Intel::(cluster_new_item|purge_item)$/;
|
||||
# A worker queries the manager to insert, remove or indicate the match of an item.
|
||||
redef Cluster::worker2manager_events += /^Intel::(cluster_new_item|remove_item|match_no_items)$/;
|
||||
|
||||
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
event bro_init()
|
||||
{
|
||||
Broker::subscribe(item_topic);
|
||||
Broker::subscribe(match_topic);
|
||||
|
||||
Broker::auto_publish(indicator_topic, remove_indicator);
|
||||
}
|
||||
|
||||
# Handling of new worker nodes.
|
||||
event remote_connection_handshake_done(p: event_peer)
|
||||
event Cluster::node_up(name: string, id: string)
|
||||
{
|
||||
# When a worker connects, send it the complete minimal data store.
|
||||
# It will be kept up to date after this by the cluster_new_item event.
|
||||
if ( Cluster::nodes[p$descr]$node_type == Cluster::WORKER )
|
||||
# It will be kept up to date after this by the insert_indicator event.
|
||||
if ( name in Cluster::nodes && Cluster::nodes[name]$node_type == Cluster::WORKER )
|
||||
{
|
||||
send_id(p, "Intel::min_data_store");
|
||||
Broker::publish_id(Cluster::node_topic(name), "Intel::min_data_store");
|
||||
}
|
||||
}
|
||||
|
||||
# Handling of matches triggered by worker nodes.
|
||||
event Intel::match_no_items(s: Seen) &priority=5
|
||||
# On the manager, the new_item event indicates a new indicator that
|
||||
# has to be distributed.
|
||||
event Intel::new_item(item: Item) &priority=5
|
||||
{
|
||||
if ( Cluster::proxy_pool$alive_count == 0 )
|
||||
Broker::publish(indicator_topic, Intel::insert_indicator, item);
|
||||
else
|
||||
Cluster::relay_rr(Cluster::proxy_pool, "Intel::new_item_relay_rr",
|
||||
indicator_topic, Intel::insert_indicator, item);
|
||||
}
|
||||
|
||||
# Handling of item insertion triggered by remote node.
|
||||
event Intel::insert_item(item: Intel::Item) &priority=5
|
||||
{
|
||||
Intel::_insert(item, T);
|
||||
}
|
||||
|
||||
# Handling of item removal triggered by remote node.
|
||||
event Intel::remove_item(item: Item, purge_indicator: bool) &priority=5
|
||||
{
|
||||
remove(item, purge_indicator);
|
||||
}
|
||||
|
||||
# Handling of match triggered by remote node.
|
||||
event Intel::match_remote(s: Seen) &priority=5
|
||||
{
|
||||
if ( Intel::find(s) )
|
||||
event Intel::match(s, Intel::get_items(s));
|
||||
}
|
||||
|
||||
# Handling of item removal triggered by worker nodes.
|
||||
event Intel::remove_item(item: Item, purge_indicator: bool)
|
||||
{
|
||||
remove(item, purge_indicator);
|
||||
}
|
||||
@endif
|
||||
|
||||
# Handling of item insertion.
|
||||
event Intel::new_item(item: Intel::Item) &priority=5
|
||||
@if ( Cluster::local_node_type() == Cluster::WORKER )
|
||||
event bro_init()
|
||||
{
|
||||
# The cluster manager always rebroadcasts intelligence.
|
||||
# Workers redistribute it if it was locally generated.
|
||||
if ( Cluster::local_node_type() == Cluster::MANAGER ||
|
||||
item$first_dispatch )
|
||||
{
|
||||
item$first_dispatch=F;
|
||||
event Intel::cluster_new_item(item);
|
||||
}
|
||||
Broker::subscribe(indicator_topic);
|
||||
|
||||
Broker::auto_publish(match_topic, match_remote);
|
||||
Broker::auto_publish(item_topic, remove_item);
|
||||
}
|
||||
|
||||
# Handling of item insertion by remote node.
|
||||
event Intel::cluster_new_item(item: Intel::Item) &priority=5
|
||||
# On a worker, the new_item event requires to trigger the insertion
|
||||
# on the manager to update the back-end data store.
|
||||
event Intel::new_item(item: Intel::Item) &priority=5
|
||||
{
|
||||
# Ignore locally generated events to avoid event storms.
|
||||
if ( is_remote_event() )
|
||||
Intel::insert(item);
|
||||
Broker::publish(item_topic, Intel::insert_item, item);
|
||||
}
|
||||
|
||||
# Handling of new indicators published by the manager.
|
||||
event Intel::insert_indicator(item: Intel::Item) &priority=5
|
||||
{
|
||||
Intel::_insert(item, F);
|
||||
}
|
||||
@endif
|
||||
|
|
|
@ -177,12 +177,12 @@ export {
|
|||
}
|
||||
|
||||
# Internal handler for matches with no metadata available.
|
||||
global match_no_items: event(s: Seen);
|
||||
global match_remote: event(s: Seen);
|
||||
|
||||
# Internal events for cluster data distribution.
|
||||
# Internal events for (cluster) data distribution.
|
||||
global new_item: event(item: Item);
|
||||
global remove_item: event(item: Item, purge_indicator: bool);
|
||||
global purge_item: event(item: Item);
|
||||
global remove_indicator: event(item: Item);
|
||||
|
||||
# Optionally store metadata. This is used internally depending on
|
||||
# if this is a cluster deployment or not.
|
||||
|
@ -248,7 +248,7 @@ function expire_subnet_data(data: table[subnet] of MetaDataTable, idx: subnet):
|
|||
for ( src in meta_tbl )
|
||||
add metas[meta_tbl[src]];
|
||||
|
||||
return expire_item(cat(idx), ADDR, metas);
|
||||
return expire_item(cat(idx), SUBNET, metas);
|
||||
}
|
||||
|
||||
function expire_string_data(data: table[string, Type] of MetaDataTable, idx: any): interval
|
||||
|
@ -357,7 +357,7 @@ function Intel::seen(s: Seen)
|
|||
}
|
||||
else
|
||||
{
|
||||
event Intel::match_no_items(s);
|
||||
event Intel::match_remote(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -389,9 +389,11 @@ hook extend_match(info: Info, s: Seen, items: set[Item]) &priority=5
|
|||
}
|
||||
}
|
||||
|
||||
function insert(item: Item)
|
||||
# Function to insert metadata of an item. The function returns T
|
||||
# if the given indicator is new.
|
||||
function insert_meta_data(item: Item): bool
|
||||
{
|
||||
# Create and fill out the metadata item.
|
||||
# Prepare the metadata entry.
|
||||
local meta = item$meta;
|
||||
local meta_tbl: table [string] of MetaData;
|
||||
local is_new: bool = T;
|
||||
|
@ -399,63 +401,101 @@ function insert(item: Item)
|
|||
# All intelligence is case insensitive at the moment.
|
||||
local lower_indicator = to_lower(item$indicator);
|
||||
|
||||
if ( item$indicator_type == ADDR )
|
||||
switch ( item$indicator_type )
|
||||
{
|
||||
local host = to_addr(item$indicator);
|
||||
if ( have_full_data )
|
||||
{
|
||||
case ADDR:
|
||||
local host = to_addr(item$indicator);
|
||||
|
||||
if ( host !in data_store$host_data )
|
||||
data_store$host_data[host] = table();
|
||||
else
|
||||
{
|
||||
is_new = F;
|
||||
# Reset expiration timer.
|
||||
data_store$host_data[host] = data_store$host_data[host];
|
||||
}
|
||||
|
||||
meta_tbl = data_store$host_data[host];
|
||||
}
|
||||
break;
|
||||
case SUBNET:
|
||||
local net = to_subnet(item$indicator);
|
||||
|
||||
add min_data_store$host_data[host];
|
||||
}
|
||||
else if ( item$indicator_type == SUBNET )
|
||||
{
|
||||
local net = to_subnet(item$indicator);
|
||||
if ( have_full_data )
|
||||
{
|
||||
if ( !check_subnet(net, data_store$subnet_data) )
|
||||
data_store$subnet_data[net] = table();
|
||||
else
|
||||
{
|
||||
is_new = F;
|
||||
# Reset expiration timer.
|
||||
data_store$subnet_data[net] = data_store$subnet_data[net];
|
||||
}
|
||||
|
||||
meta_tbl = data_store$subnet_data[net];
|
||||
}
|
||||
|
||||
add min_data_store$subnet_data[net];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( have_full_data )
|
||||
{
|
||||
break;
|
||||
default:
|
||||
if ( [lower_indicator, item$indicator_type] !in data_store$string_data )
|
||||
data_store$string_data[lower_indicator, item$indicator_type] = table();
|
||||
else
|
||||
{
|
||||
is_new = F;
|
||||
# Reset expiration timer.
|
||||
data_store$string_data[lower_indicator, item$indicator_type] =
|
||||
data_store$string_data[lower_indicator, item$indicator_type];
|
||||
}
|
||||
|
||||
meta_tbl = data_store$string_data[lower_indicator, item$indicator_type];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
add min_data_store$string_data[lower_indicator, item$indicator_type];
|
||||
# Insert new metadata or update if already present.
|
||||
meta_tbl[meta$source] = meta;
|
||||
|
||||
return is_new;
|
||||
}
|
||||
|
||||
# Function to encapsulate insertion logic. The first_dispatch parameter
|
||||
# indicates whether the item might be new for other nodes.
|
||||
function _insert(item: Item, first_dispatch: bool &default = T)
|
||||
{
|
||||
# Assume that the item is new by default.
|
||||
local is_new: bool = T;
|
||||
|
||||
# All intelligence is case insensitive at the moment.
|
||||
local lower_indicator = to_lower(item$indicator);
|
||||
|
||||
# Insert indicator into MinDataStore (might exist already).
|
||||
switch ( item$indicator_type )
|
||||
{
|
||||
case ADDR:
|
||||
local host = to_addr(item$indicator);
|
||||
add min_data_store$host_data[host];
|
||||
break;
|
||||
case SUBNET:
|
||||
local net = to_subnet(item$indicator);
|
||||
add min_data_store$subnet_data[net];
|
||||
break;
|
||||
default:
|
||||
add min_data_store$string_data[lower_indicator, item$indicator_type];
|
||||
break;
|
||||
}
|
||||
|
||||
if ( have_full_data )
|
||||
{
|
||||
# Insert new metadata or update if already present
|
||||
meta_tbl[meta$source] = meta;
|
||||
# Insert new metadata or update if already present.
|
||||
is_new = insert_meta_data(item);
|
||||
}
|
||||
|
||||
if ( is_new )
|
||||
# Trigger insert for cluster in case the item is new
|
||||
# or insert was called on a worker
|
||||
if ( first_dispatch && is_new )
|
||||
# Announce a (possibly) new item if this is the first dispatch and
|
||||
# we know it is new or have to assume that on a worker.
|
||||
event Intel::new_item(item);
|
||||
}
|
||||
|
||||
function insert(item: Item)
|
||||
{
|
||||
# Insert possibly new item.
|
||||
_insert(item, T);
|
||||
}
|
||||
|
||||
# Function to check whether an item is present.
|
||||
function item_exists(item: Item): bool
|
||||
{
|
||||
|
@ -536,12 +576,12 @@ function remove(item: Item, purge_indicator: bool)
|
|||
break;
|
||||
}
|
||||
# Trigger deletion in minimal data stores
|
||||
event Intel::purge_item(item);
|
||||
event Intel::remove_indicator(item);
|
||||
}
|
||||
}
|
||||
|
||||
# Handling of indicator removal in minimal data stores.
|
||||
event purge_item(item: Item)
|
||||
event remove_indicator(item: Item)
|
||||
{
|
||||
switch ( item$indicator_type )
|
||||
{
|
||||
|
@ -558,4 +598,3 @@ event purge_item(item: Item)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -300,7 +300,7 @@ export {
|
|||
## the correct type.
|
||||
##
|
||||
## .. bro:see:: Log::remove_filter Log::add_default_filter
|
||||
## Log::remove_default_filter
|
||||
## Log::remove_default_filter Log::get_filter Log::get_filter_names
|
||||
global add_filter: function(id: ID, filter: Filter) : bool;
|
||||
|
||||
## Removes a filter from an existing logging stream.
|
||||
|
@ -315,9 +315,21 @@ export {
|
|||
## if no filter associated with *name* was found.
|
||||
##
|
||||
## .. bro:see:: Log::remove_filter Log::add_default_filter
|
||||
## Log::remove_default_filter
|
||||
## Log::remove_default_filter Log::get_filter Log::get_filter_names
|
||||
global remove_filter: function(id: ID, name: string) : bool;
|
||||
|
||||
## Gets the names of all filters associated with an existing
|
||||
## logging stream.
|
||||
##
|
||||
## id: The ID of a logging stream from which to obtain the list
|
||||
## of filter names.
|
||||
##
|
||||
## Returns: The set of filter names associated with the stream.
|
||||
##
|
||||
## ..bro:see:: Log::remove_filter Log::add_default_filter
|
||||
## Log::remove_default_filter Log::get_filter
|
||||
global get_filter_names: function(id: ID) : set[string];
|
||||
|
||||
## Gets a filter associated with an existing logging stream.
|
||||
##
|
||||
## id: The ID associated with a logging stream from which to
|
||||
|
@ -331,7 +343,7 @@ export {
|
|||
## :bro:id:`Log::no_filter` sentinel value.
|
||||
##
|
||||
## .. bro:see:: Log::add_filter Log::remove_filter Log::add_default_filter
|
||||
## Log::remove_default_filter
|
||||
## Log::remove_default_filter Log::get_filter_names
|
||||
global get_filter: function(id: ID, name: string) : Filter;
|
||||
|
||||
## Writes a new log line/entry to a logging stream.
|
||||
|
@ -432,6 +444,8 @@ export {
|
|||
|
||||
global all_streams: table[ID] of Stream = table();
|
||||
|
||||
global stream_filters: table[ID] of set[string] = table();
|
||||
|
||||
# We keep a script-level copy of all filters so that we can manipulate them.
|
||||
global filters: table[ID, string] of Filter;
|
||||
|
||||
|
@ -525,16 +539,41 @@ function remove_stream(id: ID) : bool
|
|||
{
|
||||
delete active_streams[id];
|
||||
delete all_streams[id];
|
||||
|
||||
if ( id in stream_filters )
|
||||
{
|
||||
for ( i in stream_filters[id] )
|
||||
delete filters[id, i];
|
||||
|
||||
delete stream_filters[id];
|
||||
}
|
||||
return __remove_stream(id);
|
||||
}
|
||||
|
||||
function disable_stream(id: ID) : bool
|
||||
{
|
||||
delete active_streams[id];
|
||||
|
||||
return __disable_stream(id);
|
||||
}
|
||||
|
||||
function enable_stream(id: ID) : bool
|
||||
{
|
||||
if ( ! __enable_stream(id) )
|
||||
return F;
|
||||
|
||||
if ( id in all_streams )
|
||||
active_streams[id] = all_streams[id];
|
||||
}
|
||||
|
||||
# convenience function to add a filter name to stream_filters
|
||||
function add_stream_filters(id: ID, name: string)
|
||||
{
|
||||
if ( id in stream_filters )
|
||||
add stream_filters[id][name];
|
||||
else
|
||||
stream_filters[id] = set(name);
|
||||
}
|
||||
|
||||
function add_filter(id: ID, filter: Filter) : bool
|
||||
{
|
||||
local stream = all_streams[id];
|
||||
|
@ -545,13 +584,22 @@ function add_filter(id: ID, filter: Filter) : bool
|
|||
if ( ! filter?$path && ! filter?$path_func )
|
||||
filter$path_func = default_path_func;
|
||||
|
||||
filters[id, filter$name] = filter;
|
||||
return __add_filter(id, filter);
|
||||
local res = __add_filter(id, filter);
|
||||
if ( res )
|
||||
{
|
||||
add_stream_filters(id, filter$name);
|
||||
filters[id, filter$name] = filter;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function remove_filter(id: ID, name: string) : bool
|
||||
{
|
||||
if ( id in stream_filters )
|
||||
delete stream_filters[id][name];
|
||||
|
||||
delete filters[id, name];
|
||||
|
||||
return __remove_filter(id, name);
|
||||
}
|
||||
|
||||
|
@ -563,6 +611,14 @@ function get_filter(id: ID, name: string) : Filter
|
|||
return no_filter;
|
||||
}
|
||||
|
||||
function get_filter_names(id: ID) : set[string]
|
||||
{
|
||||
if ( id in stream_filters )
|
||||
return stream_filters[id];
|
||||
else
|
||||
return set();
|
||||
}
|
||||
|
||||
function write(id: ID, columns: any) : bool
|
||||
{
|
||||
return __write(id, columns);
|
||||
|
|
|
@ -26,6 +26,13 @@ export {
|
|||
## This option is also available as a per-filter ``$config`` option.
|
||||
const use_json = F &redef;
|
||||
|
||||
## Define the gzip level to compress the logs. If 0, then no gzip
|
||||
## compression is performed. Enabling compression also changes
|
||||
## the log file name extension to include ".gz".
|
||||
##
|
||||
## This option is also available as a per-filter ``$config`` option.
|
||||
const gzip_level = 0 &redef;
|
||||
|
||||
## Format of timestamps when writing out JSON. By default, the JSON
|
||||
## formatter will use double values for timestamps which represent the
|
||||
## number of seconds from the UNIX epoch.
|
||||
|
@ -72,9 +79,12 @@ export {
|
|||
# runs the writer's default postprocessor command on it.
|
||||
function default_rotation_postprocessor_func(info: Log::RotationInfo) : bool
|
||||
{
|
||||
# If the filename has a ".gz" extension, then keep it.
|
||||
local gz = info$fname[-3:] == ".gz" ? ".gz" : "";
|
||||
|
||||
# Move file to name including both opening and closing time.
|
||||
local dst = fmt("%s.%s.log", info$path,
|
||||
strftime(Log::default_rotation_date_format, info$open));
|
||||
local dst = fmt("%s.%s.log%s", info$path,
|
||||
strftime(Log::default_rotation_date_format, info$open), gz);
|
||||
|
||||
system(fmt("/bin/mv %s %s", info$fname, dst));
|
||||
|
||||
|
|
|
@ -225,9 +225,22 @@ global blocks: table[addr] of BlockInfo = {}
|
|||
|
||||
|
||||
@if ( Cluster::is_enabled() )
|
||||
@load base/frameworks/cluster
|
||||
redef Cluster::manager2worker_events += /NetControl::catch_release_block_(new|delete)/;
|
||||
redef Cluster::worker2manager_events += /NetControl::catch_release_(add|delete|encountered)/;
|
||||
|
||||
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
event bro_init()
|
||||
{
|
||||
Broker::auto_publish(Cluster::worker_topic, NetControl::catch_release_block_new);
|
||||
Broker::auto_publish(Cluster::worker_topic, NetControl::catch_release_block_delete);
|
||||
}
|
||||
@else
|
||||
event bro_init()
|
||||
{
|
||||
Broker::auto_publish(Cluster::manager_topic, NetControl::catch_release_add);
|
||||
Broker::auto_publish(Cluster::manager_topic, NetControl::catch_release_delete);
|
||||
Broker::auto_publish(Cluster::manager_topic, NetControl::catch_release_encountered);
|
||||
}
|
||||
@endif
|
||||
|
||||
@endif
|
||||
|
||||
function cr_check_rule(r: Rule): bool
|
||||
|
|
|
@ -16,10 +16,25 @@ export {
|
|||
global cluster_netcontrol_delete_rule: event(id: string, reason: string);
|
||||
}
|
||||
|
||||
## Workers need ability to forward commands to manager.
|
||||
redef Cluster::worker2manager_events += /NetControl::cluster_netcontrol_(add|remove|delete)_rule/;
|
||||
## Workers need to see the result events from the manager.
|
||||
redef Cluster::manager2worker_events += /NetControl::rule_(added|removed|timeout|error|exists|new|destroyed)/;
|
||||
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
event bro_init()
|
||||
{
|
||||
Broker::auto_publish(Cluster::worker_topic, NetControl::rule_added);
|
||||
Broker::auto_publish(Cluster::worker_topic, NetControl::rule_removed);
|
||||
Broker::auto_publish(Cluster::worker_topic, NetControl::rule_timeout);
|
||||
Broker::auto_publish(Cluster::worker_topic, NetControl::rule_error);
|
||||
Broker::auto_publish(Cluster::worker_topic, NetControl::rule_exists);
|
||||
Broker::auto_publish(Cluster::worker_topic, NetControl::rule_new);
|
||||
Broker::auto_publish(Cluster::worker_topic, NetControl::rule_destroyed);
|
||||
}
|
||||
@else
|
||||
event bro_init()
|
||||
{
|
||||
Broker::auto_publish(Cluster::manager_topic, NetControl::cluster_netcontrol_add_rule);
|
||||
Broker::auto_publish(Cluster::manager_topic, NetControl::cluster_netcontrol_remove_rule);
|
||||
Broker::auto_publish(Cluster::manager_topic, NetControl::cluster_netcontrol_delete_rule);
|
||||
}
|
||||
@endif
|
||||
|
||||
function activate(p: PluginState, priority: int)
|
||||
{
|
||||
|
|
|
@ -555,19 +555,19 @@ function quarantine_host(infected: addr, dns: addr, quarantine: addr, t: interva
|
|||
local orules: vector of string = vector();
|
||||
local edrop: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(infected))];
|
||||
local rdrop: Rule = [$ty=DROP, $target=FORWARD, $entity=edrop, $expire=t, $location=location];
|
||||
orules[|orules|] = add_rule(rdrop);
|
||||
orules += add_rule(rdrop);
|
||||
|
||||
local todnse: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(infected), $dst_h=addr_to_subnet(dns), $dst_p=53/udp)];
|
||||
local todnsr = Rule($ty=MODIFY, $target=FORWARD, $entity=todnse, $expire=t, $location=location, $mod=FlowMod($dst_h=quarantine), $priority=+5);
|
||||
orules[|orules|] = add_rule(todnsr);
|
||||
orules += add_rule(todnsr);
|
||||
|
||||
local fromdnse: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(dns), $src_p=53/udp, $dst_h=addr_to_subnet(infected))];
|
||||
local fromdnsr = Rule($ty=MODIFY, $target=FORWARD, $entity=fromdnse, $expire=t, $location=location, $mod=FlowMod($src_h=dns), $priority=+5);
|
||||
orules[|orules|] = add_rule(fromdnsr);
|
||||
orules += add_rule(fromdnsr);
|
||||
|
||||
local wle: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(infected), $dst_h=addr_to_subnet(quarantine), $dst_p=80/tcp)];
|
||||
local wlr = Rule($ty=WHITELIST, $target=FORWARD, $entity=wle, $expire=t, $location=location, $priority=+5);
|
||||
orules[|orules|] = add_rule(wlr);
|
||||
orules += add_rule(wlr);
|
||||
|
||||
return orules;
|
||||
}
|
||||
|
@ -637,7 +637,7 @@ event NetControl::init() &priority=-20
|
|||
function activate_impl(p: PluginState, priority: int)
|
||||
{
|
||||
p$_priority = priority;
|
||||
plugins[|plugins|] = p;
|
||||
plugins += p;
|
||||
sort(plugins, function(p1: PluginState, p2: PluginState) : int { return p2$_priority - p1$_priority; });
|
||||
|
||||
plugin_ids[plugin_counter] = p;
|
||||
|
@ -734,7 +734,7 @@ function find_rules_subnet(sn: subnet) : vector of Rule
|
|||
for ( rule_id in rules_by_subnets[sn_entry] )
|
||||
{
|
||||
if ( rule_id in rules )
|
||||
ret[|ret|] = rules[rule_id];
|
||||
ret += rules[rule_id];
|
||||
else
|
||||
Reporter::error("find_rules_subnet - internal data structure error, missing rule");
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@ module NetControl;
|
|||
@load ../plugin
|
||||
@load base/frameworks/broker
|
||||
|
||||
@ifdef ( Broker::__enable )
|
||||
|
||||
export {
|
||||
type AclRule : record {
|
||||
command: string;
|
||||
|
@ -243,7 +241,7 @@ function acld_add_rule_fun(p: PluginState, r: Rule) : bool
|
|||
if ( ar$command == "" )
|
||||
return F;
|
||||
|
||||
Broker::send_event(p$acld_config$acld_topic, Broker::event_args(acld_add_rule, p$acld_id, r, ar));
|
||||
Broker::publish(p$acld_config$acld_topic, acld_add_rule, p$acld_id, r, ar);
|
||||
return T;
|
||||
}
|
||||
|
||||
|
@ -266,19 +264,20 @@ function acld_remove_rule_fun(p: PluginState, r: Rule, reason: string) : bool
|
|||
ar$comment = reason;
|
||||
}
|
||||
|
||||
Broker::send_event(p$acld_config$acld_topic, Broker::event_args(acld_remove_rule, p$acld_id, r, ar));
|
||||
Broker::publish(p$acld_config$acld_topic, acld_remove_rule, p$acld_id, r, ar);
|
||||
return T;
|
||||
}
|
||||
|
||||
function acld_init(p: PluginState)
|
||||
{
|
||||
Broker::enable();
|
||||
Broker::connect(cat(p$acld_config$acld_host), p$acld_config$acld_port, 1sec);
|
||||
Broker::subscribe_to_events(p$acld_config$acld_topic);
|
||||
Broker::subscribe(p$acld_config$acld_topic);
|
||||
Broker::peer(cat(p$acld_config$acld_host), p$acld_config$acld_port);
|
||||
}
|
||||
|
||||
event Broker::outgoing_connection_established(peer_address: string, peer_port: port, peer_name: string)
|
||||
event Broker::peer_added(endpoint: Broker::EndpointInfo, msg: string)
|
||||
{
|
||||
local peer_address = cat(endpoint$network$address);
|
||||
local peer_port = endpoint$network$bound_port;
|
||||
if ( [peer_port, peer_address] !in netcontrol_acld_peers )
|
||||
# ok, this one was none of ours...
|
||||
return;
|
||||
|
@ -315,5 +314,3 @@ function create_acld(config: AcldConfig) : PluginState
|
|||
|
||||
return p;
|
||||
}
|
||||
|
||||
@endif
|
||||
|
|
|
@ -8,8 +8,6 @@ module NetControl;
|
|||
@load ../plugin
|
||||
@load base/frameworks/broker
|
||||
|
||||
@ifdef ( Broker::__enable )
|
||||
|
||||
export {
|
||||
## This record specifies the configuration that is passed to :bro:see:`NetControl::create_broker`.
|
||||
type BrokerConfig: record {
|
||||
|
@ -151,7 +149,7 @@ function broker_add_rule_fun(p: PluginState, r: Rule) : bool
|
|||
if ( ! broker_check_rule(p, r) )
|
||||
return F;
|
||||
|
||||
Broker::send_event(p$broker_config$topic, Broker::event_args(broker_add_rule, p$broker_id, r));
|
||||
Broker::publish(p$broker_config$topic, Broker::make_event(broker_add_rule, p$broker_id, r));
|
||||
return T;
|
||||
}
|
||||
|
||||
|
@ -160,19 +158,20 @@ function broker_remove_rule_fun(p: PluginState, r: Rule, reason: string) : bool
|
|||
if ( ! broker_check_rule(p, r) )
|
||||
return F;
|
||||
|
||||
Broker::send_event(p$broker_config$topic, Broker::event_args(broker_remove_rule, p$broker_id, r, reason));
|
||||
Broker::publish(p$broker_config$topic, Broker::make_event(broker_remove_rule, p$broker_id, r, reason));
|
||||
return T;
|
||||
}
|
||||
|
||||
function broker_init(p: PluginState)
|
||||
{
|
||||
Broker::enable();
|
||||
Broker::connect(cat(p$broker_config$host), p$broker_config$bport, 1sec);
|
||||
Broker::subscribe_to_events(p$broker_config$topic);
|
||||
Broker::subscribe(p$broker_config$topic);
|
||||
Broker::peer(cat(p$broker_config$host), p$broker_config$bport);
|
||||
}
|
||||
|
||||
event Broker::outgoing_connection_established(peer_address: string, peer_port: port, peer_name: string)
|
||||
event Broker::peer_added(endpoint: Broker::EndpointInfo, msg: string)
|
||||
{
|
||||
local peer_address = cat(endpoint$network$address);
|
||||
local peer_port = endpoint$network$bound_port;
|
||||
if ( [peer_port, peer_address] !in netcontrol_broker_peers )
|
||||
return;
|
||||
|
||||
|
@ -219,5 +218,3 @@ function create_broker(config: BrokerConfig, can_expire: bool) : PluginState
|
|||
|
||||
return p;
|
||||
}
|
||||
|
||||
@endif
|
||||
|
|
|
@ -158,17 +158,17 @@ function entity_to_match(p: PluginState, e: Entity): vector of OpenFlow::ofp_mat
|
|||
|
||||
if ( e$ty == CONNECTION )
|
||||
{
|
||||
v[|v|] = OpenFlow::match_conn(e$conn); # forward and...
|
||||
v[|v|] = OpenFlow::match_conn(e$conn, T); # reverse
|
||||
v += OpenFlow::match_conn(e$conn); # forward and...
|
||||
v += OpenFlow::match_conn(e$conn, T); # reverse
|
||||
return openflow_match_pred(p, e, v);
|
||||
}
|
||||
|
||||
if ( e$ty == MAC )
|
||||
{
|
||||
v[|v|] = OpenFlow::ofp_match(
|
||||
v += OpenFlow::ofp_match(
|
||||
$dl_src=e$mac
|
||||
);
|
||||
v[|v|] = OpenFlow::ofp_match(
|
||||
v += OpenFlow::ofp_match(
|
||||
$dl_dst=e$mac
|
||||
);
|
||||
|
||||
|
@ -182,12 +182,12 @@ function entity_to_match(p: PluginState, e: Entity): vector of OpenFlow::ofp_mat
|
|||
if ( is_v6_subnet(e$ip) )
|
||||
dl_type = OpenFlow::ETH_IPv6;
|
||||
|
||||
v[|v|] = OpenFlow::ofp_match(
|
||||
v += OpenFlow::ofp_match(
|
||||
$dl_type=dl_type,
|
||||
$nw_src=e$ip
|
||||
);
|
||||
|
||||
v[|v|] = OpenFlow::ofp_match(
|
||||
v += OpenFlow::ofp_match(
|
||||
$dl_type=dl_type,
|
||||
$nw_dst=e$ip
|
||||
);
|
||||
|
@ -231,7 +231,7 @@ function entity_to_match(p: PluginState, e: Entity): vector of OpenFlow::ofp_mat
|
|||
m$tp_dst = port_to_count(f$dst_p);
|
||||
}
|
||||
|
||||
v[|v|] = m;
|
||||
v += m;
|
||||
|
||||
return openflow_match_pred(p, e, v);
|
||||
}
|
||||
|
@ -318,7 +318,7 @@ function openflow_add_rule(p: PluginState, r: Rule) : bool
|
|||
++flow_mod$cookie;
|
||||
}
|
||||
else
|
||||
event rule_error(r, p, "Error while executing OpenFlow::flow_mod");
|
||||
event NetControl::rule_error(r, p, "Error while executing OpenFlow::flow_mod");
|
||||
}
|
||||
|
||||
return T;
|
||||
|
@ -338,7 +338,7 @@ function openflow_remove_rule(p: PluginState, r: Rule, reason: string) : bool
|
|||
of_messages[r$cid, flow_mod$command] = OfTable($p=p, $r=r);
|
||||
else
|
||||
{
|
||||
event rule_error(r, p, "Error while executing OpenFlow::flow_mod");
|
||||
event NetControl::rule_error(r, p, "Error while executing OpenFlow::flow_mod");
|
||||
return F;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,18 +8,5 @@
|
|||
@load ./actions/page
|
||||
@load ./actions/add-geodata
|
||||
|
||||
# There shouldn't be any default overhead from loading these since they
|
||||
# *should* only do anything when notices have the ACTION_EMAIL action applied.
|
||||
@load ./extend-email/hostnames
|
||||
|
||||
# The cluster framework must be loaded first.
|
||||
@load base/frameworks/cluster
|
||||
|
||||
@if ( Cluster::is_enabled() )
|
||||
@load ./cluster
|
||||
@else
|
||||
@load ./non-cluster
|
||||
@endif
|
||||
|
||||
# Load here so that it can check whether clustering is enabled.
|
||||
@load ./actions/pp-alarms
|
||||
@load ./actions/pp-alarms
|
||||
|
|
|
@ -155,9 +155,11 @@ function pretty_print_alarm(out: file, n: Info)
|
|||
|
||||
@if ( Cluster::is_enabled() )
|
||||
pdescr = "local";
|
||||
|
||||
if ( n?$src_peer )
|
||||
pdescr = n$src_peer?$descr ? n$src_peer$descr : fmt("%s", n$src_peer$host);
|
||||
|
||||
if ( n?$peer_descr )
|
||||
pdescr = n$peer_descr;
|
||||
else if ( n?$peer_name )
|
||||
pdescr = n$peer_name;
|
||||
|
||||
pdescr = fmt("<%s> ", pdescr);
|
||||
@endif
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
##! Implements notice functionality across clusters. Worker nodes
|
||||
##! will disable notice/alarm logging streams and forward notice
|
||||
##! events to the manager node for logging/processing.
|
||||
|
||||
@load ./main
|
||||
@load base/frameworks/cluster
|
||||
|
||||
module Notice;
|
||||
|
||||
export {
|
||||
## This is the event used to transport notices on the cluster.
|
||||
##
|
||||
## n: The notice information to be sent to the cluster manager for
|
||||
## further processing.
|
||||
global cluster_notice: event(n: Notice::Info);
|
||||
}
|
||||
|
||||
## Manager can communicate notice suppression to workers.
|
||||
redef Cluster::manager2worker_events += /Notice::begin_suppression/;
|
||||
## Workers need ability to forward notices to manager.
|
||||
redef Cluster::worker2manager_events += /Notice::cluster_notice/;
|
||||
|
||||
@if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||
event Notice::begin_suppression(n: Notice::Info)
|
||||
{
|
||||
local suppress_until = n$ts + n$suppress_for;
|
||||
suppressing[n$note, n$identifier] = suppress_until;
|
||||
}
|
||||
@endif
|
||||
|
||||
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
event Notice::cluster_notice(n: Notice::Info)
|
||||
{
|
||||
# Raise remotely received notices on the manager
|
||||
NOTICE(n);
|
||||
}
|
||||
@endif
|
||||
|
||||
module GLOBAL;
|
||||
|
||||
## This is the entry point in the global namespace for the notice framework.
|
||||
function NOTICE(n: Notice::Info)
|
||||
{
|
||||
# Suppress this notice if necessary.
|
||||
if ( Notice::is_being_suppressed(n) )
|
||||
return;
|
||||
|
||||
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
Notice::internal_NOTICE(n);
|
||||
else
|
||||
# For non-managers, send the notice on to the manager.
|
||||
event Notice::cluster_notice(n);
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
##! Loading this script extends the :bro:enum:`Notice::ACTION_EMAIL` action
|
||||
##! by appending to the email the hostnames associated with
|
||||
##! :bro:type:`Notice::Info`'s *src* and *dst* fields as determined by a
|
||||
##! DNS lookup.
|
||||
|
||||
@load ../main
|
||||
|
||||
module Notice;
|
||||
|
||||
# We have to store references to the notices here because the when statement
|
||||
# clones the frame which doesn't give us access to modify values outside
|
||||
# of it's execution scope. (we get a clone of the notice instead of a
|
||||
# reference to the original notice)
|
||||
global tmp_notice_storage: table[string] of Notice::Info &create_expire=max_email_delay+10secs;
|
||||
|
||||
hook notice(n: Notice::Info) &priority=10
|
||||
{
|
||||
if ( ! n?$src && ! n?$dst )
|
||||
return;
|
||||
|
||||
# This should only be done for notices that are being sent to email.
|
||||
if ( ACTION_EMAIL !in n$actions )
|
||||
return;
|
||||
|
||||
# I'm not recovering gracefully from the when statements because I want
|
||||
# the notice framework to detect that something has exceeded the maximum
|
||||
# allowed email delay and tell the user.
|
||||
local uid = unique_id("");
|
||||
tmp_notice_storage[uid] = n;
|
||||
|
||||
local output = "";
|
||||
if ( n?$src )
|
||||
{
|
||||
add n$email_delay_tokens["hostnames-src"];
|
||||
when ( local src_name = lookup_addr(n$src) )
|
||||
{
|
||||
output = string_cat("orig/src hostname: ", src_name, "\n");
|
||||
tmp_notice_storage[uid]$email_body_sections[|tmp_notice_storage[uid]$email_body_sections|] = output;
|
||||
delete tmp_notice_storage[uid]$email_delay_tokens["hostnames-src"];
|
||||
}
|
||||
}
|
||||
if ( n?$dst )
|
||||
{
|
||||
add n$email_delay_tokens["hostnames-dst"];
|
||||
when ( local dst_name = lookup_addr(n$dst) )
|
||||
{
|
||||
output = string_cat("resp/dst hostname: ", dst_name, "\n");
|
||||
tmp_notice_storage[uid]$email_body_sections[|tmp_notice_storage[uid]$email_body_sections|] = output;
|
||||
delete tmp_notice_storage[uid]$email_delay_tokens["hostnames-dst"];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
##! what is bad activity for sites. More extensive documentation about using
|
||||
##! the notice framework can be found in :doc:`/frameworks/notice`.
|
||||
|
||||
@load base/frameworks/cluster
|
||||
|
||||
module Notice;
|
||||
|
||||
export {
|
||||
|
@ -117,9 +119,10 @@ export {
|
|||
## Associated count, or perhaps a status code.
|
||||
n: count &log &optional;
|
||||
|
||||
## Peer that raised this notice.
|
||||
src_peer: event_peer &optional;
|
||||
## Textual description for the peer that raised this notice.
|
||||
## Name of remote peer that raised this notice.
|
||||
peer_name: string &optional;
|
||||
## Textual description for the peer that raised this notice,
|
||||
## including name, host address and port.
|
||||
peer_descr: string &log &optional;
|
||||
|
||||
## The actions which have been applied to this notice.
|
||||
|
@ -261,9 +264,14 @@ export {
|
|||
|
||||
## This event is generated when a notice begins to be suppressed.
|
||||
##
|
||||
## n: The record containing notice data regarding the notice type
|
||||
## about to be suppressed.
|
||||
global begin_suppression: event(n: Notice::Info);
|
||||
## ts: time indicating then when the notice to be suppressed occured.
|
||||
##
|
||||
## suppress_for: length of time that this notice should be suppressed.
|
||||
##
|
||||
## note: The :bro:type:`Notice::Type` of the notice.
|
||||
##
|
||||
## identifier: The identifier string of the notice that should be suppressed.
|
||||
global begin_suppression: event(ts: time, suppress_for: interval, note: Type, identifier: string);
|
||||
|
||||
## A function to determine if an event is supposed to be suppressed.
|
||||
##
|
||||
|
@ -311,8 +319,36 @@ export {
|
|||
##
|
||||
## n: The record of notice data.
|
||||
global internal_NOTICE: function(n: Notice::Info);
|
||||
|
||||
## This is the event used to transport notices on the cluster.
|
||||
##
|
||||
## n: The notice information to be sent to the cluster manager for
|
||||
## further processing.
|
||||
global cluster_notice: event(n: Notice::Info);
|
||||
}
|
||||
|
||||
module GLOBAL;
|
||||
|
||||
function NOTICE(n: Notice::Info)
|
||||
{
|
||||
if ( Notice::is_being_suppressed(n) )
|
||||
return;
|
||||
|
||||
@if ( Cluster::is_enabled() )
|
||||
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
Notice::internal_NOTICE(n);
|
||||
else
|
||||
{
|
||||
n$peer_name = n$peer_descr = Cluster::node;
|
||||
Broker::publish(Cluster::manager_topic, Notice::cluster_notice, n);
|
||||
}
|
||||
@else
|
||||
Notice::internal_NOTICE(n);
|
||||
@endif
|
||||
}
|
||||
|
||||
module Notice;
|
||||
|
||||
# This is used as a hack to implement per-item expiration intervals.
|
||||
function per_notice_suppression_interval(t: table[Notice::Type, string] of time, idx: any): interval
|
||||
{
|
||||
|
@ -363,24 +399,6 @@ event bro_init() &priority=5
|
|||
$interv=24hrs, $postprocessor=log_mailing_postprocessor]);
|
||||
}
|
||||
|
||||
# TODO: fix this.
|
||||
#function notice_tags(n: Notice::Info) : table[string] of string
|
||||
# {
|
||||
# local tgs: table[string] of string = table();
|
||||
# if ( is_remote_event() )
|
||||
# {
|
||||
# if ( n$src_peer$descr != "" )
|
||||
# tgs["es"] = n$src_peer$descr;
|
||||
# else
|
||||
# tgs["es"] = fmt("%s/%s", n$src_peer$host, n$src_peer$p);
|
||||
# }
|
||||
# else
|
||||
# {
|
||||
# tgs["es"] = peer_description;
|
||||
# }
|
||||
# return tgs;
|
||||
# }
|
||||
|
||||
function email_headers(subject_desc: string, dest: string): string
|
||||
{
|
||||
local header_text = string_cat(
|
||||
|
@ -501,11 +519,25 @@ hook Notice::notice(n: Notice::Info) &priority=-5
|
|||
if ( n?$identifier &&
|
||||
[n$note, n$identifier] !in suppressing &&
|
||||
n$suppress_for != 0secs )
|
||||
{
|
||||
local suppress_until = n$ts + n$suppress_for;
|
||||
suppressing[n$note, n$identifier] = suppress_until;
|
||||
event Notice::begin_suppression(n);
|
||||
}
|
||||
{
|
||||
event Notice::begin_suppression(n$ts, n$suppress_for, n$note, n$identifier);
|
||||
}
|
||||
}
|
||||
|
||||
event Notice::begin_suppression(ts: time, suppress_for: interval, note: Type,
|
||||
identifier: string)
|
||||
{
|
||||
local suppress_until = ts + suppress_for;
|
||||
suppressing[note, identifier] = suppress_until;
|
||||
}
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
if ( ! Cluster::is_enabled() )
|
||||
return;
|
||||
|
||||
Broker::auto_publish(Cluster::worker_topic, Notice::begin_suppression);
|
||||
Broker::auto_publish(Cluster::proxy_topic, Notice::begin_suppression);
|
||||
}
|
||||
|
||||
function is_being_suppressed(n: Notice::Info): bool
|
||||
|
@ -607,12 +639,6 @@ function apply_policy(n: Notice::Info)
|
|||
n$dst = n$iconn$resp_h;
|
||||
}
|
||||
|
||||
if ( ! n?$src_peer )
|
||||
n$src_peer = get_event_peer();
|
||||
if ( ! n?$peer_descr )
|
||||
n$peer_descr = n$src_peer?$descr ?
|
||||
n$src_peer$descr : fmt("%s", n$src_peer$host);
|
||||
|
||||
if ( ! n?$email_body_sections )
|
||||
n$email_body_sections = vector();
|
||||
if ( ! n?$email_delay_tokens )
|
||||
|
@ -647,6 +673,7 @@ function internal_NOTICE(n: Notice::Info)
|
|||
hook Notice::notice(n);
|
||||
}
|
||||
|
||||
module GLOBAL;
|
||||
|
||||
global NOTICE: function(n: Notice::Info);
|
||||
event Notice::cluster_notice(n: Notice::Info)
|
||||
{
|
||||
NOTICE(n);
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
@load ./main
|
||||
|
||||
module GLOBAL;
|
||||
|
||||
## This is the entry point in the global namespace for the notice framework.
|
||||
function NOTICE(n: Notice::Info)
|
||||
{
|
||||
# Suppress this notice if necessary.
|
||||
if ( Notice::is_being_suppressed(n) )
|
||||
return;
|
||||
|
||||
Notice::internal_NOTICE(n);
|
||||
}
|
|
@ -106,6 +106,7 @@ export {
|
|||
["baroque_SYN"] = ACTION_LOG,
|
||||
["base64_illegal_encoding"] = ACTION_LOG,
|
||||
["connection_originator_SYN_ack"] = ACTION_LOG_PER_ORIG,
|
||||
["contentline_size_exceeded"] = ACTION_LOG,
|
||||
["corrupt_tcp_options"] = ACTION_LOG_PER_ORIG,
|
||||
["crud_trailing_HTTP_request"] = ACTION_LOG,
|
||||
["data_after_reset"] = ACTION_LOG,
|
||||
|
|
|
@ -13,8 +13,14 @@ export {
|
|||
global cluster_flow_clear: event(name: string);
|
||||
}
|
||||
|
||||
## Workers need ability to forward commands to manager.
|
||||
redef Cluster::worker2manager_events += /OpenFlow::cluster_flow_(mod|clear)/;
|
||||
@if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||
# Workers need ability to forward commands to manager.
|
||||
event bro_init()
|
||||
{
|
||||
Broker::auto_publish(Cluster::manager_topic, OpenFlow::cluster_flow_mod);
|
||||
Broker::auto_publish(Cluster::manager_topic, OpenFlow::cluster_flow_clear);
|
||||
}
|
||||
@endif
|
||||
|
||||
# the flow_mod function wrapper
|
||||
function flow_mod(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
|
||||
module OpenFlow;
|
||||
|
||||
@ifdef ( Broker::__enable )
|
||||
|
||||
export {
|
||||
redef enum Plugin += {
|
||||
BROKER,
|
||||
|
@ -49,27 +47,28 @@ function broker_describe(state: ControllerState): string
|
|||
|
||||
function broker_flow_mod_fun(state: ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool
|
||||
{
|
||||
Broker::send_event(state$broker_topic, Broker::event_args(broker_flow_mod, state$_name, state$broker_dpid, match, flow_mod));
|
||||
Broker::publish(state$broker_topic, Broker::make_event(broker_flow_mod, state$_name, state$broker_dpid, match, flow_mod));
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
function broker_flow_clear_fun(state: OpenFlow::ControllerState): bool
|
||||
{
|
||||
Broker::send_event(state$broker_topic, Broker::event_args(broker_flow_clear, state$_name, state$broker_dpid));
|
||||
Broker::publish(state$broker_topic, Broker::make_event(broker_flow_clear, state$_name, state$broker_dpid));
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
function broker_init(state: OpenFlow::ControllerState)
|
||||
{
|
||||
Broker::enable();
|
||||
Broker::connect(cat(state$broker_host), state$broker_port, 1sec);
|
||||
Broker::subscribe_to_events(state$broker_topic); # openflow success and failure events are directly sent back via the other plugin via broker.
|
||||
Broker::peer(cat(state$broker_host), state$broker_port);
|
||||
Broker::subscribe(state$broker_topic); # openflow success and failure events are directly sent back via the other plugin via broker.
|
||||
}
|
||||
|
||||
event Broker::outgoing_connection_established(peer_address: string, peer_port: port, peer_name: string)
|
||||
event Broker::peer_added(endpoint: Broker::EndpointInfo, msg: string)
|
||||
{
|
||||
local peer_address = cat(endpoint$network$address);
|
||||
local peer_port = endpoint$network$bound_port;
|
||||
if ( [peer_port, peer_address] !in broker_peers )
|
||||
# ok, this one was none of ours...
|
||||
return;
|
||||
|
@ -94,5 +93,3 @@ function broker_new(name: string, host: addr, host_port: port, topic: string, dp
|
|||
|
||||
return c;
|
||||
}
|
||||
|
||||
@endif
|
||||
|
|
|
@ -88,7 +88,7 @@ function ryu_flow_mod(state: OpenFlow::ControllerState, match: ofp_match, flow_m
|
|||
local flow_actions: vector of ryu_flow_action = vector();
|
||||
|
||||
for ( i in flow_mod$actions$out_ports )
|
||||
flow_actions[|flow_actions|] = ryu_flow_action($_type="OUTPUT", $_port=flow_mod$actions$out_ports[i]);
|
||||
flow_actions += ryu_flow_action($_type="OUTPUT", $_port=flow_mod$actions$out_ports[i]);
|
||||
|
||||
# Generate our ryu_flow_mod record for the ReST API call.
|
||||
local mod: ryu_ofp_flow_mod = ryu_ofp_flow_mod(
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
@load base/utils/directions-and-hosts
|
||||
@load base/utils/numbers
|
||||
@load base/frameworks/cluster
|
||||
|
||||
module Software;
|
||||
|
||||
|
@ -68,8 +69,9 @@ export {
|
|||
## Hosts whose software should be detected and tracked.
|
||||
## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS.
|
||||
const asset_tracking = LOCAL_HOSTS &redef;
|
||||
|
||||
|
||||
## Other scripts should call this function when they detect software.
|
||||
##
|
||||
## id: The connection id where the software was discovered.
|
||||
##
|
||||
## info: A record representing the software discovered.
|
||||
|
@ -84,6 +86,16 @@ export {
|
|||
## is compared lexicographically.
|
||||
global cmp_versions: function(v1: Version, v2: Version): int;
|
||||
|
||||
## Sometimes software will expose itself on the network with
|
||||
## slight naming variations. This table provides a mechanism
|
||||
## for a piece of software to be renamed to a single name
|
||||
## even if it exposes itself with an alternate name. The
|
||||
## yielded string is the name that will be logged and generally
|
||||
## used for everything.
|
||||
global alternate_names: table[string] of string {
|
||||
["Flash Player"] = "Flash",
|
||||
} &default=function(a: string): string { return a; };
|
||||
|
||||
## Type to represent a collection of :bro:type:`Software::Info` records.
|
||||
## It's indexed with the name of a piece of software such as "Firefox"
|
||||
## and it yields a :bro:type:`Software::Info` record with more
|
||||
|
@ -92,15 +104,21 @@ export {
|
|||
|
||||
## The set of software associated with an address. Data expires from
|
||||
## this table after one day by default so that a detected piece of
|
||||
## software will be logged once each day.
|
||||
global tracked: table[addr] of SoftwareSet
|
||||
&create_expire=1day
|
||||
&synchronized
|
||||
&redef;
|
||||
## software will be logged once each day. In a cluster, this table is
|
||||
## uniformly distributed among proxy nodes.
|
||||
global tracked: table[addr] of SoftwareSet &create_expire=1day;
|
||||
|
||||
## This event can be handled to access the :bro:type:`Software::Info`
|
||||
## record as it is sent on to the logging framework.
|
||||
global log_software: event(rec: Info);
|
||||
|
||||
## This event can be handled to access software information whenever it's
|
||||
## version is found to have changed.
|
||||
global version_change: event(old: Info, new: Info);
|
||||
|
||||
## This event is raised when software is about to be registered for
|
||||
## tracking in :bro:see:`Software::tracked`.
|
||||
global register: event(info: Info);
|
||||
}
|
||||
|
||||
event bro_init() &priority=5
|
||||
|
@ -125,7 +143,7 @@ function parse(unparsed_version: string): Description
|
|||
local v: Version;
|
||||
|
||||
# Parse browser-alike versions separately
|
||||
if ( /^(Mozilla|Opera)\/[0-9]\./ in unparsed_version )
|
||||
if ( /^(Mozilla|Opera)\/[0-9]+\./ in unparsed_version )
|
||||
{
|
||||
return parse_mozilla(unparsed_version);
|
||||
}
|
||||
|
@ -133,11 +151,17 @@ function parse(unparsed_version: string): Description
|
|||
{
|
||||
# The regular expression should match the complete version number
|
||||
# and software name.
|
||||
local version_parts = split_string_n(unparsed_version, /\/?( [\(])?v?[0-9\-\._, ]{2,}/, T, 1);
|
||||
local clean_unparsed_version = gsub(unparsed_version, /\\x/, "%");
|
||||
clean_unparsed_version = unescape_URI(clean_unparsed_version);
|
||||
local version_parts = split_string_n(clean_unparsed_version, /([\/\-_]|( [\(v]+))?[0-9\-\._, ]{2,}/, T, 1);
|
||||
if ( 0 in version_parts )
|
||||
{
|
||||
# Remove any bits of junk at end of first part.
|
||||
if ( /([\/\-_]|( [\(v]+))$/ in version_parts[0] )
|
||||
version_parts[0] = strip(sub(version_parts[0], /([\/\-_]|( [\(v]+))/, ""));
|
||||
|
||||
if ( /^\(/ in version_parts[0] )
|
||||
software_name = strip(sub(version_parts[0], /[\(]/, ""));
|
||||
software_name = strip(sub(version_parts[0], /\(/, ""));
|
||||
else
|
||||
software_name = strip(version_parts[0]);
|
||||
}
|
||||
|
@ -192,7 +216,7 @@ function parse(unparsed_version: string): Description
|
|||
}
|
||||
}
|
||||
|
||||
return [$version=v, $unparsed_version=unparsed_version, $name=software_name];
|
||||
return [$version=v, $unparsed_version=unparsed_version, $name=alternate_names[software_name]];
|
||||
}
|
||||
|
||||
|
||||
|
@ -227,6 +251,13 @@ function parse_mozilla(unparsed_version: string): Description
|
|||
v = parse(parts[1])$version;
|
||||
}
|
||||
}
|
||||
else if ( /Edge\// in unparsed_version )
|
||||
{
|
||||
software_name="Edge";
|
||||
parts = split_string_all(unparsed_version, /Edge\/[0-9\.]*/);
|
||||
if ( 1 in parts )
|
||||
v = parse(parts[1])$version;
|
||||
}
|
||||
else if ( /Version\/.*Safari\// in unparsed_version )
|
||||
{
|
||||
software_name = "Safari";
|
||||
|
@ -280,6 +311,14 @@ function parse_mozilla(unparsed_version: string): Description
|
|||
v = parse(parts[1])$version;
|
||||
}
|
||||
}
|
||||
else if ( /Flash%20Player/ in unparsed_version )
|
||||
{
|
||||
software_name = "Flash";
|
||||
parts = split_string_all(unparsed_version, /[\/ ]/);
|
||||
if ( 2 in parts )
|
||||
v = parse(parts[2])$version;
|
||||
}
|
||||
|
||||
else if ( /AdobeAIR\/[0-9\.]*/ in unparsed_version )
|
||||
{
|
||||
software_name = "AdobeAIR";
|
||||
|
@ -406,63 +445,70 @@ function software_fmt(i: Info): string
|
|||
return fmt("%s %s", i$name, software_fmt_version(i$version));
|
||||
}
|
||||
|
||||
# Insert a mapping into the table
|
||||
# Overides old entries for the same software and generates events if needed.
|
||||
event register(id: conn_id, info: Info)
|
||||
event Software::register(info: Info)
|
||||
{
|
||||
# Host already known?
|
||||
if ( info$host !in tracked )
|
||||
tracked[info$host] = table();
|
||||
local ts: SoftwareSet;
|
||||
|
||||
if ( info$host in tracked )
|
||||
ts = tracked[info$host];
|
||||
else
|
||||
ts = tracked[info$host] = SoftwareSet();
|
||||
|
||||
local ts = tracked[info$host];
|
||||
# Software already registered for this host? We don't want to endlessly
|
||||
# log the same thing.
|
||||
if ( info$name in ts )
|
||||
{
|
||||
local old = ts[info$name];
|
||||
|
||||
# If the version hasn't changed, then we're just redetecting the
|
||||
# same thing, then we don't care. This results in no extra logging.
|
||||
# But if the $force_log value is set then we'll continue.
|
||||
if ( ! info$force_log && cmp_versions(old$version, info$version) == 0 )
|
||||
local changed = cmp_versions(old$version, info$version) != 0;
|
||||
|
||||
if ( changed )
|
||||
event Software::version_change(old, info);
|
||||
else if ( ! info$force_log )
|
||||
# If the version hasn't changed, then we're just redetecting the
|
||||
# same thing, then we don't care.
|
||||
return;
|
||||
}
|
||||
|
||||
ts[info$name] = info;
|
||||
|
||||
Log::write(Software::LOG, info);
|
||||
}
|
||||
|
||||
function found(id: conn_id, info: Info): bool
|
||||
{
|
||||
if ( info$force_log || addr_matches_host(info$host, asset_tracking) )
|
||||
{
|
||||
if ( !info?$ts )
|
||||
info$ts=network_time();
|
||||
|
||||
if ( info?$version ) # we have a version number and don't have to parse. check if the name is also set...
|
||||
{
|
||||
if ( ! info?$name )
|
||||
{
|
||||
Reporter::error("Required field name not present in Software::found");
|
||||
return F;
|
||||
}
|
||||
}
|
||||
else # no version present, we have to parse...
|
||||
{
|
||||
if ( !info?$unparsed_version )
|
||||
{
|
||||
Reporter::error("No unparsed version string present in Info record with version in Software::found");
|
||||
return F;
|
||||
}
|
||||
local sw = parse(info$unparsed_version);
|
||||
info$unparsed_version = sw$unparsed_version;
|
||||
info$name = sw$name;
|
||||
info$version = sw$version;
|
||||
}
|
||||
|
||||
event register(id, info);
|
||||
return T;
|
||||
}
|
||||
else
|
||||
if ( ! info$force_log && ! addr_matches_host(info$host, asset_tracking) )
|
||||
return F;
|
||||
|
||||
if ( ! info?$ts )
|
||||
info$ts = network_time();
|
||||
|
||||
if ( info?$version )
|
||||
{
|
||||
if ( ! info?$name )
|
||||
{
|
||||
Reporter::error("Required field name not present in Software::found");
|
||||
return F;
|
||||
}
|
||||
}
|
||||
else if ( ! info?$unparsed_version )
|
||||
{
|
||||
Reporter::error("No unparsed version string present in Info record with version in Software::found");
|
||||
return F;
|
||||
}
|
||||
|
||||
if ( ! info?$version )
|
||||
{
|
||||
local sw = parse(info$unparsed_version);
|
||||
info$unparsed_version = sw$unparsed_version;
|
||||
info$name = sw$name;
|
||||
info$version = sw$version;
|
||||
}
|
||||
|
||||
@if ( Cluster::is_enabled() )
|
||||
Cluster::publish_hrw(Cluster::proxy_pool, info$host, Software::register,
|
||||
info);
|
||||
@else
|
||||
event Software::register(info);
|
||||
@endif
|
||||
|
||||
return T;
|
||||
}
|
||||
|
|
|
@ -55,18 +55,20 @@ export {
|
|||
global cluster_threshold_crossed: event(ss_name: string, key: SumStats::Key, thold_index: count);
|
||||
}
|
||||
|
||||
# Add events to the cluster framework to make this work.
|
||||
redef Cluster::manager2worker_events += /SumStats::cluster_(ss_request|get_result|threshold_crossed)/;
|
||||
redef Cluster::manager2worker_events += /SumStats::(get_a_key)/;
|
||||
redef Cluster::worker2manager_events += /SumStats::cluster_(send_result|key_intermediate_response)/;
|
||||
redef Cluster::worker2manager_events += /SumStats::(send_a_key|send_no_key)/;
|
||||
|
||||
# This variable is maintained to know what keys have recently sent or received
|
||||
# intermediate updates so they don't overwhelm the manager.
|
||||
global recent_global_view_keys: set[string, Key] &create_expire=1min;
|
||||
|
||||
@if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||
|
||||
event bro_init() &priority=100
|
||||
{
|
||||
Broker::auto_publish(Cluster::manager_topic, SumStats::cluster_send_result);
|
||||
Broker::auto_publish(Cluster::manager_topic, SumStats::cluster_key_intermediate_response);
|
||||
Broker::auto_publish(Cluster::manager_topic, SumStats::send_a_key);
|
||||
Broker::auto_publish(Cluster::manager_topic, SumStats::send_no_key);
|
||||
}
|
||||
|
||||
# Result tables indexed on a uid that are currently being sent to the
|
||||
# manager.
|
||||
global sending_results: table[string] of ResultTable = table() &read_expire=1min;
|
||||
|
@ -207,6 +209,14 @@ function request_key(ss_name: string, key: Key): Result
|
|||
|
||||
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
|
||||
event bro_init() &priority=100
|
||||
{
|
||||
Broker::auto_publish(Cluster::worker_topic, SumStats::cluster_ss_request);
|
||||
Broker::auto_publish(Cluster::worker_topic, SumStats::cluster_get_result);
|
||||
Broker::auto_publish(Cluster::worker_topic, SumStats::cluster_threshold_crossed);
|
||||
Broker::auto_publish(Cluster::worker_topic, SumStats::get_a_key);
|
||||
}
|
||||
|
||||
# This variable is maintained by manager nodes as they collect and aggregate
|
||||
# results.
|
||||
# Index on a uid.
|
||||
|
|
|
@ -267,7 +267,7 @@ function add_observe_plugin_dependency(calc: Calculation, depends_on: Calculatio
|
|||
{
|
||||
if ( calc !in calc_deps )
|
||||
calc_deps[calc] = vector();
|
||||
calc_deps[calc][|calc_deps[calc]|] = depends_on;
|
||||
calc_deps[calc] += depends_on;
|
||||
}
|
||||
|
||||
event bro_init() &priority=100000
|
||||
|
@ -348,7 +348,7 @@ function add_calc_deps(calcs: vector of Calculation, c: Calculation)
|
|||
{
|
||||
if ( calc_deps[c][i] in calc_deps )
|
||||
add_calc_deps(calcs, calc_deps[c][i]);
|
||||
calcs[|c|] = calc_deps[c][i];
|
||||
calcs += calc_deps[c][i];
|
||||
#print fmt("add dep for %s [%s] ", c, calc_deps[c][i]);
|
||||
}
|
||||
}
|
||||
|
@ -387,7 +387,7 @@ function create(ss: SumStat)
|
|||
skip_calc=T;
|
||||
}
|
||||
if ( ! skip_calc )
|
||||
reducer$calc_funcs[|reducer$calc_funcs|] = calc;
|
||||
reducer$calc_funcs += calc;
|
||||
}
|
||||
|
||||
if ( reducer$stream !in reducer_store )
|
||||
|
@ -399,7 +399,7 @@ function create(ss: SumStat)
|
|||
schedule ss$epoch { SumStats::finish_epoch(ss) };
|
||||
}
|
||||
|
||||
function observe(id: string, key: Key, obs: Observation)
|
||||
function observe(id: string, orig_key: Key, obs: Observation)
|
||||
{
|
||||
if ( id !in reducer_store )
|
||||
return;
|
||||
|
@ -407,8 +407,7 @@ function observe(id: string, key: Key, obs: Observation)
|
|||
# Try to add the data to all of the defined reducers.
|
||||
for ( r in reducer_store[id] )
|
||||
{
|
||||
if ( r?$normalize_key )
|
||||
key = r$normalize_key(copy(key));
|
||||
local key = r?$normalize_key ? r$normalize_key(copy(orig_key)) : orig_key;
|
||||
|
||||
# If this reducer has a predicate, run the predicate
|
||||
# and skip this key if the predicate return false.
|
||||
|
|
|
@ -6,28 +6,25 @@ event SumStats::process_epoch_result(ss: SumStat, now: time, data: ResultTable)
|
|||
{
|
||||
# TODO: is this the right processing group size?
|
||||
local i = 50;
|
||||
local keys_to_delete: vector of SumStats::Key = vector();
|
||||
|
||||
for ( key in data )
|
||||
{
|
||||
ss$epoch_result(now, key, data[key]);
|
||||
delete data[key];
|
||||
keys_to_delete += key;
|
||||
|
||||
if ( |data| == 0 )
|
||||
{
|
||||
if ( ss?$epoch_finished )
|
||||
ss$epoch_finished(now);
|
||||
|
||||
# Now that no data is left we can finish.
|
||||
return;
|
||||
}
|
||||
|
||||
i = i-1;
|
||||
if ( i == 0 )
|
||||
{
|
||||
# TODO: is this the right interval?
|
||||
schedule 0.01 secs { process_epoch_result(ss, now, data) };
|
||||
if ( --i == 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for ( idx in keys_to_delete )
|
||||
delete data[keys_to_delete[idx]];
|
||||
|
||||
if ( |data| > 0 )
|
||||
# TODO: is this the right interval?
|
||||
schedule 0.01 secs { SumStats::process_epoch_result(ss, now, data) };
|
||||
else if ( ss?$epoch_finished )
|
||||
ss$epoch_finished(now);
|
||||
}
|
||||
|
||||
event SumStats::finish_epoch(ss: SumStat)
|
||||
|
@ -46,9 +43,9 @@ event SumStats::finish_epoch(ss: SumStat)
|
|||
if ( ss?$epoch_finished )
|
||||
ss$epoch_finished(now);
|
||||
}
|
||||
else
|
||||
else if ( |data| > 0 )
|
||||
{
|
||||
event SumStats::process_epoch_result(ss, now, data);
|
||||
event SumStats::process_epoch_result(ss, now, copy(data));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,4 +86,4 @@ function request_key(ss_name: string, key: Key): Result
|
|||
else
|
||||
return table();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ function sample_add_sample(obs:Observation, rv: ResultVal)
|
|||
++rv$sample_elements;
|
||||
|
||||
if ( |rv$samples| < rv$num_samples )
|
||||
rv$samples[|rv$samples|] = obs;
|
||||
rv$samples += obs;
|
||||
else
|
||||
{
|
||||
local ra = rand(rv$sample_elements);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@load base/bif/const.bif.bro
|
||||
@load base/bif/const.bif
|
||||
@load base/bif/types.bif
|
||||
|
||||
# Type declarations
|
||||
|
@ -442,10 +442,13 @@ type fa_file: record {
|
|||
|
||||
## Metadata that's been inferred about a particular file.
|
||||
type fa_metadata: record {
|
||||
## The strongest matching mime type if one was discovered.
|
||||
## The strongest matching MIME type if one was discovered.
|
||||
mime_type: string &optional;
|
||||
## All matching mime types if any were discovered.
|
||||
## All matching MIME types if any were discovered.
|
||||
mime_types: mime_matches &optional;
|
||||
## Specifies whether the MIME type was inferred using signatures,
|
||||
## or provided directly by the protocol the file appeared in.
|
||||
inferred: bool &default=T;
|
||||
};
|
||||
|
||||
## Fields of a SYN packet.
|
||||
|
@ -528,7 +531,7 @@ type EventStats: record {
|
|||
dispatched: count; ##< Total number of events dispatched so far.
|
||||
};
|
||||
|
||||
## Summary statistics of all regular expression matchers.
|
||||
## Holds statistics for all types of reassembly.
|
||||
##
|
||||
## .. bro:see:: get_reassembler_stats
|
||||
type ReassemblerStats: record {
|
||||
|
@ -600,6 +603,29 @@ type ThreadStats: record {
|
|||
num_threads: count;
|
||||
};
|
||||
|
||||
## Statistics about Broker communication.
|
||||
##
|
||||
## .. bro:see:: get_broker_stats
|
||||
type BrokerStats: record {
|
||||
num_peers: count;
|
||||
## Number of active data stores.
|
||||
num_stores: count;
|
||||
## Number of pending data store queries.
|
||||
num_pending_queries: count;
|
||||
## Number of total log messages received.
|
||||
num_events_incoming: count;
|
||||
## Number of total log messages sent.
|
||||
num_events_outgoing: count;
|
||||
## Number of total log records received.
|
||||
num_logs_incoming: count;
|
||||
## Number of total log records sent.
|
||||
num_logs_outgoing: count;
|
||||
## Number of total identifiers received.
|
||||
num_ids_incoming: count;
|
||||
## Number of total identifiers sent.
|
||||
num_ids_outgoing: count;
|
||||
};
|
||||
|
||||
## Deprecated.
|
||||
##
|
||||
## .. todo:: Remove. It's still declared internally but doesn't seem used anywhere
|
||||
|
@ -628,6 +654,7 @@ type script_id: record {
|
|||
exported: bool; ##< True if the identifier is exported.
|
||||
constant: bool; ##< True if the identifier is a constant.
|
||||
enum_constant: bool; ##< True if the identifier is an enum value.
|
||||
option_value: bool; ##< True if the identifier is an option.
|
||||
redefinable: bool; ##< True if the identifier is declared with the :bro:attr:`&redef` attribute.
|
||||
value: any &optional; ##< The current value of the identifier.
|
||||
};
|
||||
|
@ -733,7 +760,7 @@ type IPAddrAnonymizationClass: enum {
|
|||
## A locally unique ID identifying a communication peer. The ID is returned by
|
||||
## :bro:id:`connect`.
|
||||
##
|
||||
## .. bro:see:: connect Communication
|
||||
## .. bro:see:: connect
|
||||
type peer_id: count;
|
||||
|
||||
## A communication peer.
|
||||
|
@ -756,7 +783,7 @@ type event_peer: record {
|
|||
p: port;
|
||||
is_local: bool; ##< True if this record describes the local process.
|
||||
descr: string; ##< The peer's :bro:see:`peer_description`.
|
||||
class: string &optional; ##< The self-assigned *class* of the peer. See :bro:see:`Communication::Node`.
|
||||
class: string &optional; ##< The self-assigned *class* of the peer.
|
||||
};
|
||||
|
||||
## Deprecated.
|
||||
|
@ -845,6 +872,9 @@ type geo_location: record {
|
|||
longitude: double &optional; ##< Longitude.
|
||||
} &log;
|
||||
|
||||
## The directory containing MaxMind DB (.mmdb) files to use for GeoIP support.
|
||||
const mmdb_dir: string = "" &redef;
|
||||
|
||||
## Computed entropy values. The record captures a number of measures that are
|
||||
## computed in parallel. See `A Pseudorandom Number Sequence Test Program
|
||||
## <http://www.fourmilab.ch/random>`_ for more information, Bro uses the same
|
||||
|
@ -1770,9 +1800,11 @@ type gtp_delete_pdp_ctx_response_elements: record {
|
|||
};
|
||||
|
||||
# Prototypes of Bro built-in functions.
|
||||
@load base/bif/strings.bif
|
||||
@load base/bif/bro.bif
|
||||
@load base/bif/stats.bif
|
||||
@load base/bif/reporter.bif
|
||||
@load base/bif/strings.bif
|
||||
@load base/bif/option.bif
|
||||
|
||||
## Deprecated. This is superseded by the new logging framework.
|
||||
global log_file_name: function(tag: string): string &redef;
|
||||
|
@ -2142,6 +2174,28 @@ export {
|
|||
rep_dur: interval;
|
||||
## The length in bytes of the reply.
|
||||
rep_len: count;
|
||||
## The user id of the reply.
|
||||
rpc_uid: count;
|
||||
## The group id of the reply.
|
||||
rpc_gid: count;
|
||||
## The stamp of the reply.
|
||||
rpc_stamp: count;
|
||||
## The machine name of the reply.
|
||||
rpc_machine_name: string;
|
||||
## The auxiliary ids of the reply.
|
||||
rpc_auxgids: index_vec;
|
||||
};
|
||||
|
||||
## NFS file attributes. Field names are based on RFC 1813.
|
||||
##
|
||||
## .. bro:see:: nfs_proc_sattr
|
||||
type sattr_t: record {
|
||||
mode: count &optional; ##< Mode
|
||||
uid: count &optional; ##< User ID.
|
||||
gid: count &optional; ##< Group ID.
|
||||
size: count &optional; ##< Size.
|
||||
atime: time_how_t &optional; ##< Time of last access.
|
||||
mtime: time_how_t &optional; ##< Time of last modification.
|
||||
};
|
||||
|
||||
## NFS file attributes. Field names are based on RFC 1813.
|
||||
|
@ -2164,6 +2218,14 @@ export {
|
|||
ctime: time; ##< Time of creation.
|
||||
};
|
||||
|
||||
## NFS symlinkdata attributes. Field names are based on RFC 1813
|
||||
##
|
||||
## .. bro:see:: nfs_proc_symlink
|
||||
type symlinkdata_t: record {
|
||||
symlink_attributes: sattr_t; ##< The initial attributes for the symbolic link
|
||||
nfspath: string &optional; ##< The string containing the symbolic link data.
|
||||
};
|
||||
|
||||
## NFS *readdir* arguments.
|
||||
##
|
||||
## .. bro:see:: nfs_proc_readdir
|
||||
|
@ -2172,6 +2234,40 @@ export {
|
|||
fname: string; ##< The name of the file we are interested in.
|
||||
};
|
||||
|
||||
## NFS *rename* arguments.
|
||||
##
|
||||
## .. bro:see:: nfs_proc_rename
|
||||
type renameopargs_t : record {
|
||||
src_dirfh : string;
|
||||
src_fname : string;
|
||||
dst_dirfh : string;
|
||||
dst_fname : string;
|
||||
};
|
||||
|
||||
## NFS *symlink* arguments.
|
||||
##
|
||||
## .. bro:see:: nfs_proc_symlink
|
||||
type symlinkargs_t: record {
|
||||
link : diropargs_t; ##< The location of the link to be created.
|
||||
symlinkdata: symlinkdata_t; ##< The symbolic link to be created.
|
||||
};
|
||||
|
||||
## NFS *link* arguments.
|
||||
##
|
||||
## .. bro:see:: nfs_proc_link
|
||||
type linkargs_t: record {
|
||||
fh : string; ##< The file handle for the existing file system object.
|
||||
link : diropargs_t; ##< The location of the link to be created.
|
||||
};
|
||||
|
||||
## NFS *sattr* arguments.
|
||||
##
|
||||
## .. bro:see:: nfs_proc_sattr
|
||||
type sattrargs_t: record {
|
||||
fh : string; ##< The file handle for the existing file system object.
|
||||
new_attributes: sattr_t; ##< The new attributes for the file.
|
||||
};
|
||||
|
||||
## NFS lookup reply. If the lookup failed, *dir_attr* may be set. If the
|
||||
## lookup succeeded, *fh* is always set and *obj_attr* and *dir_attr*
|
||||
## may be set.
|
||||
|
@ -2230,6 +2326,23 @@ export {
|
|||
mtime: time; ##< Modification time.
|
||||
};
|
||||
|
||||
## NFS *link* reply.
|
||||
##
|
||||
## .. bro:see:: nfs_proc_link
|
||||
type link_reply_t: record {
|
||||
post_attr: fattr_t &optional; ##< Optional post-operation attributes of the file system object identified by file
|
||||
preattr: wcc_attr_t &optional; ##< Optional attributes associated w/ file.
|
||||
postattr: fattr_t &optional; ##< Optional attributes associated w/ file.
|
||||
};
|
||||
|
||||
## NFS *sattr* reply. If the request fails, *pre|post* attr may be set.
|
||||
## If the request succeeds, *pre|post* attr are set.
|
||||
##
|
||||
type sattr_reply_t: record {
|
||||
dir_pre_attr: wcc_attr_t &optional; ##< Optional attributes associated w/ dir.
|
||||
dir_post_attr: fattr_t &optional; ##< Optional attributes associated w/ dir.
|
||||
};
|
||||
|
||||
## NFS *write* reply. If the request fails, *pre|post* attr may be set.
|
||||
## If the request succeeds, *pre|post* attr may be set and all other
|
||||
## fields are set.
|
||||
|
@ -2264,6 +2377,16 @@ export {
|
|||
dir_post_attr: fattr_t &optional; ##< Optional attributes associated w/ dir.
|
||||
};
|
||||
|
||||
## NFS reply for *rename*. Corresponds to *wcc_data* in the spec.
|
||||
##
|
||||
## .. bro:see:: nfs_proc_rename
|
||||
type renameobj_reply_t: record {
|
||||
src_dir_pre_attr: wcc_attr_t;
|
||||
src_dir_post_attr: fattr_t;
|
||||
dst_dir_pre_attr: wcc_attr_t;
|
||||
dst_dir_post_attr: fattr_t;
|
||||
};
|
||||
|
||||
## NFS *readdir* arguments. Used for both *readdir* and *readdirplus*.
|
||||
##
|
||||
## .. bro:see:: nfs_proc_readdir
|
||||
|
@ -2317,6 +2440,71 @@ export {
|
|||
};
|
||||
} # end export
|
||||
|
||||
|
||||
module MOUNT3;
|
||||
export {
|
||||
|
||||
## Record summarizing the general results and status of MOUNT3
|
||||
## request/reply pairs.
|
||||
##
|
||||
## Note that when *rpc_stat* or *mount_stat* indicates not successful,
|
||||
## the reply record passed to the corresponding event will be empty and
|
||||
## contain uninitialized fields, so don't use it. Also note that time
|
||||
# and duration values might not be fully accurate. For TCP, we record
|
||||
# times when the corresponding chunk of data is delivered to the
|
||||
# analyzer. Depending on the reassembler, this might be well after the
|
||||
# first packet of the request was received.
|
||||
#
|
||||
# .. bro:see:: mount_proc_mnt mount_proc_dump mount_proc_umnt
|
||||
# mount_proc_umntall mount_proc_export mount_proc_not_implemented
|
||||
type info_t: record {
|
||||
## The RPC status.
|
||||
rpc_stat: rpc_status;
|
||||
## The MOUNT status.
|
||||
mnt_stat: status_t;
|
||||
## The start time of the request.
|
||||
req_start: time;
|
||||
## The duration of the request.
|
||||
req_dur: interval;
|
||||
## The length in bytes of the request.
|
||||
req_len: count;
|
||||
## The start time of the reply.
|
||||
rep_start: time;
|
||||
## The duration of the reply.
|
||||
rep_dur: interval;
|
||||
## The length in bytes of the reply.
|
||||
rep_len: count;
|
||||
## The user id of the reply.
|
||||
rpc_uid: count;
|
||||
## The group id of the reply.
|
||||
rpc_gid: count;
|
||||
## The stamp of the reply.
|
||||
rpc_stamp: count;
|
||||
## The machine name of the reply.
|
||||
rpc_machine_name: string;
|
||||
## The auxiliary ids of the reply.
|
||||
rpc_auxgids: index_vec;
|
||||
};
|
||||
|
||||
## MOUNT *mnt* arguments.
|
||||
##
|
||||
## .. bro:see:: mount_proc_mnt
|
||||
type dirmntargs_t : record {
|
||||
dirname: string; ##< Name of directory to mount
|
||||
};
|
||||
|
||||
## MOUNT lookup reply. If the mount failed, *dir_attr* may be set. If the
|
||||
## mount succeeded, *fh* is always set.
|
||||
##
|
||||
## .. bro:see:: mount_proc_mnt
|
||||
type mnt_reply_t: record {
|
||||
dirfh: string &optional; ##< Dir handle
|
||||
auth_flavors: vector of auth_flavor_t &optional; ##< Returned authentication flavors
|
||||
};
|
||||
|
||||
} # end export
|
||||
|
||||
|
||||
module Threading;
|
||||
|
||||
export {
|
||||
|
@ -2801,6 +2989,73 @@ export {
|
|||
security_blob : string &optional;
|
||||
};
|
||||
|
||||
type SMB1::Trans2_Args: record {
|
||||
## Total parameter count
|
||||
total_param_count: count;
|
||||
## Total data count
|
||||
total_data_count: count;
|
||||
## Max parameter count
|
||||
max_param_count: count;
|
||||
## Max data count
|
||||
max_data_count: count;
|
||||
## Max setup count
|
||||
max_setup_count: count;
|
||||
## Flags
|
||||
flags: count;
|
||||
## Timeout
|
||||
trans_timeout: count;
|
||||
## Parameter count
|
||||
param_count: count;
|
||||
## Parameter offset
|
||||
param_offset: count;
|
||||
## Data count
|
||||
data_count: count;
|
||||
## Data offset
|
||||
data_offset: count;
|
||||
## Setup count
|
||||
setup_count: count;
|
||||
};
|
||||
|
||||
type SMB1::Trans_Sec_Args: record {
|
||||
## Total parameter count
|
||||
total_param_count: count;
|
||||
## Total data count
|
||||
total_data_count: count;
|
||||
## Parameter count
|
||||
param_count: count;
|
||||
## Parameter offset
|
||||
param_offset: count;
|
||||
## Parameter displacement
|
||||
param_displacement: count;
|
||||
## Data count
|
||||
data_count: count;
|
||||
## Data offset
|
||||
data_offset: count;
|
||||
## Data displacement
|
||||
data_displacement: count;
|
||||
};
|
||||
|
||||
type SMB1::Trans2_Sec_Args: record {
|
||||
## Total parameter count
|
||||
total_param_count: count;
|
||||
## Total data count
|
||||
total_data_count: count;
|
||||
## Parameter count
|
||||
param_count: count;
|
||||
## Parameter offset
|
||||
param_offset: count;
|
||||
## Parameter displacement
|
||||
param_displacement: count;
|
||||
## Data count
|
||||
data_count: count;
|
||||
## Data offset
|
||||
data_offset: count;
|
||||
## Data displacement
|
||||
data_displacement: count;
|
||||
## File ID
|
||||
FID: count;
|
||||
};
|
||||
|
||||
type SMB1::Find_First2_Request_Args: record {
|
||||
## File attributes to apply as a constraint to the search
|
||||
search_attrs : count;
|
||||
|
@ -3021,28 +3276,188 @@ export {
|
|||
## The type of share being accessed. Physical disk, named pipe, or printer.
|
||||
share_type: count;
|
||||
};
|
||||
|
||||
## The request sent by the client to request either creation of or access to a file.
|
||||
##
|
||||
## For more information, see MS-SMB2:2.2.13
|
||||
##
|
||||
## .. bro:see:: smb2_create_request
|
||||
type SMB2::CreateRequest: record {
|
||||
## Name of the file
|
||||
filename : string;
|
||||
## Defines the action the server MUST take if the file that is specified already exists.
|
||||
disposition : count;
|
||||
## Specifies the options to be applied when creating or opening the file.
|
||||
create_options : count;
|
||||
};
|
||||
|
||||
## The response to an SMB2 *create_request* request, which is sent by the client to request
|
||||
## either creation of or access to a file.
|
||||
##
|
||||
## For more information, see MS-SMB2:2.2.14
|
||||
##
|
||||
## .. bro:see:: smb2_create_response
|
||||
type SMB2::CreateResponse: record {
|
||||
## The SMB2 GUID for the file.
|
||||
file_id : SMB2::GUID;
|
||||
## Size of the file.
|
||||
size : count;
|
||||
## Timestamps associated with the file in question.
|
||||
times : SMB::MACTimes;
|
||||
## File attributes.
|
||||
attrs : SMB2::FileAttrs;
|
||||
## The action taken in establishing the open.
|
||||
create_action : count;
|
||||
};
|
||||
}
|
||||
|
||||
module GLOBAL;
|
||||
|
||||
## A list of router addresses offered by a DHCP server.
|
||||
##
|
||||
## .. bro:see:: dhcp_ack dhcp_offer
|
||||
type dhcp_router_list: table[count] of addr;
|
||||
module DHCP;
|
||||
|
||||
## A DHCP message.
|
||||
##
|
||||
## .. bro:see:: dhcp_ack dhcp_decline dhcp_discover dhcp_inform dhcp_nak
|
||||
## dhcp_offer dhcp_release dhcp_request
|
||||
type dhcp_msg: record {
|
||||
op: count; ##< Message OP code. 1 = BOOTREQUEST, 2 = BOOTREPLY
|
||||
m_type: count; ##< The type of DHCP message.
|
||||
xid: count; ##< Transaction ID of a DHCP session.
|
||||
h_addr: string; ##< Hardware address of the client.
|
||||
ciaddr: addr; ##< Original IP address of the client.
|
||||
yiaddr: addr; ##< IP address assigned to the client.
|
||||
};
|
||||
export {
|
||||
## A list of addresses offered by a DHCP server. Could be routers,
|
||||
## DNS servers, or other.
|
||||
##
|
||||
## .. bro:see:: dhcp_message
|
||||
type DHCP::Addrs: vector of addr;
|
||||
|
||||
## A DHCP message.
|
||||
## .. bro:see:: dhcp_message
|
||||
type DHCP::Msg: record {
|
||||
op: count; ##< Message OP code. 1 = BOOTREQUEST, 2 = BOOTREPLY
|
||||
m_type: count; ##< The type of DHCP message.
|
||||
xid: count; ##< Transaction ID of a DHCP session.
|
||||
## Number of seconds since client began address acquisition
|
||||
## or renewal process
|
||||
secs: interval;
|
||||
flags: count;
|
||||
ciaddr: addr; ##< Original IP address of the client.
|
||||
yiaddr: addr; ##< IP address assigned to the client.
|
||||
siaddr: addr; ##< IP address of the server.
|
||||
giaddr: addr; ##< IP address of the relaying gateway.
|
||||
chaddr: string; ##< Client hardware address.
|
||||
sname: string &default=""; ##< Server host name.
|
||||
file_n: string &default=""; ##< Boot file name.
|
||||
};
|
||||
|
||||
## DHCP Client Identifier (Option 61)
|
||||
## .. bro:see:: dhcp_message
|
||||
type DHCP::ClientID: record {
|
||||
hwtype: count;
|
||||
hwaddr: string;
|
||||
};
|
||||
|
||||
## DHCP Client FQDN Option information (Option 81)
|
||||
type DHCP::ClientFQDN: record {
|
||||
## An unparsed bitfield of flags (refer to RFC 4702).
|
||||
flags: count;
|
||||
## This field is deprecated in the standard.
|
||||
rcode1: count;
|
||||
## This field is deprecated in the standard.
|
||||
rcode2: count;
|
||||
## The Domain Name part of the option carries all or part of the FQDN
|
||||
## of a DHCP client.
|
||||
domain_name: string;
|
||||
};
|
||||
|
||||
## DHCP Relay Agent Information Option (Option 82)
|
||||
## .. bro:see:: dhcp_message
|
||||
type DHCP::SubOpt: record {
|
||||
code: count;
|
||||
value: string;
|
||||
};
|
||||
|
||||
type DHCP::SubOpts: vector of DHCP::SubOpt;
|
||||
|
||||
type DHCP::Options: record {
|
||||
## The ordered list of all DHCP option numbers.
|
||||
options: index_vec &optional;
|
||||
|
||||
## Subnet Mask Value (option 1)
|
||||
subnet_mask: addr &optional;
|
||||
|
||||
## Router addresses (option 3)
|
||||
routers: DHCP::Addrs &optional;
|
||||
|
||||
## DNS Server addresses (option 6)
|
||||
dns_servers: DHCP::Addrs &optional;
|
||||
|
||||
## The Hostname of the client (option 12)
|
||||
host_name: string &optional;
|
||||
|
||||
## The DNS domain name of the client (option 15)
|
||||
domain_name: string &optional;
|
||||
|
||||
## Enable/Disable IP Forwarding (option 19)
|
||||
forwarding: bool &optional;
|
||||
|
||||
## Broadcast Address (option 28)
|
||||
broadcast: addr &optional;
|
||||
|
||||
## Vendor specific data. This can frequently
|
||||
## be unparsed binary data. (option 43)
|
||||
vendor: string &optional;
|
||||
|
||||
## NETBIOS name server list (option 44)
|
||||
nbns: DHCP::Addrs &optional;
|
||||
|
||||
## Address requested by the client (option 50)
|
||||
addr_request: addr &optional;
|
||||
|
||||
## Lease time offered by the server. (option 51)
|
||||
lease: interval &optional;
|
||||
|
||||
## Server address to allow clients to distinguish
|
||||
## between lease offers. (option 54)
|
||||
serv_addr: addr &optional;
|
||||
|
||||
## DHCP Parameter Request list (option 55)
|
||||
param_list: index_vec &optional;
|
||||
|
||||
## Textual error message (option 56)
|
||||
message: string &optional;
|
||||
|
||||
## Maximum Message Size (option 57)
|
||||
max_msg_size: count &optional;
|
||||
|
||||
## This option specifies the time interval from address
|
||||
## assignment until the client transitions to the
|
||||
## RENEWING state. (option 58)
|
||||
renewal_time: interval &optional;
|
||||
|
||||
## This option specifies the time interval from address
|
||||
## assignment until the client transitions to the
|
||||
## REBINDING state. (option 59)
|
||||
rebinding_time: interval &optional;
|
||||
|
||||
## This option is used by DHCP clients to optionally
|
||||
## identify the vendor type and configuration of a DHCP
|
||||
## client. (option 60)
|
||||
vendor_class: string &optional;
|
||||
|
||||
## DHCP Client Identifier (Option 61)
|
||||
client_id: DHCP::ClientID &optional;
|
||||
|
||||
## User Class opaque value (Option 77)
|
||||
user_class: string &optional;
|
||||
|
||||
## DHCP Client FQDN (Option 81)
|
||||
client_fqdn: DHCP::ClientFQDN &optional;
|
||||
|
||||
## DHCP Relay Agent Information Option (Option 82)
|
||||
sub_opt: DHCP::SubOpts &optional;
|
||||
|
||||
## Auto Config option to let host know if it's allowed to
|
||||
## auto assign an IP address. (Option 116)
|
||||
auto_config: bool &optional;
|
||||
|
||||
## URL to find a proxy.pac for auto proxy config (Option 252)
|
||||
auto_proxy_config: string &optional;
|
||||
};
|
||||
}
|
||||
|
||||
module GLOBAL;
|
||||
## A DNS message.
|
||||
##
|
||||
## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
|
||||
|
@ -3835,6 +4250,8 @@ export {
|
|||
|
||||
module KRB;
|
||||
export {
|
||||
## Kerberos keytab file name. Used to decrypt tickets encountered on the wire.
|
||||
const keytab = "" &redef;
|
||||
## KDC Options. See :rfc:`4120`
|
||||
type KRB::KDC_Options: record {
|
||||
## The ticket to be issued should have its forwardable flag set.
|
||||
|
@ -3955,6 +4372,10 @@ export {
|
|||
service_name : string;
|
||||
## Cipher the ticket was encrypted with
|
||||
cipher : count;
|
||||
## Cipher text of the ticket
|
||||
ciphertext : string &optional;
|
||||
## Authentication info
|
||||
authenticationinfo: string &optional;
|
||||
};
|
||||
|
||||
type KRB::Ticket_Vector: vector of KRB::Ticket;
|
||||
|
@ -4394,6 +4815,17 @@ export {
|
|||
const max_frag_data = 30000 &redef;
|
||||
}
|
||||
|
||||
module NCP;
|
||||
export {
|
||||
## The maximum number of bytes to allocate when parsing NCP frames.
|
||||
const max_frame_size = 65536 &redef;
|
||||
}
|
||||
|
||||
module Cluster;
|
||||
export {
|
||||
type Cluster::Pool: record {};
|
||||
}
|
||||
|
||||
module GLOBAL;
|
||||
|
||||
## Seed for hashes computed internally for probabilistic data structures. Using
|
||||
|
@ -4406,16 +4838,9 @@ const global_hash_seed: string = "" &redef;
|
|||
## The maximum is currently 128 bits.
|
||||
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
|
||||
@load base/frameworks/files
|
||||
|
||||
@load base/bif
|
||||
|
||||
# Load BiFs defined by plugins.
|
||||
@load base/bif/plugins
|
||||
|
||||
## Whether usage of the old communication system is considered an error or
|
||||
## not. The default Bro configuration no longer works with the non-Broker
|
||||
## communication system unless you have manually taken action to initialize
|
||||
## and set up the old comm. system. Deprecation warnings are still emitted
|
||||
## when setting this flag, but they will not result in a fatal error.
|
||||
const old_comm_usage_is_ok: bool = F &redef;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
@load base/utils/exec
|
||||
@load base/utils/files
|
||||
@load base/utils/geoip-distance
|
||||
@load base/utils/hash_hrw
|
||||
@load base/utils/numbers
|
||||
@load base/utils/paths
|
||||
@load base/utils/patterns
|
||||
|
@ -32,10 +33,10 @@
|
|||
@load base/frameworks/signatures
|
||||
@load base/frameworks/packet-filter
|
||||
@load base/frameworks/software
|
||||
@load base/frameworks/communication
|
||||
@load base/frameworks/control
|
||||
@load base/frameworks/cluster
|
||||
@load base/frameworks/intel
|
||||
@load base/frameworks/config
|
||||
@load base/frameworks/reporter
|
||||
@load base/frameworks/sumstats
|
||||
@load base/frameworks/tunnels
|
||||
|
@ -61,8 +62,6 @@
|
|||
@load base/protocols/rfb
|
||||
@load base/protocols/sip
|
||||
@load base/protocols/snmp
|
||||
# This DOES NOT enable the SMB analyzer. It's just some base support
|
||||
# for other protocols.
|
||||
@load base/protocols/smb
|
||||
@load base/protocols/smtp
|
||||
@load base/protocols/socks
|
||||
|
|
15
scripts/base/init-frameworks-and-bifs.bro
Normal file
15
scripts/base/init-frameworks-and-bifs.bro
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Load these frameworks here because they use fairly deep integration with
|
||||
# BiFs and script-land defined types. They are also more likely to
|
||||
# make use of calling BIFs for variable initializations, and that
|
||||
# can't be done until init-bare.bro has been loaded completely (hence
|
||||
# the separate file).
|
||||
@load base/frameworks/logging
|
||||
@load base/frameworks/broker
|
||||
@load base/frameworks/input
|
||||
@load base/frameworks/analyzer
|
||||
@load base/frameworks/files
|
||||
|
||||
@load base/bif
|
||||
|
||||
# Load BiFs defined by plugins.
|
||||
@load base/bif/plugins
|
|
@ -86,5 +86,5 @@ export {
|
|||
|
||||
function at_least(version_string: string): bool
|
||||
{
|
||||
return Version::parse(version_string)$version_number >= Version::number;
|
||||
return Version::number >= Version::parse(version_string)$version_number;
|
||||
}
|
||||
|
|
|
@ -86,8 +86,9 @@ export {
|
|||
## d packet with payload ("data")
|
||||
## f packet with FIN bit set
|
||||
## r packet with RST bit set
|
||||
## c packet with a bad checksum
|
||||
## c packet with a bad checksum (applies to UDP too)
|
||||
## t packet with retransmitted payload
|
||||
## w packet with a zero window advertisement
|
||||
## i inconsistent packet (e.g. FIN+RST bits set)
|
||||
## q multi-flag packet (SYN+FIN or SYN+RST bits set)
|
||||
## ^ connection direction was flipped by Bro's heuristic
|
||||
|
@ -95,9 +96,15 @@ export {
|
|||
##
|
||||
## If the event comes from the originator, the letter is in
|
||||
## upper-case; if it comes from the responder, it's in
|
||||
## lower-case. Multiple packets of the same type will only be
|
||||
## noted once (e.g. we only record one "d" in each direction,
|
||||
## regardless of how many data packets were seen.)
|
||||
## lower-case. The 'a', 'd', 'i' and 'q' flags are
|
||||
## recorded a maximum of one time in either direction regardless
|
||||
## of how many are actually seen. 'f', 'h', 'r' and
|
||||
## 's' can be recorded multiple times for either direction
|
||||
## if the associated sequence number differs from the
|
||||
## last-seen packet of the same flag type.
|
||||
## 'c', 't' and 'w' are recorded in a logarithmic fashion:
|
||||
## the second instance represents that the event was seen
|
||||
## (at least) 10 times; the third instance, 100 times; etc.
|
||||
history: string &log &optional;
|
||||
## Number of packets that the originator sent.
|
||||
## Only set if :bro:id:`use_conn_size_analyzer` = T.
|
||||
|
@ -116,7 +123,7 @@ export {
|
|||
## If this connection was over a tunnel, indicate the
|
||||
## *uid* values for any encapsulating parent connections
|
||||
## used over the lifetime of this inner connection.
|
||||
tunnel_parents: set[string] &log;
|
||||
tunnel_parents: set[string] &log &optional;
|
||||
};
|
||||
|
||||
## Event that can be handled to access the :bro:type:`Conn::Info`
|
||||
|
@ -207,7 +214,11 @@ function set_conn(c: connection, eoc: bool)
|
|||
c$conn$uid=c$uid;
|
||||
c$conn$id=c$id;
|
||||
if ( c?$tunnel && |c$tunnel| > 0 )
|
||||
{
|
||||
if ( ! c$conn?$tunnel_parents )
|
||||
c$conn$tunnel_parents = set();
|
||||
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 )
|
||||
{
|
||||
|
@ -253,7 +264,11 @@ event tunnel_changed(c: connection, e: EncapsulatingConnVector) &priority=5
|
|||
{
|
||||
set_conn(c, F);
|
||||
if ( |e| > 0 )
|
||||
{
|
||||
if ( ! c$conn?$tunnel_parents )
|
||||
c$conn$tunnel_parents = set();
|
||||
add c$conn$tunnel_parents[e[|e|-1]$uid];
|
||||
}
|
||||
c$tunnel = e;
|
||||
}
|
||||
|
||||
|
|
|
@ -149,10 +149,6 @@ export {
|
|||
# IActivation
|
||||
["4d9f4ab8-7d1c-11cf-861e-0020af6e7c57",0] = "RemoteActivation",
|
||||
|
||||
# IRemoteSCMActivator
|
||||
["000001a0-0000-0000-c000-000000000046",3] = "RemoteGetClassObject",
|
||||
["000001a0-0000-0000-c000-000000000046",4] = "RemoteCreateInstance",
|
||||
|
||||
# nspi
|
||||
["f5cc5a18-4264-101a-8c59-08002b2f8426",0] = "NspiBind",
|
||||
["f5cc5a18-4264-101a-8c59-08002b2f8426",1] = "NspiUnbind",
|
||||
|
@ -222,8 +218,8 @@ export {
|
|||
["000001a0-0000-0000-c000-000000000046",0] = "QueryInterfaceIRemoteSCMActivator",
|
||||
["000001a0-0000-0000-c000-000000000046",1] = "AddRefIRemoteISCMActivator",
|
||||
["000001a0-0000-0000-c000-000000000046",2] = "ReleaseIRemoteISCMActivator",
|
||||
#["000001a0-0000-0000-c000-000000000046",3] = "RemoteGetClassObject",
|
||||
#["000001a0-0000-0000-c000-000000000046",4] = "RemoteCreateInstance",
|
||||
["000001a0-0000-0000-c000-000000000046",3] = "RemoteGetClassObject",
|
||||
["000001a0-0000-0000-c000-000000000046",4] = "RemoteCreateInstance",
|
||||
|
||||
# netlogon
|
||||
["12345678-1234-abcd-ef00-01234567cffb",0] = "NetrLogonUasLogon",
|
||||
|
|
|
@ -4,17 +4,186 @@
|
|||
module DHCP;
|
||||
|
||||
export {
|
||||
|
||||
## Types of DHCP messages. See :rfc:`1533`.
|
||||
## Types of DHCP messages. See :rfc:`1533`, :rfc:`3203`,
|
||||
## :rfc:`4388`, :rfc:`6926`, and :rfc:`7724`.
|
||||
const message_types = {
|
||||
[1] = "DHCP_DISCOVER",
|
||||
[2] = "DHCP_OFFER",
|
||||
[3] = "DHCP_REQUEST",
|
||||
[4] = "DHCP_DECLINE",
|
||||
[5] = "DHCP_ACK",
|
||||
[6] = "DHCP_NAK",
|
||||
[7] = "DHCP_RELEASE",
|
||||
[8] = "DHCP_INFORM",
|
||||
[1] = "DISCOVER",
|
||||
[2] = "OFFER",
|
||||
[3] = "REQUEST",
|
||||
[4] = "DECLINE",
|
||||
[5] = "ACK",
|
||||
[6] = "NAK",
|
||||
[7] = "RELEASE",
|
||||
[8] = "INFORM",
|
||||
[9] = "FORCERENEW", # RFC3203
|
||||
[10] = "LEASEQUERY", # RFC4388
|
||||
[11] = "LEASEUNASSIGNED", # RFC4388
|
||||
[12] = "LEASEUNKNOWN", # RFC4388
|
||||
[13] = "LEASEACTIVE", # RFC4388
|
||||
[14] = "BULKLEASEQUERY", # RFC6926
|
||||
[15] = "LEASEQUERYDONE", # RFC6926
|
||||
[16] = "ACTIVELEASEQUERY", # RFC7724
|
||||
[17] = "LEASEQUERYSTATUS", # RFC7724
|
||||
[18] = "TLS", # RFC7724
|
||||
} &default = function(n: count): string { return fmt("unknown-message-type-%d", n); };
|
||||
|
||||
## Option types mapped to their names.
|
||||
const option_types: table[int] of string = {
|
||||
[0] = "Pad",
|
||||
[1] = "Subnet Mask",
|
||||
[2] = "Time Offset",
|
||||
[3] = "Router",
|
||||
[4] = "Time Server",
|
||||
[5] = "Name Server",
|
||||
[6] = "Domain Server",
|
||||
[7] = "Log Server",
|
||||
[8] = "Quotes Server",
|
||||
[9] = "LPR Server",
|
||||
[10] = "Impress Server",
|
||||
[11] = "RLP Server",
|
||||
[12] = "Hostname",
|
||||
[13] = "Boot File Size",
|
||||
[14] = "Merit Dump File",
|
||||
[15] = "Domain Name",
|
||||
[16] = "Swap Server",
|
||||
[17] = "Root Path",
|
||||
[18] = "Extension File",
|
||||
[19] = "Forward On/Off",
|
||||
[20] = "SrcRte On/Off",
|
||||
[21] = "Policy Filter",
|
||||
[22] = "Max DG Assembly",
|
||||
[23] = "Default IP TTL",
|
||||
[24] = "MTU Timeout",
|
||||
[25] = "MTU Plateau",
|
||||
[26] = "MTU Interface",
|
||||
[27] = "MTU Subnet",
|
||||
[28] = "Broadcast Address",
|
||||
[29] = "Mask Discovery",
|
||||
[30] = "Mask Supplier",
|
||||
[31] = "Router Discovery",
|
||||
[32] = "Router Request",
|
||||
[33] = "Static Route",
|
||||
[34] = "Trailers",
|
||||
[35] = "ARP Timeout",
|
||||
[36] = "Ethernet",
|
||||
[37] = "Default TCP TTL",
|
||||
[38] = "Keepalive Time",
|
||||
[39] = "Keepalive Data",
|
||||
[40] = "NIS Domain",
|
||||
[41] = "NIS Servers",
|
||||
[42] = "NTP Servers",
|
||||
[43] = "Vendor Specific",
|
||||
[44] = "NETBIOS Name Srv",
|
||||
[45] = "NETBIOS Dist Srv",
|
||||
[46] = "NETBIOS Node Type",
|
||||
[47] = "NETBIOS Scope",
|
||||
[48] = "X Window Font",
|
||||
[49] = "X Window Manager",
|
||||
[50] = "Address Request",
|
||||
[51] = "Address Time",
|
||||
[52] = "Overload",
|
||||
[53] = "DHCP Msg Type",
|
||||
[54] = "DHCP Server Id",
|
||||
[55] = "Parameter List",
|
||||
[56] = "DHCP Message",
|
||||
[57] = "DHCP Max Msg Size",
|
||||
[58] = "Renewal Time",
|
||||
[59] = "Rebinding Time",
|
||||
[60] = "Class Id",
|
||||
[61] = "Client Id",
|
||||
[62] = "NetWare/IP Domain",
|
||||
[63] = "NetWare/IP Option",
|
||||
[64] = "NIS-Domain-Name",
|
||||
[65] = "NIS-Server-Addr",
|
||||
[66] = "Server-Name",
|
||||
[67] = "Bootfile-Name",
|
||||
[68] = "Home-Agent-Addrs",
|
||||
[69] = "SMTP-Server",
|
||||
[70] = "POP3-Server",
|
||||
[71] = "NNTP-Server",
|
||||
[72] = "WWW-Server",
|
||||
[73] = "Finger-Server",
|
||||
[74] = "IRC-Server",
|
||||
[75] = "StreetTalk-Server",
|
||||
[76] = "STDA-Server",
|
||||
[77] = "User-Class",
|
||||
[78] = "Directory Agent",
|
||||
[79] = "Service Scope",
|
||||
[80] = "Rapid Commit",
|
||||
[81] = "Client FQDN",
|
||||
[82] = "Relay Agent Information",
|
||||
[83] = "iSNS",
|
||||
[85] = "NDS Servers",
|
||||
[86] = "NDS Tree Name",
|
||||
[87] = "NDS Context",
|
||||
[88] = "BCMCS Controller Domain Name list",
|
||||
[89] = "BCMCS Controller IPv4 address option",
|
||||
[90] = "Authentication",
|
||||
[91] = "client-last-transaction-time option",
|
||||
[92] = "associated-ip option",
|
||||
[93] = "Client System",
|
||||
[94] = "Client NDI",
|
||||
[95] = "LDAP",
|
||||
[97] = "UUID/GUID",
|
||||
[98] = "User-Auth",
|
||||
[99] = "GEOCONF_CIVIC",
|
||||
[100] = "PCode",
|
||||
[101] = "TCode",
|
||||
[112] = "Netinfo Address",
|
||||
[113] = "Netinfo Tag",
|
||||
[114] = "URL",
|
||||
[116] = "Auto-Config",
|
||||
[117] = "Name Service Search",
|
||||
[118] = "Subnet Selection Option",
|
||||
[119] = "Domain Search",
|
||||
[120] = "SIP Servers DHCP Option",
|
||||
[121] = "Classless Static Route Option",
|
||||
[122] = "CCC",
|
||||
[123] = "GeoConf Option",
|
||||
[124] = "V-I Vendor Class",
|
||||
[125] = "V-I Vendor-Specific Information",
|
||||
[128] = "PXE - undefined (vendor specific)",
|
||||
[129] = "PXE - undefined (vendor specific)",
|
||||
[130] = "PXE - undefined (vendor specific)",
|
||||
[131] = "PXE - undefined (vendor specific)",
|
||||
[132] = "IEEE 802.1Q VLAN ID",
|
||||
[133] = "IEEE 802.1D/p Layer 2 Priority",
|
||||
[134] = "Diffserv Code Point (DSCP) for VoIP signalling and media streams",
|
||||
[135] = "HTTP Proxy for phone-specific applications",
|
||||
[136] = "OPTION_PANA_AGENT",
|
||||
[137] = "OPTION_V4_LOST",
|
||||
[138] = "OPTION_CAPWAP_AC_V4",
|
||||
[139] = "OPTION-IPv4_Address-MoS",
|
||||
[140] = "OPTION-IPv4_FQDN-MoS",
|
||||
[141] = "SIP UA Configuration Service Domains",
|
||||
[142] = "OPTION-IPv4_Address-ANDSF",
|
||||
[144] = "GeoLoc",
|
||||
[145] = "FORCERENEW_NONCE_CAPABLE",
|
||||
[146] = "RDNSS Selection",
|
||||
[150] = "TFTP server address",
|
||||
[151] = "status-code",
|
||||
[152] = "base-time",
|
||||
[153] = "start-time-of-state",
|
||||
[154] = "query-start-time",
|
||||
[155] = "query-end-time",
|
||||
[156] = "dhcp-state",
|
||||
[157] = "data-source",
|
||||
[158] = "OPTION_V4_PCP_SERVER",
|
||||
[159] = "OPTION_V4_PORTPARAMS",
|
||||
[160] = "DHCP Captive-Portal",
|
||||
[161] = "OPTION_MUD_URL_V4 (TEMPORARY - registered 2016-11-17)",
|
||||
[175] = "Etherboot (Tentatively Assigned - 2005-06-23)",
|
||||
[176] = "IP Telephone (Tentatively Assigned - 2005-06-23)",
|
||||
[177] = "PacketCable and CableHome (replaced by 122)",
|
||||
[208] = "PXELINUX Magic",
|
||||
[209] = "Configuration File",
|
||||
[210] = "Path Prefix",
|
||||
[211] = "Reboot Time",
|
||||
[212] = "OPTION_6RD",
|
||||
[213] = "OPTION_V4_ACCESS_DOMAIN",
|
||||
[220] = "Subnet Allocation Option",
|
||||
[221] = "Virtual Subnet Selection (VSS) Option",
|
||||
[252] = "auto-proxy-config",
|
||||
[255] = "End",
|
||||
} &default = function(n: int): string { return fmt("unknown-option-type-%d", n); };
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
signature dhcp_cookie {
|
||||
ip-proto == udp
|
||||
payload /^.*\x63\x82\x53\x63/
|
||||
payload /^.{236}\x63\x82\x53\x63/
|
||||
enable "dhcp"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
##! Analyzes DHCP traffic in order to log DHCP leases given to clients.
|
||||
##! This script ignores large swaths of the protocol, since it is rather
|
||||
##! noisy on most networks, and focuses on the end-result: assigned leases.
|
||||
##!
|
||||
##! If you'd like to track known DHCP devices and to log the hostname
|
||||
##! supplied by the client, see
|
||||
##! :doc:`/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro`.
|
||||
##! Analyze DHCP traffic and provide a log that is organized around
|
||||
##! the idea of a DHCP "conversation" defined by messages exchanged within
|
||||
##! a relatively short period of time using the same transaction ID.
|
||||
##! The log will have information from clients and servers to give a more
|
||||
##! complete picture of what happened.
|
||||
|
||||
@load ./utils.bro
|
||||
@load base/frameworks/cluster
|
||||
@load ./consts
|
||||
|
||||
module DHCP;
|
||||
|
||||
|
@ -17,22 +16,88 @@ export {
|
|||
type Info: record {
|
||||
## The earliest time at which a DHCP message over the
|
||||
## associated connection is observed.
|
||||
ts: time &log;
|
||||
## A unique identifier of the connection over which DHCP is
|
||||
## occurring.
|
||||
uid: string &log;
|
||||
## The connection's 4-tuple of endpoint addresses/ports.
|
||||
id: conn_id &log;
|
||||
ts: time &log;
|
||||
|
||||
## A series of unique identifiers of the connections over which
|
||||
## DHCP is occurring. This behavior with multiple connections is
|
||||
## unique to DHCP because of the way it uses broadcast packets
|
||||
## on local networks.
|
||||
uids: set[string] &log;
|
||||
|
||||
## IP address of the client. If a transaction
|
||||
## is only a client sending INFORM messages then
|
||||
## there is no lease information exchanged so this
|
||||
## is helpful to know who sent the messages.
|
||||
## Getting an address in this field does require
|
||||
## that the client sources at least one DHCP message
|
||||
## using a non-broadcast address.
|
||||
client_addr: addr &log &optional;
|
||||
## IP address of the server involved in actually
|
||||
## handing out the lease. There could be other
|
||||
## servers replying with OFFER messages which won't
|
||||
## be represented here. Getting an address in this
|
||||
## field also requires that the server handing out
|
||||
## the lease also sources packets from a non-broadcast
|
||||
## IP address.
|
||||
server_addr: addr &log &optional;
|
||||
|
||||
## Client port number seen at time of server handing out IP (expected
|
||||
## as 68/udp).
|
||||
client_port: port &optional;
|
||||
## Server port number seen at time of server handing out IP (expected
|
||||
## as 67/udp).
|
||||
server_port: port &optional;
|
||||
|
||||
## Client's hardware address.
|
||||
mac: string &log &optional;
|
||||
## Client's actual assigned IP address.
|
||||
assigned_ip: addr &log &optional;
|
||||
mac: string &log &optional;
|
||||
|
||||
## Name given by client in Hostname option 12.
|
||||
host_name: string &log &optional;
|
||||
## FQDN given by client in Client FQDN option 81.
|
||||
client_fqdn: string &log &optional;
|
||||
## Domain given by the server in option 15.
|
||||
domain: string &log &optional;
|
||||
|
||||
## IP address requested by the client.
|
||||
requested_addr: addr &log &optional;
|
||||
## IP address assigned by the server.
|
||||
assigned_addr: addr &log &optional;
|
||||
## IP address lease interval.
|
||||
lease_time: interval &log &optional;
|
||||
## A random number chosen by the client for this transaction.
|
||||
trans_id: count &log;
|
||||
lease_time: interval &log &optional;
|
||||
|
||||
## Message typically accompanied with a DHCP_DECLINE
|
||||
## so the client can tell the server why it rejected
|
||||
## an address.
|
||||
client_message: string &log &optional;
|
||||
## Message typically accompanied with a DHCP_NAK to let
|
||||
## the client know why it rejected the request.
|
||||
server_message: string &log &optional;
|
||||
|
||||
## The DHCP message types seen by this DHCP transaction
|
||||
msg_types: vector of string &log &default=string_vec();
|
||||
|
||||
## Duration of the DHCP "session" representing the
|
||||
## time from the first message to the last.
|
||||
duration: interval &log &default=0secs;
|
||||
};
|
||||
|
||||
## The maximum amount of time that a transation ID will be watched
|
||||
## for to try and tie messages together into a single DHCP
|
||||
## transaction narrative.
|
||||
const DHCP::max_txid_watch_time = 30secs &redef;
|
||||
|
||||
## This event is used internally to distribute data around clusters
|
||||
## since DHCP doesn't follow the normal "connection" model used by
|
||||
## most protocols. It can also be handled to extend the DHCP log.
|
||||
## bro:see::`DHCP::log_info`.
|
||||
global DHCP::aggregate_msgs: event(ts: time, id: conn_id, uid: string, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options);
|
||||
|
||||
## This is a global variable that is only to be used in the
|
||||
## :bro::see::`DHCP::aggregate_msgs` event. It can be used to avoid
|
||||
## looking up the info record for a transaction ID in every event handler
|
||||
## for :bro:see::`DHCP::aggregate_msgs`.
|
||||
global DHCP::log_info: Info;
|
||||
|
||||
## Event that can be handled to access the DHCP
|
||||
## record as it is sent on to the logging framework.
|
||||
global log_dhcp: event(rec: Info);
|
||||
|
@ -43,8 +108,13 @@ redef record connection += {
|
|||
dhcp: Info &optional;
|
||||
};
|
||||
|
||||
redef record Info += {
|
||||
last_message_ts: time &optional;
|
||||
};
|
||||
|
||||
# 67/udp is the server's port, 68/udp the client.
|
||||
const ports = { 67/udp, 68/udp };
|
||||
# 4011/udp seems to be some proxyDHCP thing.
|
||||
const ports = { 67/udp, 68/udp, 4011/udp };
|
||||
redef likely_server_ports += { 67/udp };
|
||||
|
||||
event bro_init() &priority=5
|
||||
|
@ -53,27 +123,150 @@ event bro_init() &priority=5
|
|||
Analyzer::register_for_ports(Analyzer::ANALYZER_DHCP, ports);
|
||||
}
|
||||
|
||||
event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=5
|
||||
@if ( Cluster::is_enabled() )
|
||||
event bro_init()
|
||||
{
|
||||
local info: Info;
|
||||
info$ts = network_time();
|
||||
info$id = c$id;
|
||||
info$uid = c$uid;
|
||||
info$lease_time = lease;
|
||||
info$trans_id = msg$xid;
|
||||
Broker::auto_publish(Cluster::manager_topic, DHCP::aggregate_msgs);
|
||||
}
|
||||
@endif
|
||||
|
||||
if ( msg$h_addr != "" )
|
||||
info$mac = msg$h_addr;
|
||||
function join_data_expiration(t: table[count] of Info, idx: count): interval
|
||||
{
|
||||
local info = t[idx];
|
||||
|
||||
if ( reverse_ip(msg$yiaddr) != 0.0.0.0 )
|
||||
info$assigned_ip = reverse_ip(msg$yiaddr);
|
||||
local now = network_time();
|
||||
# If a message hasn't been seen in the past 5 seconds or the
|
||||
# total time watching has been more than the maximum time
|
||||
# allowed by the configuration then log this data and expire it.
|
||||
# Also, if Bro is shutting down.
|
||||
if ( (now - info$last_message_ts) > 5sec ||
|
||||
(now - info$ts) > max_txid_watch_time ||
|
||||
bro_is_terminating() )
|
||||
{
|
||||
Log::write(LOG, info);
|
||||
|
||||
# Go ahead and expire the data now that the log
|
||||
# entry has been written.
|
||||
return 0secs;
|
||||
}
|
||||
else
|
||||
info$assigned_ip = c$id$orig_h;
|
||||
|
||||
c$dhcp = info;
|
||||
{
|
||||
return 5secs;
|
||||
}
|
||||
}
|
||||
|
||||
event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=-5
|
||||
# This is where the data is stored as it's centralized. All data for a log must
|
||||
# arrive within the expiration interval if it's to be logged fully. On a cluster,
|
||||
# this data is only maintained on the manager.
|
||||
global join_data: table[count] of Info = table()
|
||||
&create_expire=10secs &expire_func=join_data_expiration;
|
||||
|
||||
|
||||
|
||||
@if ( ! Cluster::is_enabled() || Cluster::local_node_type() == Cluster::MANAGER )
|
||||
# We are handling this event at priority 1000 because we really want
|
||||
# the DHCP::log_info global to be set correctly before a user might try
|
||||
# to access it.
|
||||
event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options) &priority=1000
|
||||
{
|
||||
Log::write(DHCP::LOG, c$dhcp);
|
||||
if ( msg$xid !in join_data )
|
||||
{
|
||||
join_data[msg$xid] = Info($ts=ts,
|
||||
$uids=set(uid));
|
||||
}
|
||||
|
||||
log_info = join_data[msg$xid];
|
||||
}
|
||||
|
||||
event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options) &priority=5
|
||||
{
|
||||
log_info$duration = ts - log_info$ts;
|
||||
|
||||
if ( uid !in log_info$uids )
|
||||
add log_info$uids[uid];
|
||||
|
||||
log_info$msg_types += DHCP::message_types[msg$m_type];
|
||||
|
||||
# Let's watch for messages in any DHCP message type
|
||||
# and split them out based on client and server.
|
||||
if ( options?$message )
|
||||
{
|
||||
if ( is_orig )
|
||||
log_info$client_message = options$message;
|
||||
else
|
||||
log_info$server_message = options$message;
|
||||
}
|
||||
|
||||
# Update the last message time so that we can do some data
|
||||
# expiration handling.
|
||||
log_info$last_message_ts = ts;
|
||||
|
||||
if ( is_orig ) # client requests
|
||||
{
|
||||
# Assign the client addr in case this is a session
|
||||
# of only INFORM messages (no lease handed out).
|
||||
# This also works if a normal lease handout uses
|
||||
# unicast.
|
||||
if ( id$orig_h != 0.0.0.0 && id$orig_h != 255.255.255.255 )
|
||||
log_info$client_addr = id$orig_h;
|
||||
|
||||
if ( options?$host_name )
|
||||
log_info$host_name = options$host_name;
|
||||
|
||||
if ( options?$client_fqdn )
|
||||
log_info$client_fqdn = options$client_fqdn$domain_name;
|
||||
|
||||
if ( options?$client_id &&
|
||||
options$client_id$hwtype == 1 ) # ETHERNET
|
||||
log_info$mac = options$client_id$hwaddr;
|
||||
|
||||
if ( options?$addr_request )
|
||||
log_info$requested_addr = options$addr_request;
|
||||
}
|
||||
else # server reply messages
|
||||
{
|
||||
# Only log the address of the server if it handed out
|
||||
# an IP address.
|
||||
if ( msg$yiaddr != 0.0.0.0 &&
|
||||
id$resp_h != 255.255.255.255 )
|
||||
{
|
||||
log_info$server_addr = id$resp_h;
|
||||
log_info$server_port = id$resp_p;
|
||||
log_info$client_port = id$orig_p;
|
||||
}
|
||||
|
||||
# Only use the client hardware address from the server
|
||||
# if we didn't already pick one up from the client.
|
||||
if ( msg$chaddr != "" && !log_info?$mac )
|
||||
log_info$mac = msg$chaddr;
|
||||
|
||||
if ( msg$yiaddr != 0.0.0.0 )
|
||||
log_info$assigned_addr = msg$yiaddr;
|
||||
|
||||
# If no client address has been seen yet, let's use the assigned addr.
|
||||
if ( ! log_info?$client_addr && log_info?$assigned_addr )
|
||||
log_info$client_addr = log_info$assigned_addr;
|
||||
|
||||
if ( options?$domain_name )
|
||||
log_info$domain = options$domain_name;
|
||||
|
||||
if ( options?$lease )
|
||||
log_info$lease_time = options$lease;
|
||||
}
|
||||
}
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
# Aggregate DHCP messages to the manager.
|
||||
event dhcp_message(c: connection, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options) &priority=-5
|
||||
{
|
||||
event DHCP::aggregate_msgs(network_time(), c$id, c$uid, is_orig, msg, options);
|
||||
}
|
||||
|
||||
event bro_done() &priority=-5
|
||||
{
|
||||
# Log any remaining data that hasn't already been logged!
|
||||
for ( i in DHCP::join_data )
|
||||
join_data_expiration(DHCP::join_data, i);
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
##! Utilities specific for DHCP processing.
|
||||
|
||||
module DHCP;
|
||||
|
||||
export {
|
||||
## Reverse the octets of an IPv4 address.
|
||||
##
|
||||
## ip: An IPv4 address.
|
||||
##
|
||||
## Returns: A reversed IPv4 address.
|
||||
global reverse_ip: function(ip: addr): addr;
|
||||
}
|
||||
|
||||
function reverse_ip(ip: addr): addr
|
||||
{
|
||||
local octets = split_string(cat(ip), /\./);
|
||||
return to_addr(cat(octets[3], ".", octets[2], ".", octets[1], ".", octets[0]));
|
||||
}
|
||||
|
|
@ -21,8 +21,8 @@ export {
|
|||
[29] = "LOC", [30] = "EID", [31] = "NIMLOC", [32] = "NB",
|
||||
[33] = "SRV", [34] = "ATMA", [35] = "NAPTR", [36] = "KX",
|
||||
[37] = "CERT", [38] = "A6", [39] = "DNAME", [40] = "SINK",
|
||||
[EDNS] = "EDNS", [42] = "APL", [43] = "DS", [44] = "SINK",
|
||||
[45] = "SSHFP", [46] = "RRSIG", [47] = "NSEC", [48] = "DNSKEY",
|
||||
[EDNS] = "EDNS", [42] = "APL", [43] = "DS", [44] = "SSHFP",
|
||||
[45] = "IPSECKEY", [46] = "RRSIG", [47] = "NSEC", [48] = "DNSKEY",
|
||||
[49] = "DHCID", [99] = "SPF", [100] = "DINFO", [101] = "UID",
|
||||
[102] = "GID", [103] = "UNSPEC", [249] = "TKEY", [250] = "TSIG",
|
||||
[251] = "IXFR", [252] = "AXFR", [253] = "MAILB", [254] = "MAILA",
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
##! their responses.
|
||||
|
||||
@load base/utils/queue
|
||||
@load base/frameworks/notice/weird
|
||||
@load ./consts
|
||||
|
||||
module DNS;
|
||||
|
@ -177,9 +176,6 @@ function log_unmatched_msgs_queue(q: Queue::Queue)
|
|||
|
||||
for ( i in infos )
|
||||
{
|
||||
local wi = Weird::Info($ts=network_time(), $name="dns_unmatched_msg", $uid=infos[i]$uid,
|
||||
$id=infos[i]$id);
|
||||
Weird::weird(wi);
|
||||
Log::write(DNS::LOG, infos[i]);
|
||||
}
|
||||
}
|
||||
|
@ -187,21 +183,19 @@ function log_unmatched_msgs_queue(q: Queue::Queue)
|
|||
function log_unmatched_msgs(msgs: PendingMessages)
|
||||
{
|
||||
for ( trans_id in msgs )
|
||||
{
|
||||
log_unmatched_msgs_queue(msgs[trans_id]);
|
||||
}
|
||||
|
||||
clear_table(msgs);
|
||||
}
|
||||
|
||||
function enqueue_new_msg(msgs: PendingMessages, id: count, msg: Info)
|
||||
{
|
||||
local wi: Weird::Info;
|
||||
if ( id !in msgs )
|
||||
{
|
||||
if ( |msgs| > max_pending_query_ids )
|
||||
{
|
||||
wi = Weird::Info($ts=network_time(), $name="dns_unmatched_msg", $uid=msg$uid,
|
||||
$id=msg$id);
|
||||
Weird::weird(wi);
|
||||
# Throw away all unmatched on assumption they'll never be matched.
|
||||
log_unmatched_msgs(msgs);
|
||||
}
|
||||
|
@ -212,9 +206,6 @@ function enqueue_new_msg(msgs: PendingMessages, id: count, msg: Info)
|
|||
{
|
||||
if ( Queue::len(msgs[id]) > max_pending_msgs )
|
||||
{
|
||||
wi = Weird::Info($ts=network_time(), $name="dns_unmatched_msg_quantity", $uid=msg$uid,
|
||||
$id=msg$id);
|
||||
Weird::weird(wi);
|
||||
log_unmatched_msgs_queue(msgs[id]);
|
||||
# Throw away all unmatched on assumption they'll never be matched.
|
||||
msgs[id] = Queue::init();
|
||||
|
@ -271,7 +262,6 @@ hook set_session(c: connection, msg: dns_msg, is_query: bool) &priority=5
|
|||
# Create a new DNS session and put it in the reply queue so
|
||||
# we can wait for a matching query.
|
||||
c$dns = new_session(c, msg$id);
|
||||
event conn_weird("dns_unmatched_reply", c, "");
|
||||
enqueue_new_msg(c$dns_state$pending_replies, msg$id, c$dns);
|
||||
}
|
||||
}
|
||||
|
@ -334,11 +324,11 @@ hook DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string)
|
|||
{
|
||||
if ( ! c$dns?$answers )
|
||||
c$dns$answers = vector();
|
||||
c$dns$answers[|c$dns$answers|] = reply;
|
||||
c$dns$answers += reply;
|
||||
|
||||
if ( ! c$dns?$TTLs )
|
||||
c$dns$TTLs = vector();
|
||||
c$dns$TTLs[|c$dns$TTLs|] = ans$TTL;
|
||||
c$dns$TTLs += ans$TTL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,9 @@ event ConnThreshold::bytes_threshold_crossed(c: connection, threshold: count, is
|
|||
if ( threshold < size_threshold || "gridftp-data" in c$service || c$duration > max_time )
|
||||
return;
|
||||
|
||||
if ( ! data_channel_initial_criteria(c) )
|
||||
return;
|
||||
|
||||
add c$service["gridftp-data"];
|
||||
event GridFTP::data_channel_detected(c);
|
||||
|
||||
|
|
|
@ -87,14 +87,14 @@ event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priori
|
|||
if ( ! c$http?$orig_fuids )
|
||||
c$http$orig_fuids = string_vec(f$id);
|
||||
else
|
||||
c$http$orig_fuids[|c$http$orig_fuids|] = f$id;
|
||||
c$http$orig_fuids += f$id;
|
||||
|
||||
if ( f$info?$filename )
|
||||
{
|
||||
if ( ! c$http?$orig_filenames )
|
||||
c$http$orig_filenames = string_vec(f$info$filename);
|
||||
else
|
||||
c$http$orig_filenames[|c$http$orig_filenames|] = f$info$filename;
|
||||
c$http$orig_filenames += f$info$filename;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,14 +103,14 @@ event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priori
|
|||
if ( ! c$http?$resp_fuids )
|
||||
c$http$resp_fuids = string_vec(f$id);
|
||||
else
|
||||
c$http$resp_fuids[|c$http$resp_fuids|] = f$id;
|
||||
c$http$resp_fuids += f$id;
|
||||
|
||||
if ( f$info?$filename )
|
||||
{
|
||||
if ( ! c$http?$resp_filenames )
|
||||
c$http$resp_filenames = string_vec(f$info$filename);
|
||||
else
|
||||
c$http$resp_filenames[|c$http$resp_filenames|] = f$info$filename;
|
||||
c$http$resp_filenames += f$info$filename;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -130,14 +130,14 @@ event file_sniff(f: fa_file, meta: fa_metadata) &priority=5
|
|||
if ( ! f$http?$orig_mime_types )
|
||||
f$http$orig_mime_types = string_vec(meta$mime_type);
|
||||
else
|
||||
f$http$orig_mime_types[|f$http$orig_mime_types|] = meta$mime_type;
|
||||
f$http$orig_mime_types += meta$mime_type;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! f$http?$resp_mime_types )
|
||||
f$http$resp_mime_types = string_vec(meta$mime_type);
|
||||
else
|
||||
f$http$resp_mime_types[|f$http$resp_mime_types|] = meta$mime_type;
|
||||
f$http$resp_mime_types += meta$mime_type;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ function extract_keys(data: string, kv_splitter: pattern): string_vec
|
|||
{
|
||||
local key_val = split_string1(parts[part_index], /=/);
|
||||
if ( 0 in key_val )
|
||||
key_vec[|key_vec|] = key_val[0];
|
||||
key_vec += key_val[0];
|
||||
}
|
||||
return key_vec;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
@load ./main
|
||||
@load base/utils/files
|
||||
@load base/frameworks/cluster
|
||||
|
||||
module IRC;
|
||||
|
||||
|
@ -23,9 +24,33 @@ export {
|
|||
## Sniffed mime type of the file.
|
||||
dcc_mime_type: string &log &optional;
|
||||
};
|
||||
|
||||
## The broker topic name to which expected DCC transfer updates are
|
||||
## relayed.
|
||||
const dcc_transfer_update_topic = "bro/irc/dcc_transfer_update" &redef;
|
||||
}
|
||||
|
||||
global dcc_expected_transfers: table[addr, port] of Info &synchronized &read_expire=5mins;
|
||||
global dcc_expected_transfers: table[addr, port] of Info &read_expire=5mins;
|
||||
|
||||
event dcc_transfer_add(host: addr, p: port, info: Info)
|
||||
{
|
||||
dcc_expected_transfers[host, p] = info;
|
||||
Analyzer::schedule_analyzer(0.0.0.0, host, p,
|
||||
Analyzer::ANALYZER_IRC_DATA, 5 min);
|
||||
}
|
||||
|
||||
event dcc_transfer_remove(host: addr, p: port)
|
||||
{
|
||||
delete dcc_expected_transfers[host, p];
|
||||
}
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
local lnt = Cluster::local_node_type();
|
||||
|
||||
if ( lnt == Cluster::WORKER )
|
||||
Broker::subscribe(dcc_transfer_update_topic);
|
||||
}
|
||||
|
||||
function log_dcc(f: fa_file)
|
||||
{
|
||||
|
@ -51,6 +76,9 @@ function log_dcc(f: fa_file)
|
|||
delete irc$dcc_mime_type;
|
||||
|
||||
delete dcc_expected_transfers[cid$resp_h, cid$resp_p];
|
||||
Cluster::relay_rr(Cluster::proxy_pool, dcc_transfer_update_topic,
|
||||
dcc_transfer_update_topic, dcc_transfer_remove,
|
||||
cid$resp_h, cid$resp_p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +102,9 @@ event irc_dcc_message(c: connection, is_orig: bool,
|
|||
local p = count_to_port(dest_port, tcp);
|
||||
Analyzer::schedule_analyzer(0.0.0.0, address, p, Analyzer::ANALYZER_IRC_DATA, 5 min);
|
||||
dcc_expected_transfers[address, p] = c$irc;
|
||||
Cluster::relay_rr(Cluster::proxy_pool, dcc_transfer_update_topic,
|
||||
dcc_transfer_update_topic, dcc_transfer_add,
|
||||
address, p, c$irc);
|
||||
}
|
||||
|
||||
event scheduled_analyzer_applied(c: connection, a: Analyzer::Tag) &priority=10
|
||||
|
@ -86,5 +117,10 @@ event scheduled_analyzer_applied(c: connection, a: Analyzer::Tag) &priority=10
|
|||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( [c$id$resp_h, c$id$resp_p] in dcc_expected_transfers )
|
||||
{
|
||||
delete dcc_expected_transfers[c$id$resp_h, c$id$resp_p];
|
||||
Cluster::relay_rr(Cluster::proxy_pool, dcc_transfer_update_topic,
|
||||
dcc_transfer_update_topic, dcc_transfer_remove,
|
||||
c$id$resp_h, c$id$resp_p);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,40 +78,23 @@ event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priori
|
|||
if ( f$source != "KRB_TCP" && f$source != "KRB" )
|
||||
return;
|
||||
|
||||
local info: Info;
|
||||
|
||||
if ( ! c?$krb )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
}
|
||||
else
|
||||
info = c$krb;
|
||||
set_session(c);
|
||||
|
||||
if ( is_orig )
|
||||
{
|
||||
info$client_cert = f$info;
|
||||
info$client_cert_fuid = f$id;
|
||||
c$krb$client_cert = f$info;
|
||||
c$krb$client_cert_fuid = f$id;
|
||||
}
|
||||
else
|
||||
{
|
||||
info$server_cert = f$info;
|
||||
info$server_cert_fuid = f$id;
|
||||
c$krb$server_cert = f$info;
|
||||
c$krb$server_cert_fuid = f$id;
|
||||
}
|
||||
|
||||
c$krb = info;
|
||||
|
||||
Files::add_analyzer(f, Files::ANALYZER_X509);
|
||||
# Always calculate hashes. They are not necessary for base scripts
|
||||
# but very useful for identification, and required for policy scripts
|
||||
Files::add_analyzer(f, Files::ANALYZER_MD5);
|
||||
Files::add_analyzer(f, Files::ANALYZER_SHA1);
|
||||
}
|
||||
|
||||
function fill_in_subjects(c: connection)
|
||||
{
|
||||
if ( !c?$krb )
|
||||
if ( ! c?$krb )
|
||||
return;
|
||||
|
||||
if ( c$krb?$client_cert && c$krb$client_cert?$x509 && c$krb$client_cert$x509?$certificate )
|
||||
|
|
|
@ -10,41 +10,41 @@ export {
|
|||
|
||||
type Info: record {
|
||||
## Timestamp for when the event happened.
|
||||
ts: time &log;
|
||||
ts: time &log;
|
||||
## Unique ID for the connection.
|
||||
uid: string &log;
|
||||
uid: string &log;
|
||||
## The connection's 4-tuple of endpoint addresses/ports.
|
||||
id: conn_id &log;
|
||||
id: conn_id &log;
|
||||
|
||||
## Request type - Authentication Service ("AS") or
|
||||
## Ticket Granting Service ("TGS")
|
||||
request_type: string &log &optional;
|
||||
request_type: string &log &optional;
|
||||
## Client
|
||||
client: string &log &optional;
|
||||
client: string &log &optional;
|
||||
## Service
|
||||
service: string &log;
|
||||
service: string &log &optional;
|
||||
|
||||
## Request result
|
||||
success: bool &log &optional;
|
||||
success: bool &log &optional;
|
||||
## Error code
|
||||
error_code: count &optional;
|
||||
error_code: count &optional;
|
||||
## Error message
|
||||
error_msg: string &log &optional;
|
||||
error_msg: string &log &optional;
|
||||
|
||||
## Ticket valid from
|
||||
from: time &log &optional;
|
||||
from: time &log &optional;
|
||||
## Ticket valid till
|
||||
till: time &log &optional;
|
||||
till: time &log &optional;
|
||||
## Ticket encryption type
|
||||
cipher: string &log &optional;
|
||||
cipher: string &log &optional;
|
||||
|
||||
## Forwardable ticket requested
|
||||
forwardable: bool &log &optional;
|
||||
forwardable: bool &log &optional;
|
||||
## Renewable ticket requested
|
||||
renewable: bool &log &optional;
|
||||
renewable: bool &log &optional;
|
||||
|
||||
## We've already logged this
|
||||
logged: bool &default=F;
|
||||
logged: bool &default=F;
|
||||
};
|
||||
|
||||
## The server response error texts which are *not* logged.
|
||||
|
@ -80,172 +80,140 @@ event bro_init() &priority=5
|
|||
Log::create_stream(KRB::LOG, [$columns=Info, $ev=log_krb, $path="kerberos"]);
|
||||
}
|
||||
|
||||
event krb_error(c: connection, msg: Error_Msg) &priority=5
|
||||
function set_session(c: connection): bool
|
||||
{
|
||||
local info: Info;
|
||||
|
||||
if ( msg?$error_text && msg$error_text in ignored_errors )
|
||||
if ( ! c?$krb )
|
||||
{
|
||||
if ( c?$krb ) delete c$krb;
|
||||
return;
|
||||
c$krb = Info($ts = network_time(),
|
||||
$uid = c$uid,
|
||||
$id = c$id);
|
||||
}
|
||||
|
||||
if ( c?$krb && c$krb$logged )
|
||||
return;
|
||||
|
||||
if ( c?$krb )
|
||||
info = c$krb;
|
||||
|
||||
if ( ! info?$ts )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
}
|
||||
|
||||
if ( ! info?$client && ( msg?$client_name || msg?$client_realm ) )
|
||||
info$client = fmt("%s%s", msg?$client_name ? msg$client_name + "/" : "",
|
||||
msg?$client_realm ? msg$client_realm : "");
|
||||
|
||||
info$service = msg$service_name;
|
||||
info$success = F;
|
||||
|
||||
info$error_code = msg$error_code;
|
||||
|
||||
if ( msg?$error_text ) info$error_msg = msg$error_text;
|
||||
else if ( msg$error_code in error_msg ) info$error_msg = error_msg[msg$error_code];
|
||||
|
||||
c$krb = info;
|
||||
|
||||
return c$krb$logged;
|
||||
}
|
||||
|
||||
event krb_error(c: connection, msg: Error_Msg) &priority=-5
|
||||
function do_log(c: connection)
|
||||
{
|
||||
if ( c?$krb )
|
||||
if ( c?$krb && ! c$krb$logged )
|
||||
{
|
||||
Log::write(KRB::LOG, c$krb);
|
||||
c$krb$logged = T;
|
||||
}
|
||||
}
|
||||
|
||||
event krb_as_request(c: connection, msg: KDC_Request) &priority=5
|
||||
event krb_error(c: connection, msg: Error_Msg) &priority=5
|
||||
{
|
||||
if ( c?$krb && c$krb$logged )
|
||||
if ( set_session(c) )
|
||||
return;
|
||||
|
||||
local info: Info;
|
||||
|
||||
if ( !c?$krb )
|
||||
if ( msg?$error_text && msg$error_text in ignored_errors )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
if ( c?$krb )
|
||||
delete c$krb;
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
info = c$krb;
|
||||
|
||||
info$request_type = "AS";
|
||||
info$client = fmt("%s/%s", msg?$client_name ? msg$client_name : "", msg$service_realm);
|
||||
info$service = msg$service_name;
|
||||
if ( ! c$krb?$client && ( msg?$client_name || msg?$client_realm ) )
|
||||
c$krb$client = fmt("%s%s", msg?$client_name ? msg$client_name + "/" : "",
|
||||
msg?$client_realm ? msg$client_realm : "");
|
||||
|
||||
if ( msg?$from )
|
||||
info$from = msg$from;
|
||||
c$krb$service = msg$service_name;
|
||||
c$krb$success = F;
|
||||
c$krb$error_code = msg$error_code;
|
||||
|
||||
info$till = msg$till;
|
||||
|
||||
info$forwardable = msg$kdc_options$forwardable;
|
||||
info$renewable = msg$kdc_options$renewable;
|
||||
|
||||
c$krb = info;
|
||||
if ( msg?$error_text )
|
||||
c$krb$error_msg = msg$error_text;
|
||||
else if ( msg$error_code in error_msg )
|
||||
c$krb$error_msg = error_msg[msg$error_code];
|
||||
}
|
||||
|
||||
event krb_tgs_request(c: connection, msg: KDC_Request) &priority=5
|
||||
event krb_error(c: connection, msg: Error_Msg) &priority=-5
|
||||
{
|
||||
if ( c?$krb && c$krb$logged )
|
||||
do_log(c);
|
||||
}
|
||||
|
||||
event krb_as_request(c: connection, msg: KDC_Request) &priority=5
|
||||
{
|
||||
if ( set_session(c) )
|
||||
return;
|
||||
|
||||
local info: Info;
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
info$request_type = "TGS";
|
||||
info$service = msg$service_name;
|
||||
if ( msg?$from ) info$from = msg$from;
|
||||
info$till = msg$till;
|
||||
c$krb$request_type = "AS";
|
||||
c$krb$client = fmt("%s/%s", msg?$client_name ? msg$client_name : "", msg$service_realm);
|
||||
c$krb$service = msg$service_name;
|
||||
|
||||
info$forwardable = msg$kdc_options$forwardable;
|
||||
info$renewable = msg$kdc_options$renewable;
|
||||
if ( msg?$from )
|
||||
c$krb$from = msg$from;
|
||||
c$krb$till = msg$till;
|
||||
|
||||
c$krb = info;
|
||||
c$krb$forwardable = msg$kdc_options$forwardable;
|
||||
c$krb$renewable = msg$kdc_options$renewable;
|
||||
}
|
||||
|
||||
event krb_as_response(c: connection, msg: KDC_Response) &priority=5
|
||||
{
|
||||
local info: Info;
|
||||
|
||||
if ( c?$krb && c$krb$logged )
|
||||
if ( set_session(c) )
|
||||
return;
|
||||
|
||||
if ( c?$krb )
|
||||
info = c$krb;
|
||||
|
||||
if ( ! info?$ts )
|
||||
if ( ! c$krb?$client && ( msg?$client_name || msg?$client_realm ) )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
c$krb$client = fmt("%s/%s", msg?$client_name ? msg$client_name : "",
|
||||
msg?$client_realm ? msg$client_realm : "");
|
||||
}
|
||||
|
||||
if ( ! info?$client && ( msg?$client_name || msg?$client_realm ) )
|
||||
info$client = fmt("%s/%s", msg?$client_name ? msg$client_name : "", msg?$client_realm ? msg$client_realm : "");
|
||||
|
||||
info$service = msg$ticket$service_name;
|
||||
info$cipher = cipher_name[msg$ticket$cipher];
|
||||
info$success = T;
|
||||
|
||||
c$krb = info;
|
||||
c$krb$service = msg$ticket$service_name;
|
||||
c$krb$cipher = cipher_name[msg$ticket$cipher];
|
||||
c$krb$success = T;
|
||||
}
|
||||
|
||||
event krb_as_response(c: connection, msg: KDC_Response) &priority=-5
|
||||
{
|
||||
Log::write(KRB::LOG, c$krb);
|
||||
c$krb$logged = T;
|
||||
do_log(c);
|
||||
}
|
||||
|
||||
event krb_ap_request(c: connection, ticket: KRB::Ticket, opts: KRB::AP_Options) &priority=5
|
||||
{
|
||||
if ( set_session(c) )
|
||||
return;
|
||||
}
|
||||
|
||||
event krb_tgs_request(c: connection, msg: KDC_Request) &priority=5
|
||||
{
|
||||
if ( set_session(c) )
|
||||
return;
|
||||
|
||||
c$krb$request_type = "TGS";
|
||||
c$krb$service = msg$service_name;
|
||||
if ( msg?$from )
|
||||
c$krb$from = msg$from;
|
||||
c$krb$till = msg$till;
|
||||
|
||||
c$krb$forwardable = msg$kdc_options$forwardable;
|
||||
c$krb$renewable = msg$kdc_options$renewable;
|
||||
}
|
||||
|
||||
event krb_tgs_response(c: connection, msg: KDC_Response) &priority=5
|
||||
{
|
||||
local info: Info;
|
||||
|
||||
if ( c?$krb && c$krb$logged )
|
||||
if ( set_session(c) )
|
||||
return;
|
||||
|
||||
if ( c?$krb )
|
||||
info = c$krb;
|
||||
|
||||
if ( ! info?$ts )
|
||||
if ( ! c$krb?$client && ( msg?$client_name || msg?$client_realm ) )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
c$krb$client = fmt("%s/%s", msg?$client_name ? msg$client_name : "",
|
||||
msg?$client_realm ? msg$client_realm : "");
|
||||
}
|
||||
|
||||
if ( ! info?$client && ( msg?$client_name || msg?$client_realm ) )
|
||||
info$client = fmt("%s/%s", msg?$client_name ? msg$client_name : "", msg?$client_realm ? msg$client_realm : "");
|
||||
|
||||
info$service = msg$ticket$service_name;
|
||||
info$cipher = cipher_name[msg$ticket$cipher];
|
||||
info$success = T;
|
||||
|
||||
c$krb = info;
|
||||
c$krb$service = msg$ticket$service_name;
|
||||
c$krb$cipher = cipher_name[msg$ticket$cipher];
|
||||
c$krb$success = T;
|
||||
}
|
||||
|
||||
event krb_tgs_response(c: connection, msg: KDC_Response) &priority=-5
|
||||
{
|
||||
Log::write(KRB::LOG, c$krb);
|
||||
c$krb$logged = T;
|
||||
do_log(c);
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( c?$krb && ! c$krb$logged )
|
||||
Log::write(KRB::LOG, c$krb);
|
||||
do_log(c);
|
||||
}
|
||||
|
|
|
@ -10,52 +10,51 @@ export {
|
|||
|
||||
type Info: record {
|
||||
## Timestamp for when the event happened.
|
||||
ts : time &log;
|
||||
ts : time &log;
|
||||
## Unique ID for the connection.
|
||||
uid : string &log;
|
||||
uid : string &log;
|
||||
## The connection's 4-tuple of endpoint addresses/ports.
|
||||
id : conn_id &log;
|
||||
id : conn_id &log;
|
||||
## The username, if present.
|
||||
username : string &log &optional;
|
||||
username : string &log &optional;
|
||||
## MAC address, if present.
|
||||
mac : string &log &optional;
|
||||
## Remote IP address, if present.
|
||||
remote_ip : addr &log &optional;
|
||||
mac : string &log &optional;
|
||||
## The address given to the network access server, if
|
||||
## present. This is only a hint from the RADIUS server
|
||||
## and the network access server is not required to honor
|
||||
## the address.
|
||||
framed_addr : addr &log &optional;
|
||||
## Remote IP address, if present. This is collected
|
||||
## from the Tunnel-Client-Endpoint attribute.
|
||||
remote_ip : addr &log &optional;
|
||||
## Connect info, if present.
|
||||
connect_info : string &log &optional;
|
||||
connect_info : string &log &optional;
|
||||
## Reply message from the server challenge. This is
|
||||
## frequently shown to the user authenticating.
|
||||
reply_msg : string &log &optional;
|
||||
## Successful or failed authentication.
|
||||
result : string &log &optional;
|
||||
## Whether this has already been logged and can be ignored.
|
||||
logged : bool &optional;
|
||||
result : string &log &optional;
|
||||
## The duration between the first request and
|
||||
## either the "Access-Accept" message or an error.
|
||||
## If the field is empty, it means that either
|
||||
## the request or response was not seen.
|
||||
ttl : interval &log &optional;
|
||||
|
||||
## Whether this has already been logged and can be ignored.
|
||||
logged : bool &default=F;
|
||||
};
|
||||
|
||||
## The amount of time we wait for an authentication response before
|
||||
## expiring it.
|
||||
const expiration_interval = 10secs &redef;
|
||||
|
||||
## Logs an authentication attempt if we didn't see a response in time.
|
||||
##
|
||||
## t: A table of Info records.
|
||||
##
|
||||
## idx: The index of the connection$radius table corresponding to the
|
||||
## radius authentication about to expire.
|
||||
##
|
||||
## Returns: 0secs, which when this function is used as an
|
||||
## :bro:attr:`&expire_func`, indicates to remove the element at
|
||||
## *idx* immediately.
|
||||
global expire: function(t: table[count] of Info, idx: count): interval;
|
||||
|
||||
## Event that can be handled to access the RADIUS record as it is sent on
|
||||
## to the loggin framework.
|
||||
## to the logging framework.
|
||||
global log_radius: event(rec: Info);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
radius: table[count] of Info &optional &write_expire=expiration_interval &expire_func=expire;
|
||||
radius: Info &optional;
|
||||
};
|
||||
|
||||
const ports = { 1812/udp };
|
||||
redef likely_server_ports += { ports };
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
|
@ -63,64 +62,86 @@ event bro_init() &priority=5
|
|||
Analyzer::register_for_ports(Analyzer::ANALYZER_RADIUS, ports);
|
||||
}
|
||||
|
||||
event radius_message(c: connection, result: RADIUS::Message)
|
||||
event radius_message(c: connection, result: RADIUS::Message) &priority=5
|
||||
{
|
||||
local info: Info;
|
||||
|
||||
if ( c?$radius && result$trans_id in c$radius )
|
||||
info = c$radius[result$trans_id];
|
||||
else
|
||||
if ( ! c?$radius )
|
||||
{
|
||||
c$radius = table();
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
c$radius = Info($ts = network_time(),
|
||||
$uid = c$uid,
|
||||
$id = c$id);
|
||||
}
|
||||
|
||||
switch ( RADIUS::msg_types[result$code] ) {
|
||||
switch ( RADIUS::msg_types[result$code] )
|
||||
{
|
||||
case "Access-Request":
|
||||
if ( result?$attributes ) {
|
||||
if ( result?$attributes )
|
||||
{
|
||||
# User-Name
|
||||
if ( ! info?$username && 1 in result$attributes )
|
||||
info$username = result$attributes[1][0];
|
||||
if ( ! c$radius?$username && 1 in result$attributes )
|
||||
c$radius$username = result$attributes[1][0];
|
||||
|
||||
# Calling-Station-Id (we expect this to be a MAC)
|
||||
if ( ! info?$mac && 31 in result$attributes )
|
||||
info$mac = normalize_mac(result$attributes[31][0]);
|
||||
if ( ! c$radius?$mac && 31 in result$attributes )
|
||||
c$radius$mac = normalize_mac(result$attributes[31][0]);
|
||||
|
||||
# Tunnel-Client-EndPoint (useful for VPNs)
|
||||
if ( ! info?$remote_ip && 66 in result$attributes )
|
||||
info$remote_ip = to_addr(result$attributes[66][0]);
|
||||
if ( ! c$radius?$remote_ip && 66 in result$attributes )
|
||||
c$radius$remote_ip = to_addr(result$attributes[66][0]);
|
||||
|
||||
# Connect-Info
|
||||
if ( ! info?$connect_info && 77 in result$attributes )
|
||||
info$connect_info = result$attributes[77][0];
|
||||
}
|
||||
if ( ! c$radius?$connect_info && 77 in result$attributes )
|
||||
c$radius$connect_info = result$attributes[77][0];
|
||||
}
|
||||
break;
|
||||
|
||||
case "Access-Challenge":
|
||||
if ( result?$attributes )
|
||||
{
|
||||
# Framed-IP-Address
|
||||
if ( ! c$radius?$framed_addr && 8 in result$attributes )
|
||||
c$radius$framed_addr = raw_bytes_to_v4_addr(result$attributes[8][0]);
|
||||
|
||||
if ( ! c$radius?$reply_msg && 18 in result$attributes )
|
||||
c$radius$reply_msg = result$attributes[18][0];
|
||||
}
|
||||
break;
|
||||
|
||||
case "Access-Accept":
|
||||
info$result = "success";
|
||||
c$radius$result = "success";
|
||||
break;
|
||||
|
||||
case "Access-Reject":
|
||||
info$result = "failed";
|
||||
c$radius$result = "failed";
|
||||
break;
|
||||
}
|
||||
|
||||
if ( info?$result && ! info?$logged )
|
||||
{
|
||||
info$logged = T;
|
||||
Log::write(RADIUS::LOG, info);
|
||||
# TODO: Support RADIUS accounting. (add port 1813/udp above too)
|
||||
#case "Accounting-Request":
|
||||
# break;
|
||||
#
|
||||
#case "Accounting-Response":
|
||||
# break;
|
||||
}
|
||||
|
||||
c$radius[result$trans_id] = info;
|
||||
}
|
||||
|
||||
event radius_message(c: connection, result: RADIUS::Message) &priority=-5
|
||||
{
|
||||
if ( c$radius?$result )
|
||||
{
|
||||
local ttl = network_time() - c$radius$ts;
|
||||
if ( ttl != 0secs )
|
||||
c$radius$ttl = ttl;
|
||||
|
||||
function expire(t: table[count] of Info, idx: count): interval
|
||||
{
|
||||
t[idx]$result = "unknown";
|
||||
Log::write(RADIUS::LOG, t[idx]);
|
||||
return 0secs;
|
||||
}
|
||||
Log::write(RADIUS::LOG, c$radius);
|
||||
|
||||
delete c$radius;
|
||||
}
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( c?$radius && ! c$radius$logged )
|
||||
{
|
||||
c$radius$result = "unknown";
|
||||
Log::write(RADIUS::LOG, c$radius);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -236,10 +236,6 @@ event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priori
|
|||
{
|
||||
# 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
signature dpd_rfb_server {
|
||||
ip-proto == tcp
|
||||
payload /^RFB/
|
||||
tcp-state responder
|
||||
requires-reverse-signature dpd_rfb_client
|
||||
enable "rfb"
|
||||
}
|
||||
|
@ -9,4 +10,4 @@ signature dpd_rfb_client {
|
|||
ip-proto == tcp
|
||||
payload /^RFB/
|
||||
tcp-state originator
|
||||
}
|
||||
}
|
||||
|
|
|
@ -226,7 +226,7 @@ event sip_header(c: connection, is_request: bool, name: string, value: string) &
|
|||
c$sip$user_agent = value;
|
||||
break;
|
||||
case "VIA", "V":
|
||||
c$sip$request_path[|c$sip$request_path|] = split_string1(value, /;[ ]?branch/)[0];
|
||||
c$sip$request_path += split_string1(value, /;[ ]?branch/)[0];
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -256,7 +256,7 @@ event sip_header(c: connection, is_request: bool, name: string, value: string) &
|
|||
c$sip$response_to = value;
|
||||
break;
|
||||
case "VIA", "V":
|
||||
c$sip$response_path[|c$sip$response_path|] = split_string1(value, /;[ ]?branch/)[0];
|
||||
c$sip$response_path += split_string1(value, /;[ ]?branch/)[0];
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Definitions of constants used by the SMB protocol.
|
||||
Support for SMB protocol analysis.
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
@load ./consts
|
||||
@load ./const-dos-error
|
||||
@load ./const-nt-status
|
||||
|
||||
@load ./main
|
||||
@load ./smb1-main
|
||||
@load ./smb2-main
|
||||
@load ./files
|
||||
|
||||
@load-sigs ./dpd.sig
|
||||
|
|
|
@ -64,8 +64,8 @@ redef SMB::statuses += {
|
|||
[0x40000007] = [$id="BAD_CURRENT_DIRECTORY", $desc="{Invalid Current Directory} The process cannot switch to the startup current directory %hs. Select OK to set the current directory to %hs, or select CANCEL to exit."],
|
||||
[0x40000008] = [$id="SERIAL_MORE_WRITES", $desc="{Serial IOCTL Complete} A serial I/O operation was completed by another write to a serial port. (The IOCTL_SERIAL_XOFF_COUNTER reached zero.)"],
|
||||
[0x40000009] = [$id="REGISTRY_RECOVERED", $desc="{Registry Recovery} One of the files that contains the system registry data had to be recovered by using a log or alternate copy. The recovery was successful."],
|
||||
[0x4000000A] = [$id="FT_READ_RECOVERY_FROM_BACKUP", $desc="{Redundant Read} To satisfy a read request, the Windows NT fault-tolerant file system successfully read the requested data from a redundant copy. This was done because the file system encountered a failure on a member of the fault-tolerant volume but was unable to reassign the failing area of the device."],
|
||||
[0x4000000B] = [$id="FT_WRITE_RECOVERY", $desc="{Redundant Write} To satisfy a write request, the Windows NT fault-tolerant file system successfully wrote a redundant copy of the information. This was done because the file system encountered a failure on a member of the fault-tolerant volume but was unable to reassign the failing area of the device."],
|
||||
[0x4000000A] = [$id="FT_READ_RECOVERY_FROM_BACKUP", $desc="{Redundant Read} To satisfy a read request, the Windows NT fault-tolerant file system successfully read the requested data from a redundant copy. This was done because the file system encountered a failure on a member of the fault-tolerant volume but was unable to reassign the failing area of the device."],
|
||||
[0x4000000B] = [$id="FT_WRITE_RECOVERY", $desc="{Redundant Write} To satisfy a write request, the Windows NT fault-tolerant file system successfully wrote a redundant copy of the information. This was done because the file system encountered a failure on a member of the fault-tolerant volume but was unable to reassign the failing area of the device."],
|
||||
[0x4000000C] = [$id="SERIAL_COUNTER_TIMEOUT", $desc="{Serial IOCTL Timeout} A serial I/O operation completed because the time-out period expired. (The IOCTL_SERIAL_XOFF_COUNTER had not reached zero.)"],
|
||||
[0x4000000D] = [$id="NULL_LM_PASSWORD", $desc="{Password Too Complex} The Windows password is too complex to be converted to a LAN Manager password. The LAN Manager password that returned is a NULL string."],
|
||||
[0x4000000E] = [$id="IMAGE_MACHINE_TYPE_MISMATCH", $desc="{Machine Type Mismatch} The image file %hs is valid but is for a machine type other than the current machine. Select OK to continue, or CANCEL to fail the DLL load."],
|
||||
|
@ -494,7 +494,7 @@ redef SMB::statuses += {
|
|||
[0xC0000131] = [$id="INVALID_IMAGE_WIN_16", $desc="The specified image file did not have the correct format: it appears to be a 16-bit Windows image."],
|
||||
[0xC0000132] = [$id="LOGON_SERVER_CONFLICT", $desc="The Netlogon service cannot start because another Netlogon service running in the domain conflicts with the specified role."],
|
||||
[0xC0000133] = [$id="TIME_DIFFERENCE_AT_DC", $desc="The time at the primary domain controller is different from the time at the backup domain controller or member server by too large an amount."],
|
||||
[0xC0000134] = [$id="SYNCHRONIZATION_REQUIRED", $desc="The SAM database on a Windows Server is significantly out of synchronization with the copy on the domain controller. A complete synchronization is required."],
|
||||
[0xC0000134] = [$id="SYNCHRONIZATION_REQUIRED", $desc="The SAM database on a Windows Server is significantly out of synchronization with the copy on the domain controller. A complete synchronization is required."],
|
||||
[0xC0000135] = [$id="DLL_NOT_FOUND", $desc="{Unable To Locate Component} This application has failed to start because %hs was not found. Reinstalling the application may fix this problem."],
|
||||
[0xC0000136] = [$id="OPEN_FAILED", $desc="The NtCreateFile API failed. This error should never be returned to an application; it is a place holder for the Windows LAN Manager Redirector to use in its internal error-mapping routines."],
|
||||
[0xC0000137] = [$id="IO_PRIVILEGE_FAILED", $desc="{Privilege Failed} The I/O permissions for the process could not be changed."],
|
||||
|
@ -536,7 +536,7 @@ redef SMB::statuses += {
|
|||
[0xC000015B] = [$id="LOGON_TYPE_NOT_GRANTED", $desc="A user has requested a type of logon (for example, interactive or network) that has not been granted. An administrator has control over who may logon interactively and through the network."],
|
||||
[0xC000015C] = [$id="NOT_REGISTRY_FILE", $desc="The system has attempted to load or restore a file into the registry, and the specified file is not in the format of a registry file."],
|
||||
[0xC000015D] = [$id="NT_CROSS_ENCRYPTION_REQUIRED", $desc="An attempt was made to change a user password in the security account manager without providing the necessary Windows cross-encrypted password."],
|
||||
[0xC000015E] = [$id="DOMAIN_CTRLR_CONFIG_ERROR", $desc="A Windows Server has an incorrect configuration."],
|
||||
[0xC000015E] = [$id="DOMAIN_CTRLR_CONFIG_ERROR", $desc="A Windows Server has an incorrect configuration."],
|
||||
[0xC000015F] = [$id="FT_MISSING_MEMBER", $desc="An attempt was made to explicitly access the secondary copy of information via a device control to the fault tolerance driver and the secondary copy is not present in the system."],
|
||||
[0xC0000160] = [$id="ILL_FORMED_SERVICE_ENTRY", $desc="A configuration registry node that represents a driver service entry was ill-formed and did not contain the required value entries."],
|
||||
[0xC0000161] = [$id="ILLEGAL_CHARACTER", $desc="An illegal character was encountered. For a multibyte character set, this includes a lead byte without a succeeding trail byte. For the Unicode character set this includes the characters 0xFFFF and 0xFFFE."],
|
||||
|
@ -577,7 +577,7 @@ redef SMB::statuses += {
|
|||
[0xC0000188] = [$id="LOG_FILE_FULL", $desc="The log file space is insufficient to support this operation."],
|
||||
[0xC0000189] = [$id="TOO_LATE", $desc="A write operation was attempted to a volume after it was dismounted."],
|
||||
[0xC000018A] = [$id="NO_TRUST_LSA_SECRET", $desc="The workstation does not have a trust secret for the primary domain in the local LSA database."],
|
||||
[0xC000018B] = [$id="NO_TRUST_SAM_ACCOUNT", $desc="The SAM database on the Windows Server does not have a computer account for this workstation trust relationship."],
|
||||
[0xC000018B] = [$id="NO_TRUST_SAM_ACCOUNT", $desc="The SAM database on the Windows Server does not have a computer account for this workstation trust relationship."],
|
||||
[0xC000018C] = [$id="TRUSTED_DOMAIN_FAILURE", $desc="The logon request failed because the trust relationship between the primary domain and the trusted domain failed."],
|
||||
[0xC000018D] = [$id="TRUSTED_RELATIONSHIP_FAILURE", $desc="The logon request failed because the trust relationship between this workstation and the primary domain failed."],
|
||||
[0xC000018E] = [$id="EVENTLOG_FILE_CORRUPT", $desc="The Eventlog log file is corrupt."],
|
||||
|
@ -833,18 +833,18 @@ redef SMB::statuses += {
|
|||
[0xC00002FD] = [$id="KDC_UNKNOWN_ETYPE", $desc="The encryption type requested is not supported by the KDC."],
|
||||
[0xC00002FE] = [$id="SHUTDOWN_IN_PROGRESS", $desc="A system shutdown is in progress."],
|
||||
[0xC00002FF] = [$id="SERVER_SHUTDOWN_IN_PROGRESS", $desc="The server machine is shutting down."],
|
||||
[0xC0000300] = [$id="NOT_SUPPORTED_ON_SBS", $desc="This operation is not supported on a computer running Windows Server 2003 for Small Business Server."],
|
||||
[0xC0000300] = [$id="NOT_SUPPORTED_ON_SBS", $desc="This operation is not supported on a computer running Windows Server 2003 for Small Business Server."],
|
||||
[0xC0000301] = [$id="WMI_GUID_DISCONNECTED", $desc="The WMI GUID is no longer available."],
|
||||
[0xC0000302] = [$id="WMI_ALREADY_DISABLED", $desc="Collection or events for the WMI GUID is already disabled."],
|
||||
[0xC0000303] = [$id="WMI_ALREADY_ENABLED", $desc="Collection or events for the WMI GUID is already enabled."],
|
||||
[0xC0000304] = [$id="MFT_TOO_FRAGMENTED", $desc="The master file table on the volume is too fragmented to complete this operation."],
|
||||
[0xC0000305] = [$id="COPY_PROTECTION_FAILURE", $desc="Copy protection failure."],
|
||||
[0xC0000306] = [$id="CSS_AUTHENTICATION_FAILURE", $desc="Copy protection error—DVD CSS Authentication failed."],
|
||||
[0xC0000307] = [$id="CSS_KEY_NOT_PRESENT", $desc="Copy protection error—The specified sector does not contain a valid key."],
|
||||
[0xC0000308] = [$id="CSS_KEY_NOT_ESTABLISHED", $desc="Copy protection error—DVD session key not established."],
|
||||
[0xC0000309] = [$id="CSS_SCRAMBLED_SECTOR", $desc="Copy protection error—The read failed because the sector is encrypted."],
|
||||
[0xC000030A] = [$id="CSS_REGION_MISMATCH", $desc="Copy protection error—The region of the specified DVD does not correspond to the region setting of the drive."],
|
||||
[0xC000030B] = [$id="CSS_RESETS_EXHAUSTED", $desc="Copy protection error—The region setting of the drive may be permanent."],
|
||||
[0xC0000306] = [$id="CSS_AUTHENTICATION_FAILURE", $desc="Copy protection error-DVD CSS Authentication failed."],
|
||||
[0xC0000307] = [$id="CSS_KEY_NOT_PRESENT", $desc="Copy protection error-The specified sector does not contain a valid key."],
|
||||
[0xC0000308] = [$id="CSS_KEY_NOT_ESTABLISHED", $desc="Copy protection error-DVD session key not established."],
|
||||
[0xC0000309] = [$id="CSS_SCRAMBLED_SECTOR", $desc="Copy protection error-The read failed because the sector is encrypted."],
|
||||
[0xC000030A] = [$id="CSS_REGION_MISMATCH", $desc="Copy protection error-The region of the specified DVD does not correspond to the region setting of the drive."],
|
||||
[0xC000030B] = [$id="CSS_RESETS_EXHAUSTED", $desc="Copy protection error-The region setting of the drive may be permanent."],
|
||||
[0xC0000320] = [$id="PKINIT_FAILURE", $desc="The Kerberos protocol encountered an error while validating the KDC certificate during smart card logon. There is more information in the system event log."],
|
||||
[0xC0000321] = [$id="SMARTCARD_SUBSYSTEM_FAILURE", $desc="The Kerberos protocol encountered an error while attempting to use the smart card subsystem."],
|
||||
[0xC0000322] = [$id="NO_KERB_KEY", $desc="The target server does not have acceptable Kerberos credentials."],
|
||||
|
@ -855,7 +855,7 @@ redef SMB::statuses += {
|
|||
[0xC0000354] = [$id="DEBUGGER_INACTIVE", $desc="An attempt to do an operation on a debug port failed because the port is in the process of being deleted."],
|
||||
[0xC0000355] = [$id="DS_VERSION_CHECK_FAILURE", $desc="This version of Windows is not compatible with the behavior version of the directory forest, domain, or domain controller."],
|
||||
[0xC0000356] = [$id="AUDITING_DISABLED", $desc="The specified event is currently not being audited."],
|
||||
[0xC0000357] = [$id="PRENT4_MACHINE_ACCOUNT", $desc="The machine account was created prior to Windows NT 4.0. The account needs to be recreated."],
|
||||
[0xC0000357] = [$id="PRENT4_MACHINE_ACCOUNT", $desc="The machine account was created prior to Windows NT 4.0. The account needs to be recreated."],
|
||||
[0xC0000358] = [$id="DS_AG_CANT_HAVE_UNIVERSAL_MEMBER", $desc="An account group cannot have a universal group as a member."],
|
||||
[0xC0000359] = [$id="INVALID_IMAGE_WIN_32", $desc="The specified image file did not have the correct format; it appears to be a 32-bit Windows image."],
|
||||
[0xC000035A] = [$id="INVALID_IMAGE_WIN_64", $desc="The specified image file did not have the correct format; it appears to be a 64-bit Windows image."],
|
||||
|
@ -1790,4 +1790,4 @@ redef SMB::statuses += {
|
|||
[0xC03A0017] = [$id="VHD_CHILD_PARENT_SIZE_MISMATCH", $desc="The chain of virtual hard disks is corrupted. There is a mismatch in the virtual sizes of the parent virtual hard disk and differencing disk."],
|
||||
[0xC03A0018] = [$id="VHD_DIFFERENCING_CHAIN_CYCLE_DETECTED", $desc="The chain of virtual hard disks is corrupted. A differencing disk is indicated in its own parent chain."],
|
||||
[0xC03A0019] = [$id="VHD_DIFFERENCING_CHAIN_ERROR_IN_PARENT", $desc="The chain of virtual hard disks is inaccessible. There was an error opening a virtual hard disk further up the chain."],
|
||||
};
|
||||
};
|
||||
|
|
|
@ -255,10 +255,12 @@ export {
|
|||
} &default=function(i: count): string { return fmt("unknown-%d", i); };
|
||||
|
||||
const dialects: table[count] of string = {
|
||||
[0x0202] = "2.002",
|
||||
[0x0202] = "2.0.2",
|
||||
[0x0210] = "2.1",
|
||||
[0x0300] = "3.0",
|
||||
[0x0302] = "3.02",
|
||||
[0x0302] = "3.0.2",
|
||||
[0x0311] = "3.1.1",
|
||||
[0x02FF] = "2.1+",
|
||||
} &default=function(i: count): string { return fmt("unknown-%d", i); };
|
||||
|
||||
const share_types: table[count] of string = {
|
||||
|
|
5
scripts/base/protocols/smb/dpd.sig
Normal file
5
scripts/base/protocols/smb/dpd.sig
Normal file
|
@ -0,0 +1,5 @@
|
|||
signature dpd_smb {
|
||||
ip-proto == tcp
|
||||
payload /^....[\xfe\xff]SMB/
|
||||
enable "smb"
|
||||
}
|
69
scripts/base/protocols/smb/files.bro
Normal file
69
scripts/base/protocols/smb/files.bro
Normal file
|
@ -0,0 +1,69 @@
|
|||
@load base/frameworks/files
|
||||
@load ./main
|
||||
|
||||
module SMB;
|
||||
|
||||
export {
|
||||
## Default file handle provider for SMB.
|
||||
global get_file_handle: function(c: connection, is_orig: bool): string;
|
||||
|
||||
## Default file describer for SMB.
|
||||
global describe_file: function(f: fa_file): string;
|
||||
}
|
||||
|
||||
function get_file_handle(c: connection, is_orig: bool): string
|
||||
{
|
||||
if ( ! (c$smb_state?$current_file &&
|
||||
(c$smb_state$current_file?$name ||
|
||||
c$smb_state$current_file?$path)) )
|
||||
{
|
||||
# TODO - figure out what are the cases where this happens.
|
||||
return "";
|
||||
}
|
||||
local current_file = c$smb_state$current_file;
|
||||
local path_name = current_file?$path ? current_file$path : "";
|
||||
local file_name = current_file?$name ? current_file$name : "";
|
||||
# Include last_mod time if available because if a file has been modified it
|
||||
# should be considered a new file.
|
||||
local last_mod = cat(current_file?$times ? current_file$times$modified : double_to_time(0.0));
|
||||
# TODO: This is doing hexdump to avoid problems due to file analysis handling
|
||||
# using CheckString which is not immune to encapsulated null bytes.
|
||||
# This needs to be fixed lower in the file analysis code later.
|
||||
return hexdump(cat(Analyzer::ANALYZER_SMB, c$id$orig_h, c$id$resp_h, path_name, file_name, last_mod));
|
||||
}
|
||||
|
||||
function describe_file(f: fa_file): string
|
||||
{
|
||||
# This shouldn't be needed, but just in case...
|
||||
if ( f$source != "SMB" )
|
||||
return "";
|
||||
|
||||
for ( cid in f$conns )
|
||||
{
|
||||
local info = f$conns[cid];
|
||||
if ( info?$smb_state && info$smb_state?$current_file && info$smb_state$current_file?$name )
|
||||
return info$smb_state$current_file$name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Files::register_protocol(Analyzer::ANALYZER_SMB,
|
||||
[$get_file_handle = SMB::get_file_handle,
|
||||
$describe = SMB::describe_file]);
|
||||
}
|
||||
|
||||
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5
|
||||
{
|
||||
if ( c?$smb_state && c$smb_state?$current_file )
|
||||
{
|
||||
c$smb_state$current_file$fuid = f$id;
|
||||
|
||||
if ( c$smb_state$current_file$size > 0 )
|
||||
f$total_bytes = c$smb_state$current_file$size;
|
||||
|
||||
if ( c$smb_state$current_file?$name )
|
||||
f$info$filename = c$smb_state$current_file$name;
|
||||
}
|
||||
}
|
250
scripts/base/protocols/smb/main.bro
Normal file
250
scripts/base/protocols/smb/main.bro
Normal file
|
@ -0,0 +1,250 @@
|
|||
@load ./consts
|
||||
@load ./const-dos-error
|
||||
@load ./const-nt-status
|
||||
|
||||
module SMB;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += {
|
||||
AUTH_LOG,
|
||||
MAPPING_LOG,
|
||||
FILES_LOG
|
||||
};
|
||||
|
||||
## Abstracted actions for SMB file actions.
|
||||
type Action: enum {
|
||||
FILE_READ,
|
||||
FILE_WRITE,
|
||||
FILE_OPEN,
|
||||
FILE_CLOSE,
|
||||
FILE_DELETE,
|
||||
FILE_RENAME,
|
||||
FILE_SET_ATTRIBUTE,
|
||||
|
||||
PIPE_READ,
|
||||
PIPE_WRITE,
|
||||
PIPE_OPEN,
|
||||
PIPE_CLOSE,
|
||||
|
||||
PRINT_READ,
|
||||
PRINT_WRITE,
|
||||
PRINT_OPEN,
|
||||
PRINT_CLOSE,
|
||||
};
|
||||
|
||||
## The file actions which are logged.
|
||||
const logged_file_actions: set[Action] = {
|
||||
FILE_OPEN,
|
||||
FILE_RENAME,
|
||||
FILE_DELETE,
|
||||
|
||||
PRINT_OPEN,
|
||||
PRINT_CLOSE,
|
||||
} &redef;
|
||||
|
||||
## This record is for the smb_files.log
|
||||
type FileInfo: record {
|
||||
## Time when the file was first discovered.
|
||||
ts : time &log;
|
||||
## Unique ID of the connection the file was sent over.
|
||||
uid : string &log;
|
||||
## ID of the connection the file was sent over.
|
||||
id : conn_id &log;
|
||||
## Unique ID of the file.
|
||||
fuid : string &log &optional;
|
||||
|
||||
## Action this log record represents.
|
||||
action : Action &log &optional;
|
||||
## Path pulled from the tree this file was transferred to or from.
|
||||
path : string &log &optional;
|
||||
## Filename if one was seen.
|
||||
name : string &log &optional;
|
||||
## Total size of the file.
|
||||
size : count &log &default=0;
|
||||
## If the rename action was seen, this will be
|
||||
## the file's previous name.
|
||||
prev_name : string &log &optional;
|
||||
## Last time this file was modified.
|
||||
times : SMB::MACTimes &log &optional;
|
||||
};
|
||||
|
||||
## This record is for the smb_mapping.log
|
||||
type TreeInfo: record {
|
||||
## Time when the tree was mapped.
|
||||
ts : time &log &optional;
|
||||
## Unique ID of the connection the tree was mapped over.
|
||||
uid : string &log;
|
||||
## ID of the connection the tree was mapped over.
|
||||
id : conn_id &log;
|
||||
|
||||
## Name of the tree path.
|
||||
path : string &log &optional;
|
||||
## The type of resource of the tree (disk share, printer share, named pipe, etc.).
|
||||
service : string &log &optional;
|
||||
## File system of the tree.
|
||||
native_file_system : string &log &optional;
|
||||
## If this is SMB2, a share type will be included. For SMB1,
|
||||
## the type of share will be deduced and included as well.
|
||||
share_type : string &log &default="DISK";
|
||||
};
|
||||
|
||||
## This record is for the smb_cmd.log
|
||||
type CmdInfo: record {
|
||||
## Timestamp of the command request.
|
||||
ts : time &log;
|
||||
## Unique ID of the connection the request was sent over.
|
||||
uid : string &log;
|
||||
## ID of the connection the request was sent over.
|
||||
id : conn_id &log;
|
||||
|
||||
## The command sent by the client.
|
||||
command : string &log;
|
||||
## The subcommand sent by the client, if present.
|
||||
sub_command : string &log &optional;
|
||||
## Command argument sent by the client, if any.
|
||||
argument : string &log &optional;
|
||||
|
||||
## Server reply to the client's command.
|
||||
status : string &log &optional;
|
||||
## Round trip time from the request to the response.
|
||||
rtt : interval &log &optional;
|
||||
## Version of SMB for the command.
|
||||
version : string &log;
|
||||
|
||||
## Authenticated username, if available.
|
||||
username : string &log &optional;
|
||||
|
||||
## If this is related to a tree, this is the tree
|
||||
## that was used for the current command.
|
||||
tree : string &log &optional;
|
||||
## The type of tree (disk share, printer share, named pipe, etc.).
|
||||
tree_service : string &log &optional;
|
||||
|
||||
## If the command referenced a file, store it here.
|
||||
referenced_file : FileInfo &log &optional;
|
||||
## If the command referenced a tree, store it here.
|
||||
referenced_tree : TreeInfo &optional;
|
||||
};
|
||||
|
||||
## This record stores the SMB state of in-flight commands,
|
||||
## the file and tree map of the connection.
|
||||
type State: record {
|
||||
## A reference to the current command.
|
||||
current_cmd : CmdInfo &optional;
|
||||
## A reference to the current file.
|
||||
current_file : FileInfo &optional;
|
||||
## A reference to the current tree.
|
||||
current_tree : TreeInfo &optional;
|
||||
|
||||
## Indexed on MID to map responses to requests.
|
||||
pending_cmds : table[count] of CmdInfo &optional;
|
||||
## File map to retrieve file information based on the file ID.
|
||||
fid_map : table[count] of FileInfo &optional;
|
||||
## Tree map to retrieve tree information based on the tree ID.
|
||||
tid_map : table[count] of TreeInfo &optional;
|
||||
## User map to retrieve user name based on the user ID.
|
||||
uid_map : table[count] of string &optional;
|
||||
## Pipe map to retrieve UUID based on the file ID of a pipe.
|
||||
pipe_map : table[count] of string &optional;
|
||||
|
||||
## A set of recent files to avoid logging the same
|
||||
## files over and over in the smb files log.
|
||||
## This only applies to files seen in a single connection.
|
||||
recent_files : set[string] &default=string_set() &read_expire=3min;
|
||||
};
|
||||
|
||||
## Everything below here is used internally in the SMB scripts.
|
||||
|
||||
redef record connection += {
|
||||
smb_state : State &optional;
|
||||
};
|
||||
|
||||
## This is an internally used function.
|
||||
const set_current_file: function(smb_state: State, file_id: count) &redef;
|
||||
|
||||
## This is an internally used function.
|
||||
const write_file_log: function(state: State) &redef;
|
||||
}
|
||||
|
||||
redef record FileInfo += {
|
||||
## ID referencing this file.
|
||||
fid : count &optional;
|
||||
|
||||
## UUID referencing this file if DCE/RPC.
|
||||
uuid : string &optional;
|
||||
};
|
||||
|
||||
const ports = { 139/tcp, 445/tcp };
|
||||
redef likely_server_ports += { ports };
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(SMB::FILES_LOG, [$columns=SMB::FileInfo, $path="smb_files"]);
|
||||
Log::create_stream(SMB::MAPPING_LOG, [$columns=SMB::TreeInfo, $path="smb_mapping"]);
|
||||
|
||||
Analyzer::register_for_ports(Analyzer::ANALYZER_SMB, ports);
|
||||
}
|
||||
|
||||
function set_current_file(smb_state: State, file_id: count)
|
||||
{
|
||||
if ( file_id !in smb_state$fid_map )
|
||||
{
|
||||
smb_state$fid_map[file_id] = smb_state$current_cmd$referenced_file;
|
||||
smb_state$fid_map[file_id]$fid = file_id;
|
||||
}
|
||||
|
||||
smb_state$current_cmd$referenced_file = smb_state$fid_map[file_id];
|
||||
smb_state$current_file = smb_state$current_cmd$referenced_file;
|
||||
}
|
||||
|
||||
function write_file_log(state: State)
|
||||
{
|
||||
local f = state$current_file;
|
||||
if ( f?$name &&
|
||||
f$action in logged_file_actions )
|
||||
{
|
||||
# Everything in this if statement is to avoid overlogging
|
||||
# of the same data from a single connection based on recently
|
||||
# seen files in the SMB::State $recent_files field.
|
||||
if ( f?$times )
|
||||
{
|
||||
local file_ident = cat(f$action,
|
||||
f?$fuid ? f$fuid : "",
|
||||
f?$name ? f$name : "",
|
||||
f?$path ? f$path : "",
|
||||
f$size,
|
||||
f$times);
|
||||
if ( file_ident in state$recent_files )
|
||||
{
|
||||
# We've already seen this file and don't want to log it again.
|
||||
return;
|
||||
}
|
||||
else
|
||||
add state$recent_files[file_ident];
|
||||
}
|
||||
|
||||
Log::write(FILES_LOG, f);
|
||||
}
|
||||
}
|
||||
|
||||
event smb_pipe_connect_heuristic(c: connection) &priority=5
|
||||
{
|
||||
c$smb_state$current_tree$path = "<unknown>";
|
||||
c$smb_state$current_tree$share_type = "PIPE";
|
||||
}
|
||||
|
||||
event file_state_remove(f: fa_file) &priority=-5
|
||||
{
|
||||
if ( f$source != "SMB" )
|
||||
return;
|
||||
|
||||
for ( id in f$conns )
|
||||
{
|
||||
local c = f$conns[id];
|
||||
if ( c?$smb_state && c$smb_state?$current_file)
|
||||
{
|
||||
write_file_log(c$smb_state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
320
scripts/base/protocols/smb/smb1-main.bro
Normal file
320
scripts/base/protocols/smb/smb1-main.bro
Normal file
|
@ -0,0 +1,320 @@
|
|||
@load ./main
|
||||
|
||||
module SMB1;
|
||||
|
||||
redef record SMB::CmdInfo += {
|
||||
## Dialects offered by the client.
|
||||
smb1_offered_dialects: string_vec &optional;
|
||||
};
|
||||
|
||||
event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=5
|
||||
{
|
||||
if ( ! c?$smb_state )
|
||||
{
|
||||
local state: SMB::State;
|
||||
state$fid_map = table();
|
||||
state$tid_map = table();
|
||||
state$uid_map = table();
|
||||
state$pipe_map = table();
|
||||
state$pending_cmds = table();
|
||||
c$smb_state = state;
|
||||
}
|
||||
|
||||
local smb_state = c$smb_state;
|
||||
local tid = hdr$tid;
|
||||
local uid = hdr$uid;
|
||||
local pid = hdr$pid;
|
||||
local mid = hdr$mid;
|
||||
|
||||
if ( uid in smb_state$uid_map )
|
||||
{
|
||||
smb_state$current_cmd$username = smb_state$uid_map[uid];
|
||||
}
|
||||
|
||||
if ( tid !in smb_state$tid_map )
|
||||
{
|
||||
smb_state$tid_map[tid] = SMB::TreeInfo($uid=c$uid, $id=c$id);
|
||||
}
|
||||
smb_state$current_tree = smb_state$tid_map[tid];
|
||||
if ( smb_state$current_tree?$path )
|
||||
{
|
||||
smb_state$current_cmd$tree = smb_state$current_tree$path;
|
||||
}
|
||||
|
||||
if ( smb_state$current_tree?$service )
|
||||
{
|
||||
smb_state$current_cmd$tree_service = smb_state$current_tree$service;
|
||||
}
|
||||
|
||||
if ( mid !in smb_state$pending_cmds )
|
||||
{
|
||||
local tmp_cmd = SMB::CmdInfo($ts=network_time(), $uid=c$uid, $id=c$id, $version="SMB1", $command = SMB1::commands[hdr$command]);
|
||||
|
||||
local tmp_file = SMB::FileInfo($ts=network_time(), $uid=c$uid, $id=c$id);
|
||||
tmp_cmd$referenced_file = tmp_file;
|
||||
tmp_cmd$referenced_tree = smb_state$current_tree;
|
||||
|
||||
smb_state$pending_cmds[mid] = tmp_cmd;
|
||||
}
|
||||
|
||||
smb_state$current_cmd = smb_state$pending_cmds[mid];
|
||||
|
||||
if ( !is_orig )
|
||||
{
|
||||
smb_state$current_cmd$rtt = network_time() - smb_state$current_cmd$ts;
|
||||
smb_state$current_cmd$status = SMB::statuses[hdr$status]$id;
|
||||
}
|
||||
}
|
||||
|
||||
event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=-5
|
||||
{
|
||||
if ( is_orig )
|
||||
return;
|
||||
|
||||
delete c$smb_state$pending_cmds[hdr$mid];
|
||||
}
|
||||
|
||||
|
||||
event smb1_transaction2_request(c: connection, hdr: SMB1::Header, args: SMB1::Trans2_Args, sub_cmd: count)
|
||||
{
|
||||
c$smb_state$current_cmd$sub_command = SMB1::trans2_sub_commands[sub_cmd];
|
||||
}
|
||||
|
||||
|
||||
event smb1_negotiate_request(c: connection, hdr: SMB1::Header, dialects: string_vec) &priority=5
|
||||
{
|
||||
c$smb_state$current_cmd$smb1_offered_dialects = dialects;
|
||||
}
|
||||
|
||||
event smb1_negotiate_response(c: connection, hdr: SMB1::Header, response: SMB1::NegotiateResponse) &priority=5
|
||||
{
|
||||
if ( c$smb_state$current_cmd?$smb1_offered_dialects )
|
||||
{
|
||||
if ( response?$ntlm )
|
||||
{
|
||||
c$smb_state$current_cmd$argument = c$smb_state$current_cmd$smb1_offered_dialects[response$ntlm$dialect_index];
|
||||
}
|
||||
|
||||
delete c$smb_state$current_cmd$smb1_offered_dialects;
|
||||
}
|
||||
}
|
||||
|
||||
event smb1_negotiate_response(c: connection, hdr: SMB1::Header, response: SMB1::NegotiateResponse) &priority=-5
|
||||
{
|
||||
}
|
||||
|
||||
event smb1_tree_connect_andx_request(c: connection, hdr: SMB1::Header, path: string, service: string) &priority=5
|
||||
{
|
||||
local tmp_tree = SMB::TreeInfo($ts=network_time(), $uid=c$uid, $id=c$id, $path=path, $service=service);
|
||||
|
||||
c$smb_state$current_cmd$referenced_tree = tmp_tree;
|
||||
c$smb_state$current_cmd$argument = path;
|
||||
}
|
||||
|
||||
event smb1_tree_connect_andx_response(c: connection, hdr: SMB1::Header, service: string, native_file_system: string) &priority=5
|
||||
{
|
||||
c$smb_state$current_cmd$referenced_tree$service = service;
|
||||
if ( service == "IPC" )
|
||||
c$smb_state$current_cmd$referenced_tree$share_type = "PIPE";
|
||||
|
||||
c$smb_state$current_cmd$tree_service = service;
|
||||
|
||||
if ( native_file_system != "" )
|
||||
c$smb_state$current_cmd$referenced_tree$native_file_system = native_file_system;
|
||||
|
||||
c$smb_state$current_tree = c$smb_state$current_cmd$referenced_tree;
|
||||
c$smb_state$tid_map[hdr$tid] = c$smb_state$current_tree;
|
||||
}
|
||||
|
||||
event smb1_tree_connect_andx_response(c: connection, hdr: SMB1::Header, service: string, native_file_system: string) &priority=-5
|
||||
{
|
||||
Log::write(SMB::MAPPING_LOG, c$smb_state$current_tree);
|
||||
}
|
||||
|
||||
event smb1_nt_create_andx_request(c: connection, hdr: SMB1::Header, name: string) &priority=5
|
||||
{
|
||||
local tmp_file = SMB::FileInfo($ts=network_time(), $uid=c$uid, $id=c$id);
|
||||
c$smb_state$current_cmd$referenced_file = tmp_file;
|
||||
|
||||
c$smb_state$current_cmd$referenced_file$name = name;
|
||||
c$smb_state$current_cmd$referenced_file$action = SMB::FILE_OPEN;
|
||||
c$smb_state$current_file = c$smb_state$current_cmd$referenced_file;
|
||||
c$smb_state$current_cmd$argument = name;
|
||||
}
|
||||
|
||||
event smb1_nt_create_andx_response(c: connection, hdr: SMB1::Header, file_id: count, file_size: count, times: SMB::MACTimes) &priority=5
|
||||
{
|
||||
c$smb_state$current_cmd$referenced_file$action = SMB::FILE_OPEN;
|
||||
c$smb_state$current_cmd$referenced_file$fid = file_id;
|
||||
c$smb_state$current_cmd$referenced_file$size = file_size;
|
||||
|
||||
# I'm seeing negative data from IPC tree transfers
|
||||
if ( time_to_double(times$modified) > 0.0 )
|
||||
c$smb_state$current_cmd$referenced_file$times = times;
|
||||
|
||||
# We can identify the file by its file id now so let's stick it
|
||||
# in the file map.
|
||||
c$smb_state$fid_map[file_id] = c$smb_state$current_cmd$referenced_file;
|
||||
|
||||
c$smb_state$current_file = c$smb_state$fid_map[file_id];
|
||||
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
|
||||
event smb1_read_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, length: count) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, file_id);
|
||||
c$smb_state$current_file$action = SMB::FILE_READ;
|
||||
if ( c$smb_state$current_file?$name )
|
||||
c$smb_state$current_cmd$argument = c$smb_state$current_file$name;
|
||||
}
|
||||
|
||||
event smb1_read_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, length: count) &priority=-5
|
||||
{
|
||||
if ( c$smb_state$current_tree?$path && !c$smb_state$current_file?$path )
|
||||
c$smb_state$current_file$path = c$smb_state$current_tree$path;
|
||||
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
|
||||
event smb1_write_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, data_len: count) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, file_id);
|
||||
c$smb_state$current_file$action = SMB::FILE_WRITE;
|
||||
if ( !c$smb_state$current_cmd?$argument &&
|
||||
# TODO: figure out why name isn't getting set sometimes.
|
||||
c$smb_state$current_file?$name )
|
||||
c$smb_state$current_cmd$argument = c$smb_state$current_file$name;
|
||||
}
|
||||
|
||||
event smb1_write_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, data_len: count) &priority=-5
|
||||
{
|
||||
if ( c$smb_state$current_tree?$path && !c$smb_state$current_file?$path )
|
||||
c$smb_state$current_file$path = c$smb_state$current_tree$path;
|
||||
|
||||
# We don't even try to log reads and writes to the files log.
|
||||
#write_file_log(c$smb_state);
|
||||
}
|
||||
|
||||
#event smb1_write_andx_response(c: connection, hdr: SMB1::Header, written_bytes: count) &priority=5
|
||||
# {
|
||||
# # TODO - determine what to do here
|
||||
# }
|
||||
|
||||
event smb1_close_request(c: connection, hdr: SMB1::Header, file_id: count) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, file_id);
|
||||
c$smb_state$current_file$action = SMB::FILE_CLOSE;
|
||||
}
|
||||
|
||||
event smb1_close_request(c: connection, hdr: SMB1::Header, file_id: count) &priority=-5
|
||||
{
|
||||
if ( file_id in c$smb_state$fid_map )
|
||||
{
|
||||
local fl = c$smb_state$fid_map[file_id];
|
||||
# Need to check for existence of path in case tree connect message wasn't seen.
|
||||
if ( c$smb_state$current_tree?$path )
|
||||
fl$path = c$smb_state$current_tree$path;
|
||||
|
||||
if ( fl?$name )
|
||||
c$smb_state$current_cmd$argument = fl$name;
|
||||
|
||||
delete c$smb_state$fid_map[file_id];
|
||||
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
else
|
||||
{
|
||||
# TODO - Determine correct action
|
||||
# A reporter message is not right...
|
||||
#Reporter::warning("attempting to close an unknown file!");
|
||||
}
|
||||
}
|
||||
|
||||
event smb1_trans2_get_dfs_referral_request(c: connection, hdr: SMB1::Header, file_name: string)
|
||||
{
|
||||
c$smb_state$current_cmd$argument = file_name;
|
||||
}
|
||||
|
||||
event smb1_trans2_query_path_info_request(c: connection, hdr: SMB1::Header, file_name: string)
|
||||
{
|
||||
c$smb_state$current_cmd$argument = file_name;
|
||||
}
|
||||
|
||||
event smb1_trans2_find_first2_request(c: connection, hdr: SMB1::Header, args: SMB1::Find_First2_Request_Args)
|
||||
{
|
||||
c$smb_state$current_cmd$argument = args$file_name;
|
||||
}
|
||||
|
||||
event smb1_session_setup_andx_request(c: connection, hdr: SMB1::Header, request: SMB1::SessionSetupAndXRequest) &priority=5
|
||||
{
|
||||
# No behavior yet.
|
||||
}
|
||||
|
||||
event smb1_session_setup_andx_response(c: connection, hdr: SMB1::Header, response: SMB1::SessionSetupAndXResponse) &priority=-5
|
||||
{
|
||||
# No behavior yet.
|
||||
}
|
||||
|
||||
event smb1_transaction_request(c: connection, hdr: SMB1::Header, name: string, sub_cmd: count, parameters: string, data: string)
|
||||
{
|
||||
c$smb_state$current_cmd$sub_command = SMB1::trans_sub_commands[sub_cmd];
|
||||
}
|
||||
|
||||
event smb1_write_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, data_len: count)
|
||||
{
|
||||
if ( ! c$smb_state?$current_file || ! c$smb_state$current_file?$uuid )
|
||||
{
|
||||
# TODO: figure out why the uuid isn't getting set sometimes.
|
||||
return;
|
||||
}
|
||||
|
||||
c$smb_state$pipe_map[file_id] = c$smb_state$current_file$uuid;
|
||||
}
|
||||
|
||||
event smb_pipe_bind_ack_response(c: connection, hdr: SMB1::Header)
|
||||
{
|
||||
if ( ! c$smb_state?$current_file || ! c$smb_state$current_file?$uuid )
|
||||
{
|
||||
# TODO: figure out why the uuid isn't getting set sometimes.
|
||||
return;
|
||||
}
|
||||
|
||||
c$smb_state$current_cmd$sub_command = "RPC_BIND_ACK";
|
||||
c$smb_state$current_cmd$argument = SMB::rpc_uuids[c$smb_state$current_file$uuid];
|
||||
}
|
||||
|
||||
event smb_pipe_bind_request(c: connection, hdr: SMB1::Header, uuid: string, version: string)
|
||||
{
|
||||
if ( ! c$smb_state?$current_file || ! c$smb_state$current_file?$uuid )
|
||||
{
|
||||
# TODO: figure out why the current_file isn't getting set sometimes.
|
||||
return;
|
||||
}
|
||||
|
||||
c$smb_state$current_cmd$sub_command = "RPC_BIND";
|
||||
c$smb_state$current_file$uuid = uuid;
|
||||
c$smb_state$current_cmd$argument = fmt("%s v%s", SMB::rpc_uuids[uuid], version);
|
||||
}
|
||||
|
||||
event smb_pipe_request(c: connection, hdr: SMB1::Header, op_num: count)
|
||||
{
|
||||
if ( ! c$smb_state?$current_file )
|
||||
{
|
||||
# TODO: figure out why the current file isn't being set sometimes.
|
||||
return;
|
||||
}
|
||||
|
||||
local f = c$smb_state$current_file;
|
||||
if ( ! f?$uuid )
|
||||
{
|
||||
# TODO: figure out why this is happening.
|
||||
event conn_weird("smb_pipe_request_missing_uuid", c, "");
|
||||
return;
|
||||
}
|
||||
local arg = fmt("%s: %s",
|
||||
SMB::rpc_uuids[f$uuid],
|
||||
SMB::rpc_sub_cmds[f$uuid][op_num]);
|
||||
|
||||
c$smb_state$current_cmd$argument = arg;
|
||||
}
|
344
scripts/base/protocols/smb/smb2-main.bro
Normal file
344
scripts/base/protocols/smb/smb2-main.bro
Normal file
|
@ -0,0 +1,344 @@
|
|||
@load ./main
|
||||
|
||||
module SMB2;
|
||||
|
||||
redef record SMB::CmdInfo += {
|
||||
## Dialects offered by the client.
|
||||
smb2_offered_dialects: index_vec &optional;
|
||||
};
|
||||
|
||||
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=5
|
||||
{
|
||||
if ( ! c?$smb_state )
|
||||
{
|
||||
local state: SMB::State;
|
||||
state$fid_map = table();
|
||||
state$tid_map = table();
|
||||
state$uid_map = table();
|
||||
state$pending_cmds = table();
|
||||
state$pipe_map = table();
|
||||
c$smb_state = state;
|
||||
}
|
||||
|
||||
local smb_state = c$smb_state;
|
||||
local tid = hdr$tree_id;
|
||||
local pid = hdr$process_id;
|
||||
local mid = hdr$message_id;
|
||||
local sid = hdr$session_id;
|
||||
|
||||
if ( mid !in smb_state$pending_cmds )
|
||||
{
|
||||
local tmp_file = SMB::FileInfo($ts=network_time(), $uid=c$uid, $id=c$id);
|
||||
local tmp_cmd = SMB::CmdInfo($ts=network_time(), $uid=c$uid, $id=c$id, $version="SMB2", $command = SMB2::commands[hdr$command]);
|
||||
tmp_cmd$referenced_file = tmp_file;
|
||||
smb_state$pending_cmds[mid] = tmp_cmd;
|
||||
}
|
||||
smb_state$current_cmd = smb_state$pending_cmds[mid];
|
||||
|
||||
if ( tid > 0 )
|
||||
{
|
||||
if ( smb_state$current_cmd?$referenced_tree )
|
||||
{
|
||||
smb_state$tid_map[tid] = smb_state$current_cmd$referenced_tree;
|
||||
}
|
||||
else if ( tid !in smb_state$tid_map )
|
||||
{
|
||||
local tmp_tree = SMB::TreeInfo($ts=network_time(), $uid=c$uid, $id=c$id);
|
||||
smb_state$tid_map[tid] = tmp_tree;
|
||||
}
|
||||
smb_state$current_cmd$referenced_tree = smb_state$tid_map[tid];
|
||||
}
|
||||
else
|
||||
{
|
||||
smb_state$current_cmd$referenced_tree = SMB::TreeInfo($ts=network_time(), $uid=c$uid, $id=c$id);
|
||||
}
|
||||
|
||||
smb_state$current_file = smb_state$current_cmd$referenced_file;
|
||||
smb_state$current_tree = smb_state$current_cmd$referenced_tree;
|
||||
|
||||
if ( !is_orig )
|
||||
{
|
||||
smb_state$current_cmd$rtt = network_time() - smb_state$current_cmd$ts;
|
||||
smb_state$current_cmd$status = SMB::statuses[hdr$status]$id;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=-5
|
||||
{
|
||||
if ( is_orig )
|
||||
return;
|
||||
|
||||
# If the command that is being looked at right now was
|
||||
# marked as PENDING, then we'll skip all of this and wait
|
||||
# for a reply that isn't marked pending.
|
||||
if ( c$smb_state$current_cmd$status == "PENDING" )
|
||||
return;
|
||||
|
||||
delete c$smb_state$pending_cmds[hdr$message_id];
|
||||
}
|
||||
|
||||
event smb2_negotiate_request(c: connection, hdr: SMB2::Header, dialects: index_vec) &priority=5
|
||||
{
|
||||
c$smb_state$current_cmd$smb2_offered_dialects = dialects;
|
||||
}
|
||||
|
||||
event smb2_negotiate_response(c: connection, hdr: SMB2::Header, response: SMB2::NegotiateResponse) &priority=5
|
||||
{
|
||||
if ( c$smb_state$current_cmd?$smb2_offered_dialects )
|
||||
{
|
||||
for ( i in c$smb_state$current_cmd$smb2_offered_dialects )
|
||||
{
|
||||
if ( response$dialect_revision == c$smb_state$current_cmd$smb2_offered_dialects[i] )
|
||||
{
|
||||
c$smb_state$current_cmd$argument = SMB2::dialects[response$dialect_revision];
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete c$smb_state$current_cmd$smb2_offered_dialects;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_negotiate_response(c: connection, hdr: SMB2::Header, response: SMB2::NegotiateResponse) &priority=5
|
||||
{
|
||||
# No behavior yet.
|
||||
}
|
||||
|
||||
event smb2_tree_connect_request(c: connection, hdr: SMB2::Header, path: string) &priority=5
|
||||
{
|
||||
c$smb_state$current_tree$path = path;
|
||||
}
|
||||
|
||||
event smb2_tree_connect_response(c: connection, hdr: SMB2::Header, response: SMB2::TreeConnectResponse) &priority=5
|
||||
{
|
||||
c$smb_state$current_tree$share_type = SMB2::share_types[response$share_type];
|
||||
}
|
||||
|
||||
event smb2_tree_connect_response(c: connection, hdr: SMB2::Header, response: SMB2::TreeConnectResponse) &priority=-5
|
||||
{
|
||||
Log::write(SMB::MAPPING_LOG, c$smb_state$current_tree);
|
||||
}
|
||||
|
||||
event smb2_tree_disconnect_request(c: connection, hdr: SMB2::Header) &priority=5
|
||||
{
|
||||
if ( hdr$tree_id in c$smb_state$tid_map )
|
||||
{
|
||||
delete c$smb_state$tid_map[hdr$tree_id];
|
||||
delete c$smb_state$current_tree;
|
||||
delete c$smb_state$current_cmd$referenced_tree;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_create_request(c: connection, hdr: SMB2::Header, request: SMB2::CreateRequest) &priority=5
|
||||
{
|
||||
if ( request$filename == "")
|
||||
request$filename = "<share_root>";
|
||||
|
||||
c$smb_state$current_file$name = request$filename;
|
||||
|
||||
switch ( c$smb_state$current_tree$share_type )
|
||||
{
|
||||
case "DISK":
|
||||
c$smb_state$current_file$action = SMB::FILE_OPEN;
|
||||
break;
|
||||
case "PIPE":
|
||||
c$smb_state$current_file$action = SMB::PIPE_OPEN;
|
||||
break;
|
||||
case "PRINT":
|
||||
c$smb_state$current_file$action = SMB::PRINT_OPEN;
|
||||
break;
|
||||
default:
|
||||
c$smb_state$current_file$action = SMB::FILE_OPEN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_create_response(c: connection, hdr: SMB2::Header, response: SMB2::CreateResponse) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, response$file_id$persistent+response$file_id$volatile);
|
||||
|
||||
c$smb_state$current_file$fid = response$file_id$persistent+response$file_id$volatile;
|
||||
c$smb_state$current_file$size = response$size;
|
||||
|
||||
if ( c$smb_state$current_tree?$path )
|
||||
c$smb_state$current_file$path = c$smb_state$current_tree$path;
|
||||
|
||||
# I'm seeing negative data from IPC tree transfers
|
||||
if ( time_to_double(response$times$modified) > 0.0 )
|
||||
c$smb_state$current_file$times = response$times;
|
||||
|
||||
# We can identify the file by its file id now so let's stick it
|
||||
# in the file map.
|
||||
c$smb_state$fid_map[response$file_id$persistent+response$file_id$volatile] = c$smb_state$current_file;
|
||||
|
||||
c$smb_state$current_file = c$smb_state$fid_map[response$file_id$persistent+response$file_id$volatile];
|
||||
}
|
||||
|
||||
event smb2_create_response(c: connection, hdr: SMB2::Header, response: SMB2::CreateResponse) &priority=-5
|
||||
{
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
|
||||
event smb2_read_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
|
||||
|
||||
switch ( c$smb_state$current_tree$share_type )
|
||||
{
|
||||
case "DISK":
|
||||
c$smb_state$current_file$action = SMB::FILE_READ;
|
||||
break;
|
||||
case "PIPE":
|
||||
c$smb_state$current_file$action = SMB::PIPE_READ;
|
||||
break;
|
||||
case "PRINT":
|
||||
c$smb_state$current_file$action = SMB::PRINT_READ;
|
||||
break;
|
||||
default:
|
||||
c$smb_state$current_file$action = SMB::FILE_READ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_read_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=-5
|
||||
{
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
|
||||
event smb2_write_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
|
||||
|
||||
switch ( c$smb_state$current_tree$share_type )
|
||||
{
|
||||
case "DISK":
|
||||
c$smb_state$current_file$action = SMB::FILE_WRITE;
|
||||
break;
|
||||
case "PIPE":
|
||||
c$smb_state$current_file$action = SMB::PIPE_WRITE;
|
||||
break;
|
||||
case "PRINT":
|
||||
c$smb_state$current_file$action = SMB::PRINT_WRITE;
|
||||
break;
|
||||
default:
|
||||
c$smb_state$current_file$action = SMB::FILE_WRITE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_write_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=-5
|
||||
{
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
|
||||
event smb2_file_sattr(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, times: SMB::MACTimes, attrs: SMB2::FileAttrs) &priority=-5
|
||||
{
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
|
||||
event smb2_file_sattr(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, times: SMB::MACTimes, attrs: SMB2::FileAttrs) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
|
||||
|
||||
switch ( c$smb_state$current_tree$share_type )
|
||||
{
|
||||
case "DISK":
|
||||
c$smb_state$current_file$action = SMB::FILE_SET_ATTRIBUTE;
|
||||
break;
|
||||
default:
|
||||
c$smb_state$current_file$action = SMB::FILE_SET_ATTRIBUTE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_file_rename(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, dst_filename: string) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
|
||||
|
||||
if ( c$smb_state$current_file?$name )
|
||||
c$smb_state$current_file$prev_name = c$smb_state$current_file$name;
|
||||
|
||||
c$smb_state$current_file$name = dst_filename;
|
||||
|
||||
switch ( c$smb_state$current_tree$share_type )
|
||||
{
|
||||
case "DISK":
|
||||
c$smb_state$current_file$action = SMB::FILE_RENAME;
|
||||
break;
|
||||
default:
|
||||
c$smb_state$current_file$action = SMB::FILE_RENAME;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_file_rename(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, dst_filename: string) &priority=-5
|
||||
{
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
|
||||
event smb2_file_delete(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, delete_pending: bool) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
|
||||
|
||||
if ( ! delete_pending )
|
||||
{
|
||||
# This is weird beause it would mean that someone didn't
|
||||
# set the delete bit in a delete request.
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( c$smb_state$current_tree$share_type )
|
||||
{
|
||||
case "DISK":
|
||||
c$smb_state$current_file$action = SMB::FILE_DELETE;
|
||||
break;
|
||||
default:
|
||||
c$smb_state$current_file$action = SMB::FILE_DELETE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_file_delete(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, delete_pending: bool) &priority=-5
|
||||
{
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
|
||||
event smb2_close_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID) &priority=5
|
||||
{
|
||||
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
|
||||
|
||||
switch ( c$smb_state$current_tree$share_type )
|
||||
{
|
||||
case "DISK":
|
||||
c$smb_state$current_file$action = SMB::FILE_CLOSE;
|
||||
break;
|
||||
case "PIPE":
|
||||
c$smb_state$current_file$action = SMB::PIPE_CLOSE;
|
||||
break;
|
||||
case "PRINT":
|
||||
c$smb_state$current_file$action = SMB::PRINT_CLOSE;
|
||||
break;
|
||||
default:
|
||||
c$smb_state$current_file$action = SMB::FILE_CLOSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event smb2_close_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID) &priority=-5
|
||||
{
|
||||
if ( file_id$persistent+file_id$volatile in c$smb_state$fid_map )
|
||||
{
|
||||
local fl = c$smb_state$fid_map[file_id$persistent+file_id$volatile];
|
||||
# Need to check for existence of path in case tree connect message wasn't seen.
|
||||
if ( c$smb_state$current_tree?$path )
|
||||
fl$path = c$smb_state$current_tree$path;
|
||||
delete c$smb_state$fid_map[file_id$persistent+file_id$volatile];
|
||||
|
||||
SMB::write_file_log(c$smb_state);
|
||||
}
|
||||
else
|
||||
{
|
||||
# TODO - Determine correct action
|
||||
# A reporter message is not right...
|
||||
#Reporter::warning("attempting to close an unknown file!");
|
||||
}
|
||||
}
|
|
@ -49,5 +49,5 @@ event bro_init() &priority=5
|
|||
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5
|
||||
{
|
||||
if ( c?$smtp && !c$smtp$tls )
|
||||
c$smtp$fuids[|c$smtp$fuids|] = f$id;
|
||||
c$smtp$fuids += f$id;
|
||||
}
|
||||
|
|
|
@ -295,7 +295,7 @@ event mime_one_header(c: connection, h: mime_header_rec) &priority=3
|
|||
c$smtp$process_received_from = F;
|
||||
}
|
||||
if ( c$smtp$path[|c$smtp$path|-1] != ip )
|
||||
c$smtp$path[|c$smtp$path|] = ip;
|
||||
c$smtp$path += ip;
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
|
|
|
@ -6,32 +6,37 @@ module SOCKS;
|
|||
export {
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
## Whether passwords are captured or not.
|
||||
const default_capture_password = F &redef;
|
||||
|
||||
## The record type which contains the fields of the SOCKS log.
|
||||
type Info: record {
|
||||
## Time when the proxy connection was first detected.
|
||||
ts: time &log;
|
||||
ts: time &log;
|
||||
## Unique ID for the tunnel - may correspond to connection uid
|
||||
## or be non-existent.
|
||||
uid: string &log;
|
||||
uid: string &log;
|
||||
## The connection's 4-tuple of endpoint addresses/ports.
|
||||
id: conn_id &log;
|
||||
id: conn_id &log;
|
||||
## Protocol version of SOCKS.
|
||||
version: count &log;
|
||||
version: count &log;
|
||||
## Username used to request a login to the proxy.
|
||||
user: string &log &optional;
|
||||
user: string &log &optional;
|
||||
## Password used to request a login to the proxy.
|
||||
password: string &log &optional;
|
||||
password: string &log &optional;
|
||||
## Server status for the attempt at using the proxy.
|
||||
status: string &log &optional;
|
||||
status: string &log &optional;
|
||||
## Client requested SOCKS address. Could be an address, a name
|
||||
## or both.
|
||||
request: SOCKS::Address &log &optional;
|
||||
request: SOCKS::Address &log &optional;
|
||||
## Client requested port.
|
||||
request_p: port &log &optional;
|
||||
request_p: port &log &optional;
|
||||
## Server bound address. Could be an address, a name or both.
|
||||
bound: SOCKS::Address &log &optional;
|
||||
bound: SOCKS::Address &log &optional;
|
||||
## Server bound port.
|
||||
bound_p: port &log &optional;
|
||||
bound_p: port &log &optional;
|
||||
## Determines if the password will be captured for this request.
|
||||
capture_password: bool &default=default_capture_password;
|
||||
};
|
||||
|
||||
## Event that can be handled to access the SOCKS
|
||||
|
@ -90,10 +95,12 @@ event socks_reply(c: connection, version: count, reply: count, sa: SOCKS::Addres
|
|||
event socks_login_userpass_request(c: connection, user: string, password: string) &priority=5
|
||||
{
|
||||
# Authentication only possible with the version 5.
|
||||
set_session(c, 5);
|
||||
set_session(c, 5);
|
||||
|
||||
c$socks$user = user;
|
||||
c$socks$password = password;
|
||||
|
||||
if ( c$socks$capture_password )
|
||||
c$socks$password = password;
|
||||
}
|
||||
|
||||
event socks_login_userpass_reply(c: connection, code: count) &priority=5
|
||||
|
|
|
@ -24,7 +24,7 @@ export {
|
|||
## at least one, since some servers might support no authentication at all.
|
||||
## It's important to note that not all of these are failures, since
|
||||
## some servers require two-factor auth (e.g. password AND pubkey)
|
||||
auth_attempts: count &log &optional;
|
||||
auth_attempts: count &log &default=0;
|
||||
## 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.
|
||||
|
@ -185,13 +185,7 @@ event ssh_auth_attempted(c: connection, authenticated: bool) &priority=5
|
|||
return;
|
||||
|
||||
c$ssh$auth_success = authenticated;
|
||||
|
||||
if ( c$ssh?$auth_attempts )
|
||||
c$ssh$auth_attempts += 1;
|
||||
else
|
||||
{
|
||||
c$ssh$auth_attempts = 1;
|
||||
}
|
||||
c$ssh$auth_attempts += 1;
|
||||
|
||||
if ( authenticated && disable_analyzer_after_detection )
|
||||
disable_analyzer(c$id, c$ssh$analyzer_id);
|
||||
|
|
|
@ -1 +1 @@
|
|||
Support for Secure Sockets Layer (SSL) protocol analysis.
|
||||
Support for Secure Sockets Layer (SSL)/Transport Layer Security(TLS) protocol analysis.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@load ./consts
|
||||
@load ./main
|
||||
@load ./mozilla-ca-list
|
||||
@load ./ct-list
|
||||
@load ./files
|
||||
|
||||
@load-sigs ./dpd.sig
|
||||
|
|
|
@ -163,6 +163,10 @@ export {
|
|||
[42] = "early_data", # new for 1.3, state of draft-16
|
||||
[43] = "supported_versions", # new for 1.3, state of draft-16
|
||||
[44] = "cookie", # new for 1.3, state of draft-16
|
||||
[45] = "psk_key_exchange_modes", # new for 1.3, state of draft-18
|
||||
[46] = "TicketEarlyDataInfo", # new for 1.3, state of draft-16
|
||||
[47] = "certificate_authorities", # new for 1.3, state of draft-18
|
||||
[48] = "oid_filters", # new for 1.3, state of draft-18
|
||||
[13172] = "next_protocol_negotiation",
|
||||
[13175] = "origin_bound_certificates",
|
||||
[13180] = "encrypted_client_certificates",
|
||||
|
@ -403,6 +407,11 @@ export {
|
|||
const TLS_CHACHA20_POLY1305_SHA256 = 0x1303;
|
||||
const TLS_AES_128_CCM_SHA256 = 0x1304;
|
||||
const TLS_AES_128_CCM_8_SHA256 = 0x1305;
|
||||
# Google...
|
||||
const TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0x16b7;
|
||||
const TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0x16b8;
|
||||
const TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384 = 0x16b9;
|
||||
const TLS_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384 = 0x16ba;
|
||||
# draft-bmoeller-tls-downgrade-scsv-01
|
||||
const TLS_FALLBACK_SCSV = 0x5600;
|
||||
# RFC 4492
|
||||
|
@ -596,6 +605,11 @@ export {
|
|||
const TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAC;
|
||||
const TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAD;
|
||||
const TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAE;
|
||||
# draft-ietf-tls-ecdhe-psk-aead-05
|
||||
const TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 = 0xD001;
|
||||
const TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384 = 0xD002;
|
||||
const TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256 = 0xD003;
|
||||
const TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256 = 0xD004;
|
||||
|
||||
const SSL_RSA_FIPS_WITH_DES_CBC_SHA = 0xFEFE;
|
||||
const SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA = 0xFEFF;
|
||||
|
@ -788,6 +802,10 @@ export {
|
|||
[TLS_CHACHA20_POLY1305_SHA256] = "TLS_CHACHA20_POLY1305_SHA256",
|
||||
[TLS_AES_128_CCM_SHA256] = "TLS_AES_128_CCM_SHA256",
|
||||
[TLS_AES_128_CCM_8_SHA256] = "TLS_AES_128_CCM_8_SHA256",
|
||||
[TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256] = "TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
[TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256] = "TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
[TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384] = "TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384] = "TLS_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_FALLBACK_SCSV] = "TLS_FALLBACK_SCSV",
|
||||
[TLS_ECDH_ECDSA_WITH_NULL_SHA] = "TLS_ECDH_ECDSA_WITH_NULL_SHA",
|
||||
[TLS_ECDH_ECDSA_WITH_RC4_128_SHA] = "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
|
||||
|
@ -974,6 +992,10 @@ export {
|
|||
[TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256] = "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
|
||||
[TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256] = "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
|
||||
[TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256] = "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256",
|
||||
[TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256] = "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384] = "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256] = "TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256",
|
||||
[TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256] = "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256",
|
||||
[SSL_RSA_FIPS_WITH_DES_CBC_SHA] = "SSL_RSA_FIPS_WITH_DES_CBC_SHA",
|
||||
[SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA] = "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",
|
||||
[SSL_RSA_FIPS_WITH_DES_CBC_SHA_2] = "SSL_RSA_FIPS_WITH_DES_CBC_SHA_2",
|
||||
|
|
68
scripts/base/protocols/ssl/ct-list.bro
Normal file
68
scripts/base/protocols/ssl/ct-list.bro
Normal file
|
@ -0,0 +1,68 @@
|
|||
#
|
||||
# Do not edit this file. This file is automatically generated by gen-ct-list.pl
|
||||
# File generated at Fri Feb 16 10:41:42 2018
|
||||
# File generated from https://www.gstatic.com/ct/log_list/all_logs_list.json
|
||||
#
|
||||
|
||||
@load base/protocols/ssl
|
||||
module SSL;
|
||||
redef ct_logs += {
|
||||
["\xfa\xd4\xc9\x7c\xc4\x9e\xe2\xf8\xac\x85\xc5\xea\x5c\xea\x09\xd0\x22\x0d\xbb\xf4\xe4\x9c\x6b\x50\x66\x2f\xf8\x68\xf8\x6b\x8c\x28"] = CTInfo($description="Google 'Argon2017' log", $operator="Google", $url="ct.googleapis.com/logs/argon2017/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x54\x6d\x7c\x89\xdd\xea\x9d\xf0\xba\x5f\xf4\x6d\x60\x7a\x37\x4f\x02\x25\xbf\x1c\xf6\x6f\x85\xae\xaf\x15\xdf\x69\x6e\xed\xdb\xa9\x9a\x29\x97\xf2\x99\x76\x1e\xe6\x33\x46\x1e\x27\xf4\xbe\x70\xdd\x59\xd7\xba\xcf\xfe\xd0\x72\x8e\xb0\x57\x0f\x9d\x37\x89\x62\xa3"),
|
||||
["\xa4\x50\x12\x69\x05\x5a\x15\x54\x5e\x62\x11\xab\x37\xbc\x10\x3f\x62\xae\x55\x76\xa4\x5e\x4b\x17\x14\x45\x3e\x1b\x22\x10\x6a\x25"] = CTInfo($description="Google 'Argon2018' log", $operator="Google", $url="ct.googleapis.com/logs/argon2018/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd2\x00\x55\x05\xad\xd5\x47\xb4\x19\xbb\xcd\x95\xfb\x29\xd7\x58\x3d\x78\x24\xcd\xce\x46\x9d\xfb\x32\xd4\x71\x4e\x60\x02\x25\x5e\x59\x3e\xd7\xd4\x03\xb8\x6d\x43\x68\x68\x7e\xe8\xa0\x65\x0b\x3e\x6e\x71\x59\x92\x37\xbe\xa9\xe8\xf1\xa3\x2b\xe4\xd9\x0d\x55\x68"),
|
||||
["\x63\xf2\xdb\xcd\xe8\x3b\xcc\x2c\xcf\x0b\x72\x84\x27\x57\x6b\x33\xa4\x8d\x61\x77\x8f\xbd\x75\xa6\x38\xb1\xc7\x68\x54\x4b\xd8\x8d"] = CTInfo($description="Google 'Argon2019' log", $operator="Google", $url="ct.googleapis.com/logs/argon2019/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x23\x73\x10\x9b\xe1\xf3\x5e\xf6\x98\x6b\x69\x95\x96\x10\x78\xce\x49\xdb\xb4\x04\xfc\x71\x2c\x5a\x92\x60\x68\x25\xc0\x4a\x1a\xa1\xb0\x61\x2d\x1b\x87\x14\xa9\xba\xf0\x01\x33\x59\x1d\x05\x30\xe9\x42\x15\xe7\x55\xd7\x2a\xf8\xb4\xa2\xba\x45\xc9\x46\x91\x87\x56"),
|
||||
["\xb2\x1e\x05\xcc\x8b\xa2\xcd\x8a\x20\x4e\x87\x66\xf9\x2b\xb9\x8a\x25\x20\x67\x6b\xda\xfa\x70\xe7\xb2\x49\x53\x2d\xef\x8b\x90\x5e"] = CTInfo($description="Google 'Argon2020' log", $operator="Google", $url="ct.googleapis.com/logs/argon2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xe9\x3c\x76\xa7\x5c\x8a\x63\x8d\x35\xe4\xdc\x88\x62\xf7\x6b\x93\x7e\x9e\xb3\x4b\x80\x73\x5c\xc0\xe0\xf4\x3e\x4c\x64\x58\xfb\x76\x63\x51\x32\x18\x63\xd5\xb2\xbb\xed\xea\xff\x5e\x3b\x24\x6e\x2f\x35\x52\x8b\xb4\x35\x9a\xad\x9c\x15\xa8\x69\x20\xea\x50\x18\xcc"),
|
||||
["\xf6\x5c\x94\x2f\xd1\x77\x30\x22\x14\x54\x18\x08\x30\x94\x56\x8e\xe3\x4d\x13\x19\x33\xbf\xdf\x0c\x2f\x20\x0b\xcc\x4e\xf1\x64\xe3"] = CTInfo($description="Google 'Argon2021' log", $operator="Google", $url="ct.googleapis.com/logs/argon2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x4d\xe0\x66\x64\xea\xf3\x64\xaa\x38\xc5\x89\x2d\xc7\xd8\x08\xd9\xc8\x44\x71\xed\xdc\xc3\xfb\x5b\xaf\x9c\x64\xa1\x09\x66\x84\x1d\x7c\x68\xa7\xec\xc4\x3f\x8c\x9c\x82\xe0\x18\xd9\x74\x14\xe9\xb4\x79\x81\xa2\x94\x55\x62\xf3\x9c\x0b\x44\x83\xa1\x2b\xc9\x71\x2b"),
|
||||
["\x68\xf6\x98\xf8\x1f\x64\x82\xbe\x3a\x8c\xee\xb9\x28\x1d\x4c\xfc\x71\x51\x5d\x67\x93\xd4\x44\xd1\x0a\x67\xac\xbb\x4f\x4f\xfb\xc4"] = CTInfo($description="Google 'Aviator' log", $operator="Google", $url="ct.googleapis.com/aviator/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\xf4\xcc\x69\xb2\xe4\x0e\x90\xa3\x8a\xea\x5a\x70\x09\x4f\xef\x13\x62\xd0\x8d\x49\x60\xff\x1b\x40\x50\x07\x0c\x6d\x71\x86\xda\x25\x49\x8d\x65\xe1\x08\x0d\x47\x34\x6b\xbd\x27\xbc\x96\x21\x3e\x34\xf5\x87\x76\x31\xb1\x7f\x1d\xc9\x85\x3b\x0d\xf7\x1f\x3f\xe9"),
|
||||
["\x29\x3c\x51\x96\x54\xc8\x39\x65\xba\xaa\x50\xfc\x58\x07\xd4\xb7\x6f\xbf\x58\x7a\x29\x72\xdc\xa4\xc3\x0c\xf4\xe5\x45\x47\xf4\x78"] = CTInfo($description="Google 'Icarus' log", $operator="Google", $url="ct.googleapis.com/icarus/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x4e\xd2\xbc\xbf\xb3\x08\x0a\xf7\xb9\xea\xa4\xc7\x1c\x38\x61\x04\xeb\x95\xe0\x89\x54\x68\x44\xb1\x66\xbc\x82\x7e\x4f\x50\x6c\x6f\x5c\xa3\xf0\xaa\x3e\xf4\xec\x80\xf0\xdb\x0a\x9a\x7a\xa0\x5b\x72\x00\x7c\x25\x0e\x19\xef\xaf\xb2\x62\x8d\x74\x43\xf4\x26\xf6\x14"),
|
||||
["\xa4\xb9\x09\x90\xb4\x18\x58\x14\x87\xbb\x13\xa2\xcc\x67\x70\x0a\x3c\x35\x98\x04\xf9\x1b\xdf\xb8\xe3\x77\xcd\x0e\xc8\x0d\xdc\x10"] = CTInfo($description="Google 'Pilot' log", $operator="Google", $url="ct.googleapis.com/pilot/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x7d\xa8\x4b\x12\x29\x80\xa3\x3d\xad\xd3\x5a\x77\xb8\xcc\xe2\x88\xb3\xa5\xfd\xf1\xd3\x0c\xcd\x18\x0c\xe8\x41\x46\xe8\x81\x01\x1b\x15\xe1\x4b\xf1\x1b\x62\xdd\x36\x0a\x08\x18\xba\xed\x0b\x35\x84\xd0\x9e\x40\x3c\x2d\x9e\x9b\x82\x65\xbd\x1f\x04\x10\x41\x4c\xa0"),
|
||||
["\xee\x4b\xbd\xb7\x75\xce\x60\xba\xe1\x42\x69\x1f\xab\xe1\x9e\x66\xa3\x0f\x7e\x5f\xb0\x72\xd8\x83\x00\xc4\x7b\x89\x7a\xa8\xfd\xcb"] = CTInfo($description="Google 'Rocketeer' log", $operator="Google", $url="ct.googleapis.com/rocketeer/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x20\x5b\x18\xc8\x3c\xc1\x8b\xb3\x31\x08\x00\xbf\xa0\x90\x57\x2b\xb7\x47\x8c\x6f\xb5\x68\xb0\x8e\x90\x78\xe9\xa0\x73\xea\x4f\x28\x21\x2e\x9c\xc0\xf4\x16\x1b\xaa\xf9\xd5\xd7\xa9\x80\xc3\x4e\x2f\x52\x3c\x98\x01\x25\x46\x24\x25\x28\x23\x77\x2d\x05\xc2\x40\x7a"),
|
||||
["\xbb\xd9\xdf\xbc\x1f\x8a\x71\xb5\x93\x94\x23\x97\xaa\x92\x7b\x47\x38\x57\x95\x0a\xab\x52\xe8\x1a\x90\x96\x64\x36\x8e\x1e\xd1\x85"] = CTInfo($description="Google 'Skydiver' log", $operator="Google", $url="ct.googleapis.com/skydiver/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x12\x6c\x86\x0e\xf6\x17\xb1\x12\x6c\x37\x25\xd2\xad\x87\x3d\x0e\x31\xec\x21\xad\xb1\xcd\xbe\x14\x47\xb6\x71\x56\x85\x7a\x9a\xb7\x3d\x89\x90\x7b\xc6\x32\x3a\xf8\xda\xce\x8b\x01\xfe\x3f\xfc\x71\x91\x19\x8e\x14\x6e\x89\x7a\x5d\xb4\xab\x7e\xe1\x4e\x1e\x7c\xac"),
|
||||
["\xa8\x99\xd8\x78\x0c\x92\x90\xaa\xf4\x62\xf3\x18\x80\xcc\xfb\xd5\x24\x51\xe9\x70\xd0\xfb\xf5\x91\xef\x75\xb0\xd9\x9b\x64\x56\x81"] = CTInfo($description="Google 'Submariner' log", $operator="Google", $url="ct.googleapis.com/submariner/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x39\xf8\x9f\x20\x62\xd4\x57\x55\x68\xa2\xef\x49\x2d\xf0\x39\x2d\x9a\xde\x44\xb4\x94\x30\xe0\x9e\x7a\x27\x3c\xab\x70\xf0\xd1\xfa\x51\x90\x63\x16\x57\x41\xad\xab\x6d\x1f\x80\x74\x30\x79\x02\x5e\x2d\x59\x84\x07\x24\x23\xf6\x9f\x35\xb8\x85\xb8\x42\x45\xa4\x4f"),
|
||||
["\x1d\x02\x4b\x8e\xb1\x49\x8b\x34\x4d\xfd\x87\xea\x3e\xfc\x09\x96\xf7\x50\x6f\x23\x5d\x1d\x49\x70\x61\xa4\x77\x3c\x43\x9c\x25\xfb"] = CTInfo($description="Google 'Daedalus' log", $operator="Google", $url="ct.googleapis.com/daedalus/", $maximum_merge_delay=604800, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x6e\x0c\x1c\xba\xee\x2b\x6a\x41\x85\x60\x1d\x7b\x7e\xab\x08\x2c\xfc\x0c\x0a\xa5\x08\xb3\x3e\xd5\x70\x24\xd1\x6d\x1d\x2d\xb6\xb7\xf3\x8b\x36\xdc\x23\x4d\x95\x63\x12\xbb\xe4\x86\x8d\xcc\xe9\xd1\xee\xa1\x40\xa2\xdf\x0b\xa3\x06\x0a\x30\xca\x8d\xac\xa4\x29\x56"),
|
||||
["\xb0\xcc\x83\xe5\xa5\xf9\x7d\x6b\xaf\x7c\x09\xcc\x28\x49\x04\x87\x2a\xc7\xe8\x8b\x13\x2c\x63\x50\xb7\xc6\xfd\x26\xe1\x6c\x6c\x77"] = CTInfo($description="Google 'Testtube' log", $operator="Google", $url="ct.googleapis.com/testtube/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xc3\xc8\xbc\x4b\xba\xa2\x18\x4b\x3d\x35\x7b\xf4\x64\x91\x61\xea\xeb\x8e\x99\x1d\x90\xed\xd3\xe9\xaf\x39\x3d\x5c\xd3\x46\x91\x45\xe3\xce\xac\x76\x48\x3b\xd1\x7e\x2c\x0a\x63\x00\x65\x8d\xf5\xae\x8e\x8c\xc7\x11\x25\x4f\x43\x2c\x9d\x19\xa1\xe1\x91\xa4\xb3\xfe"),
|
||||
["\x1f\xbc\x36\xe0\x02\xed\xe9\x7f\x40\x19\x9e\x86\xb3\x57\x3b\x8a\x42\x17\xd8\x01\x87\x74\x6a\xd0\xda\x03\xa0\x60\x54\xd2\x0d\xf4"] = CTInfo($description="Cloudflare 'Nimbus2017' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2017/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\x9c\xa9\x07\x8d\x22\x41\xee\x93\xa0\x52\x41\xda\xf4\x80\xf0\x25\xbc\xeb\xfa\xf3\x3c\xd2\x7e\x91\xd8\x3f\x2c\xda\x51\xbd\xc8\xee\x2a\x72\xe3\xff\x18\x56\xe4\x3a\x22\x0f\x22\x3c\xc6\xd5\x30\xb3\x9b\x68\x2e\xab\x56\xc2\x41\x5f\xd6\x64\x57\x14\xb1\x5a\xaf"),
|
||||
["\xdb\x74\xaf\xee\xcb\x29\xec\xb1\xfe\xca\x3e\x71\x6d\x2c\xe5\xb9\xaa\xbb\x36\xf7\x84\x71\x83\xc7\x5d\x9d\x4f\x37\xb6\x1f\xbf\x64"] = CTInfo($description="Cloudflare 'Nimbus2018' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2018/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\xc5\x69\x5a\xfa\xc7\xdc\xa7\xb4\x55\x16\x8c\x83\xd6\x50\xa1\x08\xdb\xe6\x0f\xf1\x87\x5c\xf7\x0c\x36\xba\x22\xec\x58\xe4\x3c\x8f\xb2\x4e\x9b\xae\x5b\xeb\x50\xd5\xd9\xce\x82\x20\xd0\x37\x2f\x16\x20\x27\xda\x47\x7a\xc6\x6b\xb8\x39\xb9\x39\x5c\x0f\xe7\x46"),
|
||||
["\x74\x7e\xda\x83\x31\xad\x33\x10\x91\x21\x9c\xce\x25\x4f\x42\x70\xc2\xbf\xfd\x5e\x42\x20\x08\xc6\x37\x35\x79\xe6\x10\x7b\xcc\x56"] = CTInfo($description="Cloudflare 'Nimbus2019' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2019/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x91\x91\xf3\xd6\xfe\x6b\xf1\xaf\x4b\x99\x74\x8c\x7a\x06\x19\x02\x0e\x14\x5b\xe5\x20\xe7\xa1\xad\x35\xf2\x53\x0c\xd1\x59\xba\xe6\xc4\x25\x88\x16\x7f\x81\x5c\x0b\x90\xfe\x66\x46\x30\xb6\xd5\xd3\x0d\x2a\x38\x3a\x46\xa7\x1b\xd6\xf7\x00\x8e\x2c\xc0\x84\x36\xf2"),
|
||||
["\x5e\xa7\x73\xf9\xdf\x56\xc0\xe7\xb5\x36\x48\x7d\xd0\x49\xe0\x32\x7a\x91\x9a\x0c\x84\xa1\x12\x12\x84\x18\x75\x96\x81\x71\x45\x58"] = CTInfo($description="Cloudflare 'Nimbus2020' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd3\x51\x00\x87\x1e\x28\xd3\x33\xd0\xad\x74\xdc\x62\x38\x02\xb7\x83\x15\x16\xc4\xf4\x3f\x08\xf3\x6f\x54\x70\xac\xcd\x25\x85\x60\xe5\xc4\x06\x0f\x3f\xaf\xe0\xc8\xc0\x97\x36\x43\xa7\xff\xb2\x85\xb2\x32\xfb\xaf\x09\x3b\xf2\xd1\xcc\xa5\x8f\x2b\x5e\x7f\x00\x62"),
|
||||
["\x44\x94\x65\x2e\xb0\xee\xce\xaf\xc4\x40\x07\xd8\xa8\xfe\x28\xc0\xda\xe6\x82\xbe\xd8\xcb\x31\xb5\x3f\xd3\x33\x96\xb5\xb6\x81\xa8"] = CTInfo($description="Cloudflare 'Nimbus2021' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xc6\x9a\x27\xee\x2a\x6c\xa9\xe8\x48\x79\x4d\x5b\x9a\x9a\x20\xf5\x31\x68\xe0\xf9\x3c\xfb\xda\x0d\xf0\xe6\x07\x97\x54\x36\x24\x65\x57\x9e\x45\x45\x9e\xeb\xaf\x3e\x04\xa8\xd8\x4a\x7e\xea\xf2\xdf\x7c\xd2\xdc\x98\x46\xf1\x3a\xe7\x33\xd3\x7b\x05\x89\xe9\x9a\xb6"),
|
||||
["\x56\x14\x06\x9a\x2f\xd7\xc2\xec\xd3\xf5\xe1\xbd\x44\xb2\x3e\xc7\x46\x76\xb9\xbc\x99\x11\x5c\xc0\xef\x94\x98\x55\xd6\x89\xd0\xdd"] = CTInfo($description="DigiCert Log Server", $operator="DigiCert", $url="ct1.digicert-ct.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\x46\xc5\xbe\x1b\xbb\x82\x40\x16\xe8\xc1\xd2\xac\x19\x69\x13\x59\xf8\xf8\x70\x85\x46\x40\xb9\x38\xb0\x23\x82\xa8\x64\x4c\x7f\xbf\xbb\x34\x9f\x4a\x5f\x28\x8a\xcf\x19\xc4\x00\xf6\x36\x06\x93\x65\xed\x4c\xf5\xa9\x21\x62\x5a\xd8\x91\xeb\x38\x24\x40\xac\xe8"),
|
||||
["\x87\x75\xbf\xe7\x59\x7c\xf8\x8c\x43\x99\x5f\xbd\xf3\x6e\xff\x56\x8d\x47\x56\x36\xff\x4a\xb5\x60\xc1\xb4\xea\xff\x5e\xa0\x83\x0f"] = CTInfo($description="DigiCert Log Server 2", $operator="DigiCert", $url="ct2.digicert-ct.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xcc\x5d\x39\x2f\x66\xb8\x4c\x7f\xc1\x2e\x03\xa1\x34\xa3\xe8\x8a\x86\x02\xae\x4a\x11\xc6\xf7\x26\x6a\x37\x9b\xf0\x38\xf8\x5d\x09\x8d\x63\xe8\x31\x6b\x86\x66\xcf\x79\xb3\x25\x3c\x1e\xdf\x78\xb4\xa8\xc5\x69\xfa\xb7\xf0\x82\x79\x62\x43\xf6\xcc\xfe\x81\x66\x84"),
|
||||
["\xc1\x16\x4a\xe0\xa7\x72\xd2\xd4\x39\x2d\xc8\x0a\xc1\x07\x70\xd4\xf0\xc4\x9b\xde\x99\x1a\x48\x40\xc1\xfa\x07\x51\x64\xf6\x33\x60"] = CTInfo($description="DigiCert Yeti2018 Log", $operator="DigiCert", $url="yeti2018.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x49\x89\x4a\x14\x32\xcb\x16\x60\x3d\x25\x27\x1a\x89\xa3\x67\xaa\x55\x3c\xa1\x60\xf2\xb7\x12\x18\x31\xfb\x30\x1f\x2f\x44\xb2\x0d\x1a\x89\x7f\x96\x9c\xff\xf2\x8f\x83\xb4\x56\x21\x07\xb4\xbc\x1b\x98\xe4\x1e\x49\x60\x46\x90\x8b\xbd\x60\xaf\x42\x2d\xe7\xab\xfa"),
|
||||
["\xe2\x69\x4b\xae\x26\xe8\xe9\x40\x09\xe8\x86\x1b\xb6\x3b\x83\xd4\x3e\xe7\xfe\x74\x88\xfb\xa4\x8f\x28\x93\x01\x9d\xdd\xf1\xdb\xfe"] = CTInfo($description="DigiCert Yeti2019 Log", $operator="DigiCert", $url="yeti2019.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x91\x97\x7f\xa3\x0f\x17\xf8\x54\x95\x58\x05\x52\x7f\xcc\x73\x90\x5a\x21\x70\xfa\x61\xff\x1e\xa9\x4b\x52\x47\x87\xb8\x35\xc2\x70\x99\xe7\x2f\xfc\x1e\x4e\xa3\xcc\x9c\x6c\xea\xdd\xd8\x30\x05\xb3\xd8\x23\xdd\xe1\x59\x02\x77\x1c\x0a\x7b\x11\xa1\x70\x5c\x43\xf4"),
|
||||
["\xf0\x95\xa4\x59\xf2\x00\xd1\x82\x40\x10\x2d\x2f\x93\x88\x8e\xad\x4b\xfe\x1d\x47\xe3\x99\xe1\xd0\x34\xa6\xb0\xa8\xaa\x8e\xb2\x73"] = CTInfo($description="DigiCert Yeti2020 Log", $operator="DigiCert", $url="yeti2020.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x51\x10\x06\xf9\x9a\x34\x69\xcd\xe7\xdf\xb8\x9f\x64\xa5\x21\x04\x51\x15\xea\x37\xdc\x0b\x31\x88\x47\x3d\xed\xb2\xaf\x02\x6b\xd0\x4f\xff\x95\xd4\x1f\x2e\x99\x8a\xab\x0f\x68\x01\x1b\x54\xcd\x2e\x23\x74\xe6\xf5\x4d\xb8\x45\x50\x47\x47\xd2\x71\x0c\x49\x4f\x9a"),
|
||||
["\x5c\xdc\x43\x92\xfe\xe6\xab\x45\x44\xb1\x5e\x9a\xd4\x56\xe6\x10\x37\xfb\xd5\xfa\x47\xdc\xa1\x73\x94\xb2\x5e\xe6\xf6\xc7\x0e\xca"] = CTInfo($description="DigiCert Yeti2021 Log", $operator="DigiCert", $url="yeti2021.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xe8\x9e\x04\x6d\xca\x48\x02\x5d\x7e\x02\x44\x91\xb1\xb8\x68\x63\x9a\x11\x4e\x3d\xd5\xa0\x57\xda\x7f\x50\xe5\x42\x47\xe2\xed\x16\xde\xfc\x77\x23\x7d\x5b\x6b\xc0\xdf\x23\x68\x2f\xad\x40\x31\xa3\x17\xe7\x6a\xbc\xa8\x56\x24\x04\x3a\x43\xa7\xaf\xea\xaf\x4c\x7b"),
|
||||
["\x22\x45\x45\x07\x59\x55\x24\x56\x96\x3f\xa1\x2f\xf1\xf7\x6d\x86\xe0\x23\x26\x63\xad\xc0\x4b\x7f\x5d\xc6\x83\x5c\x6e\xe2\x0f\x02"] = CTInfo($description="DigiCert Yeti2022 Log", $operator="DigiCert", $url="yeti2022.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x9f\xf8\xd8\x1d\xde\xfb\x5b\x51\xb5\xfb\x5d\xf5\xb5\xde\x66\x11\xb0\x9d\x5f\xfd\x6f\xfc\xa8\x98\x5b\x98\x4f\x2d\xc3\x91\x3a\xfb\xfe\xc4\x0f\x0d\xc3\x60\x43\x8c\x1e\xf2\xf9\x11\xb2\xba\xd0\xf6\xbc\xa5\xd2\xb6\x9f\xf9\x5c\x87\xa2\x7d\xfc\xd4\x7d\xd6\x13\x26"),
|
||||
["\x6f\xf1\x41\xb5\x64\x7e\x42\x22\xf7\xef\x05\x2c\xef\xae\x7c\x21\xfd\x60\x8e\x27\xd2\xaf\x5a\x6e\x9f\x4b\x8a\x37\xd6\x63\x3e\xe5"] = CTInfo($description="DigiCert Nessie2018 Log", $operator="DigiCert", $url="nessie2018.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x56\xaa\x4b\x6b\x65\xbe\x47\x3d\x57\x0d\x93\xc1\x23\x22\x89\x3b\xe2\x8a\x14\xe6\x19\x4e\x3f\x4c\xa4\x95\xa7\x65\xe1\x54\xab\x37\x39\x6a\x2b\xce\x89\x61\x15\x86\xcf\x06\xcb\x60\x25\x1f\x78\xab\x58\xf1\x63\x21\x93\xd9\x32\xcd\xc3\xbf\xb3\x3e\xd0\xb6\xcf\xc9"),
|
||||
["\xfe\x44\x61\x08\xb1\xd0\x1a\xb7\x8a\x62\xcc\xfe\xab\x6a\xb2\xb2\xba\xbf\xf3\xab\xda\xd8\x0a\x4d\x8b\x30\xdf\x2d\x00\x08\x83\x0c"] = CTInfo($description="DigiCert Nessie2019 Log", $operator="DigiCert", $url="nessie2019.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x5f\xed\x27\xb9\xd0\x8a\x22\x67\x7b\x40\x2b\x5e\x96\x13\x2b\x0d\x6d\x0e\x5e\x78\xb9\x44\x4d\x74\xb6\x28\x82\x95\x97\xac\x9a\xbc\x14\x93\x68\x87\x2c\x2a\x13\x1c\x75\x55\xfb\x28\x39\x0f\x89\xff\xaf\x10\x91\x57\x24\x61\x8a\x43\xe9\x54\x33\x8b\x30\xbc\x49\x68"),
|
||||
["\xc6\x52\xa0\xec\x48\xce\xb3\xfc\xab\x17\x09\x92\xc4\x3a\x87\x41\x33\x09\xe8\x00\x65\xa2\x62\x52\x40\x1b\xa3\x36\x2a\x17\xc5\x65"] = CTInfo($description="DigiCert Nessie2020 Log", $operator="DigiCert", $url="nessie2020.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xe2\x11\xc8\xc8\xc5\x48\xad\x1f\x68\x4a\x18\x1b\x40\xc6\x04\x93\xc5\x97\xd6\x59\xa4\x7c\x52\x81\xe3\x8f\x06\x9f\xdd\xca\x6e\xc6\x67\x9f\x09\x63\x0c\x76\x3a\x31\x0a\x84\x9d\x67\xca\x1a\x03\x0e\xab\x48\x21\xdd\x02\xb8\xf1\xce\x59\x07\x75\x0a\x48\x81\x59\xe2"),
|
||||
["\xee\xc0\x95\xee\x8d\x72\x64\x0f\x92\xe3\xc3\xb9\x1b\xc7\x12\xa3\x69\x6a\x09\x7b\x4b\x6a\x1a\x14\x38\xe6\x47\xb2\xcb\xed\xc5\xf9"] = CTInfo($description="DigiCert Nessie2021 Log", $operator="DigiCert", $url="nessie2021.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xf6\x8e\xc0\x8b\x0a\xdb\x18\x12\x17\xe8\xb9\xdc\xe3\xb2\x3a\x39\xf2\xcc\x75\x99\xd1\xcc\xaa\x0f\xe6\xed\x3b\xda\x70\x62\xea\xfa\x48\x38\x4a\x28\x92\xd4\xe2\xd6\x03\x70\x95\x13\xf3\x18\x2d\xb2\x48\x67\xee\x73\x5c\x4b\x0d\xe6\x80\xff\x04\x85\x1a\x0a\x58\x16"),
|
||||
["\x51\xa3\xb0\xf5\xfd\x01\x79\x9c\x56\x6d\xb8\x37\x78\x8f\x0c\xa4\x7a\xcc\x1b\x27\xcb\xf7\x9e\x88\x42\x9a\x0d\xfe\xd4\x8b\x05\xe5"] = CTInfo($description="DigiCert Nessie2022 Log", $operator="DigiCert", $url="nessie2022.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x27\x24\xdd\x68\x03\x28\xcb\xfe\x63\xbe\x0e\x11\x47\x4d\x7d\x17\x68\xa1\x11\x5d\x4c\x71\xc9\x41\x28\xc7\xb6\xa2\x4b\x97\xec\xc0\xaf\xfc\x2f\x3b\xbf\xe9\xf1\xb1\xfc\xf5\x01\xff\xa9\xfb\x49\x40\x0c\x63\x24\x98\xd7\x79\x2e\xa6\x55\xab\x16\xc6\xbe\x51\xd8\x71"),
|
||||
["\xdd\xeb\x1d\x2b\x7a\x0d\x4f\xa6\x20\x8b\x81\xad\x81\x68\x70\x7e\x2e\x8e\x9d\x01\xd5\x5c\x88\x8d\x3d\x11\xc4\xcd\xb6\xec\xbe\xcc"] = CTInfo($description="Symantec log", $operator="DigiCert", $url="ct.ws.symantec.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x96\xea\xac\x1c\x46\x0c\x1b\x55\xdc\x0d\xfc\xb5\x94\x27\x46\x57\x42\x70\x3a\x69\x18\xe2\xbf\x3b\xc4\xdb\xab\xa0\xf4\xb6\x6c\xc0\x53\x3f\x4d\x42\x10\x33\xf0\x58\x97\x8f\x6b\xbe\x72\xf4\x2a\xec\x1c\x42\xaa\x03\x2f\x1a\x7e\x28\x35\x76\x99\x08\x3d\x21\x14\x86"),
|
||||
["\xbc\x78\xe1\xdf\xc5\xf6\x3c\x68\x46\x49\x33\x4d\xa1\x0f\xa1\x5f\x09\x79\x69\x20\x09\xc0\x81\xb4\xf3\xf6\x91\x7f\x3e\xd9\xb8\xa5"] = CTInfo($description="Symantec 'Vega' log", $operator="DigiCert", $url="vega.ws.symantec.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xea\x95\x9e\x02\xff\xee\xf1\x33\x6d\x4b\x87\xbc\xcd\xfd\x19\x17\x62\xff\x94\xd3\xd0\x59\x07\x3f\x02\x2d\x1c\x90\xfe\xc8\x47\x30\x3b\xf1\xdd\x0d\xb8\x11\x0c\x5d\x1d\x86\xdd\xab\xd3\x2b\x46\x66\xfb\x6e\x65\xb7\x3b\xfd\x59\x68\xac\xdf\xa6\xf8\xce\xd2\x18\x4d"),
|
||||
["\xa7\xce\x4a\x4e\x62\x07\xe0\xad\xde\xe5\xfd\xaa\x4b\x1f\x86\x76\x87\x67\xb5\xd0\x02\xa5\x5d\x47\x31\x0e\x7e\x67\x0a\x95\xea\xb2"] = CTInfo($description="Symantec Deneb", $operator="DigiCert", $url="deneb.ws.symantec.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x96\x82\x1e\xa3\xcd\x3a\x80\x84\x1e\x97\xb8\xb7\x07\x19\xae\x76\x1a\x0e\xf8\x55\x76\x9d\x12\x33\x4e\x91\x88\xe4\xd0\x48\x50\x5c\xc1\x9f\x6a\x72\xd6\x01\xf5\x14\xd6\xd0\x38\x6e\xe1\x32\xbc\x67\x0d\x37\xe8\xba\x22\x10\xd1\x72\x86\x79\x28\x96\xf9\x17\x1e\x98"),
|
||||
["\x15\x97\x04\x88\xd7\xb9\x97\xa0\x5b\xeb\x52\x51\x2a\xde\xe8\xd2\xe8\xb4\xa3\x16\x52\x64\x12\x1a\x9f\xab\xfb\xd5\xf8\x5a\xd9\x3f"] = CTInfo($description="Symantec 'Sirius' log", $operator="DigiCert", $url="sirius.ws.symantec.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xa3\x02\x64\x84\x22\xbb\x25\xec\x0d\xe3\xbc\xc2\xc9\x89\x7d\xdd\x45\xd0\xee\xe6\x15\x85\x8f\xd9\xe7\x17\x1b\x13\x80\xea\xed\xb2\x85\x37\xad\x6a\xc5\xd8\x25\x9d\xfa\xf4\xb4\xf3\x6e\x16\x28\x25\x37\xea\xa3\x37\x64\xb2\xc7\x0b\xfd\x51\xe5\xc1\x05\xf4\x0e\xb5"),
|
||||
["\xcd\xb5\x17\x9b\x7f\xc1\xc0\x46\xfe\xea\x31\x13\x6a\x3f\x8f\x00\x2e\x61\x82\xfa\xf8\x89\x6f\xec\xc8\xb2\xf5\xb5\xab\x60\x49\x00"] = CTInfo($description="Certly.IO log", $operator="Certly", $url="log.certly.io/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x0b\x23\xcb\x85\x62\x98\x61\x48\x04\x73\xeb\x54\x5d\xf3\xd0\x07\x8c\x2d\x19\x2d\x8c\x36\xf5\xeb\x8f\x01\x42\x0a\x7c\x98\x26\x27\xc1\xb5\xdd\x92\x93\xb0\xae\xf8\x9b\x3d\x0c\xd8\x4c\x4e\x1d\xf9\x15\xfb\x47\x68\x7b\xba\x66\xb7\x25\x9c\xd0\x4a\xc2\x66\xdb\x48"),
|
||||
["\x74\x61\xb4\xa0\x9c\xfb\x3d\x41\xd7\x51\x59\x57\x5b\x2e\x76\x49\xa4\x45\xa8\xd2\x77\x09\xb0\xcc\x56\x4a\x64\x82\xb7\xeb\x41\xa3"] = CTInfo($description="Izenpe log", $operator="Izenpe", $url="ct.izenpe.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x27\x64\x39\x0c\x2d\xdc\x50\x18\xf8\x21\x00\xa2\x0e\xed\x2c\xea\x3e\x75\xba\x9f\x93\x64\x09\x00\x11\xc4\x11\x17\xab\x5c\xcf\x0f\x74\xac\xb5\x97\x90\x93\x00\x5b\xb8\xeb\xf7\x27\x3d\xd9\xb2\x0a\x81\x5f\x2f\x0d\x75\x38\x94\x37\x99\x1e\xf6\x07\x76\xe0\xee\xbe"),
|
||||
["\x89\x41\x44\x9c\x70\x74\x2e\x06\xb9\xfc\x9c\xe7\xb1\x16\xba\x00\x24\xaa\x36\xd5\x9a\xf4\x4f\x02\x04\x40\x4f\x00\xf7\xea\x85\x66"] = CTInfo($description="Izenpe 'Argi' log", $operator="Izenpe", $url="ct.izenpe.eus/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\xc8\x0e\x23\x3e\x9e\x02\x3c\x9a\xb8\x07\x4a\x2a\x05\xff\x4a\x4b\x88\xd4\x8a\x4d\x39\xce\xf7\xc5\xf2\xb6\x37\xe9\xa3\xed\xe4\xf5\x45\x09\x0e\x67\x14\xfd\x53\x24\xd5\x3a\x94\xf2\xea\xb5\x13\xd9\x1d\x8b\x5c\xa7\xc3\xf3\x6b\xd8\x3f\x2d\x3b\x65\x72\x58\xd6"),
|
||||
["\x9e\x4f\xf7\x3d\xc3\xce\x22\x0b\x69\x21\x7c\x89\x9e\x46\x80\x76\xab\xf8\xd7\x86\x36\xd5\xcc\xfc\x85\xa3\x1a\x75\x62\x8b\xa8\x8b"] = CTInfo($description="WoSign CT log #1", $operator="WoSign", $url="ct.wosign.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\xec\x2f\x2b\x75\x4f\x37\xbc\xa3\x43\xba\x8b\x65\x66\x3c\x7d\x6a\xe5\x0c\x2a\xa6\xc2\xe5\x26\xfe\x0c\x7d\x4e\x7c\xf0\x3a\xbc\xe2\xd3\x22\xdc\x01\xd0\x1f\x6e\x43\x9c\x5c\x6e\x83\xad\x9c\x15\xf6\xc4\x8d\x60\xb5\x1d\xbb\xa3\x62\x69\x7e\xeb\xa7\xaa\x01\x9b"),
|
||||
["\x41\xb2\xdc\x2e\x89\xe6\x3c\xe4\xaf\x1b\xa7\xbb\x29\xbf\x68\xc6\xde\xe6\xf9\xf1\xcc\x04\x7e\x30\xdf\xfa\xe3\xb3\xba\x25\x92\x63"] = CTInfo($description="WoSign log", $operator="WoSign", $url="ctlog.wosign.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xcc\x11\x88\x7b\x2d\x66\xcb\xae\x8f\x4d\x30\x66\x27\x19\x25\x22\x93\x21\x46\xb4\x2f\x01\xd3\xc6\xf9\x2b\xd5\xc8\xba\x73\x9b\x06\xa2\xf0\x8a\x02\x9c\xd0\x6b\x46\x18\x30\x85\xba\xe9\x24\x8b\x0e\xd1\x5b\x70\x28\x0c\x7e\xf1\x3a\x45\x7f\x5a\xf3\x82\x42\x60\x31"),
|
||||
["\x63\xd0\x00\x60\x26\xdd\xe1\x0b\xb0\x60\x1f\x45\x24\x46\x96\x5e\xe2\xb6\xea\x2c\xd4\xfb\xc9\x5a\xc8\x66\xa5\x50\xaf\x90\x75\xb7"] = CTInfo($description="WoSign log 2", $operator="WoSign", $url="ctlog2.wosign.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xa5\x8c\xe8\x35\x2e\x8e\xe5\x6a\x75\xad\x5c\x4b\x31\x61\x29\x9d\x30\x57\x8e\x02\x13\x5f\xe9\xca\xbb\x52\xa8\x43\x05\x60\xbf\x0d\x73\x57\x77\xb2\x05\xd8\x67\xf6\xf0\x33\xc9\xf9\x44\xde\xb6\x53\x73\xaa\x0c\x55\xc2\x83\x0a\x4b\xce\x5e\x1a\xc7\x17\x1d\xb3\xcd"),
|
||||
["\xc9\xcf\x89\x0a\x21\x10\x9c\x66\x6c\xc1\x7a\x3e\xd0\x65\xc9\x30\xd0\xe0\x13\x5a\x9f\xeb\xa8\x5a\xf1\x42\x10\xb8\x07\x24\x21\xaa"] = CTInfo($description="GDCA CT log #1", $operator="Wang Shengnan", $url="ct.gdca.com.cn/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xad\x0f\x30\xad\x9e\x79\xa4\x38\x89\x26\x54\x86\xab\x41\x72\x90\x6f\xfb\xca\x17\xa6\xac\xee\xc6\x9f\x7d\x02\x05\xec\x41\xa8\xc7\x41\x9d\x32\x49\xad\xb0\x39\xbd\x3a\x87\x3e\x7c\xee\x68\x6c\x60\xd1\x47\x2a\x93\xae\xe1\x40\xf4\x0b\xc8\x35\x3c\x1d\x0f\x65\xd3"),
|
||||
["\x92\x4a\x30\xf9\x09\x33\x6f\xf4\x35\xd6\x99\x3a\x10\xac\x75\xa2\xc6\x41\x72\x8e\x7f\xc2\xd6\x59\xae\x61\x88\xff\xad\x40\xce\x01"] = CTInfo($description="GDCA CT log #2", $operator="GDCA", $url="ctlog.gdca.com.cn/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x5b\x4a\xc7\x01\xb7\x74\x54\xba\x40\x9c\x43\x75\x94\x3f\xac\xef\xb3\x71\x56\xb8\xd3\xe2\x7b\xae\xa1\xb1\x3e\x53\xaa\x97\x33\xa1\x82\xbb\x5f\x5d\x1c\x0b\xfa\x85\x0d\xbc\xf7\xe5\xa0\xe0\x22\xf0\xa0\x89\xd9\x0a\x7f\x5f\x26\x94\xd3\x24\xe3\x99\x2e\xe4\x15\x8d"),
|
||||
["\xdb\x76\xfd\xad\xac\x65\xe7\xd0\x95\x08\x88\x6e\x21\x59\xbd\x8b\x90\x35\x2f\x5f\xea\xd3\xe3\xdc\x5e\x22\xeb\x35\x0a\xcc\x7b\x98"] = CTInfo($description="Comodo 'Dodo' CT log", $operator="Comodo CA Limited", $url="dodo.ct.comodo.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x2c\xf5\xc2\x31\xf5\x63\x43\x6a\x16\x4a\x0a\xde\xc2\xee\x1f\x21\x6e\x12\x7e\x1d\xe5\x72\x8f\x74\x0b\x02\x99\xd3\xad\x69\xbc\x02\x35\x79\xf9\x61\xe9\xcf\x00\x08\x4f\x74\xa4\xa3\x34\x9a\xe0\x43\x1c\x23\x7e\x8f\x41\xd5\xee\xc7\x1c\xa3\x82\x8a\x40\xfa\xaa\xe0"),
|
||||
["\xac\x3b\x9a\xed\x7f\xa9\x67\x47\x57\x15\x9e\x6d\x7d\x57\x56\x72\xf9\xd9\x81\x00\x94\x1e\x9b\xde\xff\xec\xa1\x31\x3b\x75\x78\x2d"] = CTInfo($description="Venafi log", $operator="Venafi", $url="ctlog.api.venafi.com/", $maximum_merge_delay=86400, $key="\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xa2\x5a\x48\x1f\x17\x52\x95\x35\xcb\xa3\x5b\x3a\x1f\x53\x82\x76\x94\xa3\xff\x80\xf2\x1c\x37\x3c\xc0\xb1\xbd\xc1\x59\x8b\xab\x2d\x65\x93\xd7\xf3\xe0\x04\xd5\x9a\x6f\xbf\xd6\x23\x76\x36\x4f\x23\x99\xcb\x54\x28\xad\x8c\x15\x4b\x65\x59\x76\x41\x4a\x9c\xa6\xf7\xb3\x3b\x7e\xb1\xa5\x49\xa4\x17\x51\x6c\x80\xdc\x2a\x90\x50\x4b\x88\x24\xe9\xa5\x12\x32\x93\x04\x48\x90\x02\xfa\x5f\x0e\x30\x87\x8e\x55\x76\x05\xee\x2a\x4c\xce\xa3\x6a\x69\x09\x6e\x25\xad\x82\x76\x0f\x84\x92\xfa\x38\xd6\x86\x4e\x24\x8f\x9b\xb0\x72\xcb\x9e\xe2\x6b\x3f\xe1\x6d\xc9\x25\x75\x23\x88\xa1\x18\x58\x06\x23\x33\x78\xda\x00\xd0\x38\x91\x67\xd2\xa6\x7d\x27\x97\x67\x5a\xc1\xf3\x2f\x17\xe6\xea\xd2\x5b\xe8\x81\xcd\xfd\x92\x68\xe7\xf3\x06\xf0\xe9\x72\x84\xee\x01\xa5\xb1\xd8\x33\xda\xce\x83\xa5\xdb\xc7\xcf\xd6\x16\x7e\x90\x75\x18\xbf\x16\xdc\x32\x3b\x6d\x8d\xab\x82\x17\x1f\x89\x20\x8d\x1d\x9a\xe6\x4d\x23\x08\xdf\x78\x6f\xc6\x05\xbf\x5f\xae\x94\x97\xdb\x5f\x64\xd4\xee\x16\x8b\xa3\x84\x6c\x71\x2b\xf1\xab\x7f\x5d\x0d\x32\xee\x04\xe2\x90\xec\x41\x9f\xfb\x39\xc1\x02\x03\x01\x00\x01"),
|
||||
["\x03\x01\x9d\xf3\xfd\x85\xa6\x9a\x8e\xbd\x1f\xac\xc6\xda\x9b\xa7\x3e\x46\x97\x74\xfe\x77\xf5\x79\xfc\x5a\x08\xb8\x32\x8c\x1d\x6b"] = CTInfo($description="Venafi Gen2 CT log", $operator="Venafi", $url="ctlog-gen2.api.venafi.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x8e\x27\x27\x7a\xb6\x55\x09\x74\xeb\x6c\x4b\x94\x84\x65\xbc\xe4\x15\xf1\xea\x5a\xd8\x7c\x0e\x37\xce\xba\x3f\x6c\x09\xda\xe7\x29\x96\xd3\x45\x50\x6f\xde\x1e\xb4\x1c\xd2\x83\x88\xff\x29\x2f\xce\xa9\xff\xdf\x34\xde\x75\x0f\xc0\xcc\x18\x0d\x94\x2e\xfc\x37\x01"),
|
||||
["\xa5\x77\xac\x9c\xed\x75\x48\xdd\x8f\x02\x5b\x67\xa2\x41\x08\x9d\xf8\x6e\x0f\x47\x6e\xc2\x03\xc2\xec\xbe\xdb\x18\x5f\x28\x26\x38"] = CTInfo($description="CNNIC CT log", $operator="CNNIC", $url="ctserver.cnnic.cn/", $maximum_merge_delay=86400, $key="\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xbf\xb5\x08\x61\x9a\x29\x32\x04\xd3\x25\x63\xe9\xd8\x85\xe1\x86\xe0\x1f\xd6\x5e\x9a\xf7\x33\x3b\x80\x1b\xe7\xb6\x3e\x5f\x2d\xa1\x66\xf6\x95\x4a\x84\xa6\x21\x56\x79\xe8\xf7\x85\xee\x5d\xe3\x7c\x12\xc0\xe0\x89\x22\x09\x22\x3e\xba\x16\x95\x06\xbd\xa8\xb9\xb1\xa9\xb2\x7a\xd6\x61\x2e\x87\x11\xb9\x78\x40\x89\x75\xdb\x0c\xdc\x90\xe0\xa4\x79\xd6\xd5\x5e\x6e\xd1\x2a\xdb\x34\xf4\x99\x3f\x65\x89\x3b\x46\xc2\x29\x2c\x15\x07\x1c\xc9\x4b\x1a\x54\xf8\x6c\x1e\xaf\x60\x27\x62\x0a\x65\xd5\x9a\xb9\x50\x36\x16\x6e\x71\xf6\x1f\x01\xf7\x12\xa7\xfc\xbf\xf6\x21\xa3\x29\x90\x86\x2d\x77\xde\xbb\x4c\xd4\xcf\xfd\xd2\xcf\x82\x2c\x4d\xd4\xf2\xc2\x2d\xac\xa9\xbe\xea\xc3\x19\x25\x43\xb2\xe5\x9a\x6c\x0d\xc5\x1c\xa5\x8b\xf7\x3f\x30\xaf\xb9\x01\x91\xb7\x69\x12\x12\xe5\x83\x61\xfe\x34\x00\xbe\xf6\x71\x8a\xc7\xeb\x50\x92\xe8\x59\xfe\x15\x91\xeb\x96\x97\xf8\x23\x54\x3f\x2d\x8e\x07\xdf\xee\xda\xb3\x4f\xc8\x3c\x9d\x6f\xdf\x3c\x2c\x43\x57\xa1\x47\x0c\x91\x04\xf4\x75\x4d\xda\x89\x81\xa4\x14\x06\x34\xb9\x98\xc3\xda\xf1\xfd\xed\x33\x36\xd3\x16\x2d\x35\x02\x03\x01\x00\x01"),
|
||||
["\x34\xbb\x6a\xd6\xc3\xdf\x9c\x03\xee\xa8\xa4\x99\xff\x78\x91\x48\x6c\x9d\x5e\x5c\xac\x92\xd0\x1f\x7b\xfd\x1b\xce\x19\xdb\x48\xef"] = CTInfo($description="StartCom log", $operator="StartCom", $url="ct.startssl.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x48\xf3\x59\xf3\xf6\x05\x18\xd3\xdb\xb2\xed\x46\x7e\xcf\xc8\x11\xb5\x57\xb1\xa8\xd6\x4c\xe6\x9f\xb7\x4a\x1a\x14\x86\x43\xa9\x48\xb0\xcb\x5a\x3f\x3c\x4a\xca\xdf\xc4\x82\x14\x55\x9a\xf8\xf7\x8e\x40\x55\xdc\xf4\xd2\xaf\xea\x75\x74\xfb\x4e\x7f\x60\x86\x2e\x51"),
|
||||
["\xe0\x12\x76\x29\xe9\x04\x96\x56\x4e\x3d\x01\x47\x98\x44\x98\xaa\x48\xf8\xad\xb1\x66\x00\xeb\x79\x02\xa1\xef\x99\x09\x90\x62\x73"] = CTInfo($description="PuChuangSiDa CT log", $operator="Beijing PuChuangSiDa Technology Ltd.", $url="www.certificatetransparency.cn/ct/", $maximum_merge_delay=86400, $key="\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xac\xcf\x2f\x4b\x70\xac\xf1\x0d\x96\xbf\xe8\x0a\xfe\x44\x9d\xd4\x8c\x17\x9d\xc3\x9a\x10\x11\x84\x13\xed\x8c\xf9\x37\x6d\x83\xe4\x00\x6f\xb1\x4b\xc0\xa6\x89\xc7\x61\x8f\x9a\x34\xbb\x56\x52\xca\x03\x56\x50\xef\x24\x7f\x4b\x49\xe9\x35\x81\xdd\xf0\xe7\x17\xf5\x72\xd2\x23\xc5\xe3\x13\x7f\xd7\x8e\x78\x35\x8f\x49\xde\x98\x04\x8a\x63\xaf\xad\xa2\x39\x70\x95\x84\x68\x4b\x91\x33\xfe\x4c\xe1\x32\x17\xc2\xf2\x61\xb8\x3a\x8d\x39\x7f\xd5\x95\x82\x3e\x56\x19\x50\x45\x6f\xcb\x08\x33\x0d\xd5\x19\x42\x08\x1a\x48\x42\x10\xf1\x68\xc3\xc3\x41\x13\xcb\x0d\x1e\xdb\x02\xb7\x24\x7a\x51\x96\x6e\xbc\x08\xea\x69\xaf\x6d\xef\x92\x98\x8e\x55\xf3\x65\xe5\xe8\x9c\xbe\x1a\x47\x60\x30\x7d\x7a\x80\xad\x56\x83\x7a\x93\xc3\xae\x93\x2b\x6a\x28\x8a\xa6\x5f\x63\x19\x0c\xbe\x7c\x7b\x21\x63\x41\x38\xb7\xf7\xe8\x76\x73\x6b\x85\xcc\xbc\x72\x2b\xc1\x52\xd0\x5b\x5d\x31\x4e\x9d\x2a\xf3\x4d\x9b\x64\x14\x99\x26\xc6\x71\xf8\x7b\xf8\x44\xd5\xe3\x23\x20\xf3\x0a\xd7\x8b\x51\x3e\x72\x80\xd2\x78\x78\x35\x2d\x4a\xe7\x40\x99\x11\x95\x34\xd4\x2f\x7f\xf9\x5f\x35\x37\x02\x03\x01\x00\x01"),
|
||||
["\x55\x81\xd4\xc2\x16\x90\x36\x01\x4a\xea\x0b\x9b\x57\x3c\x53\xf0\xc0\xe4\x38\x78\x70\x25\x08\x17\x2f\xa3\xaa\x1d\x07\x13\xd3\x0c"] = CTInfo($description="Comodo 'Sabre' CT log", $operator="Comodo CA Limited", $url="sabre.ct.comodo.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xf2\x6f\xd2\x89\x0f\x3f\xc5\xf8\x87\x1e\xab\x65\xb3\xd9\xbb\x17\x23\x8c\x06\x0e\x09\x55\x96\x3d\x0a\x08\xa2\xc5\x71\xb3\xd1\xa9\x2f\x28\x3e\x83\x10\xbf\x12\xd0\x44\x66\x15\xef\x54\xe1\x98\x80\xd0\xce\x24\x6d\x3e\x67\x9a\xe9\x37\x23\xce\x52\x93\x86\xda\x80"),
|
||||
["\x6f\x53\x76\xac\x31\xf0\x31\x19\xd8\x99\x00\xa4\x51\x15\xff\x77\x15\x1c\x11\xd9\x02\xc1\x00\x29\x06\x8d\xb2\x08\x9a\x37\xd9\x13"] = CTInfo($description="Comodo 'Mammoth' CT log", $operator="Comodo CA Limited", $url="mammoth.ct.comodo.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xef\xe4\x7d\x74\x2e\x15\x15\xb6\xe9\xbb\x23\x8b\xfb\x2c\xb5\xe1\xc7\x80\x98\x47\xfb\x40\x69\x68\xfc\x49\xad\x61\x4e\x83\x47\x3c\x1a\xb7\x8d\xdf\xff\x7b\x30\xb4\xba\xff\x2f\xcb\xa0\x14\xe3\xad\xd5\x85\x3f\x44\x59\x8c\x8c\x60\x8b\xd7\xb8\xb1\xbf\xae\x8c\x67"),
|
||||
["\x53\x7b\x69\xa3\x56\x43\x35\xa9\xc0\x49\x04\xe3\x95\x93\xb2\xc2\x98\xeb\x8d\x7a\x6e\x83\x02\x36\x35\xc6\x27\x24\x8c\xd6\xb4\x40"] = CTInfo($description="Nordu 'flimsy' log", $operator="NORDUnet", $url="flimsy.ct.nordu.net:8080/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xe2\xa5\xaa\xe9\xa7\xe1\x05\x48\xb4\x39\xd7\x16\x51\x88\x72\x24\xb3\x57\x4e\x41\xaa\x43\xd3\xcc\x4b\x99\x6a\xa0\x28\x24\x57\x68\x75\x66\xfa\x4d\x8c\x11\xf6\xbb\xc5\x1b\x81\xc3\x90\xc2\xa0\xe8\xeb\xac\xfa\x05\x64\x09\x1a\x89\x68\xcd\x96\x26\x34\x71\x36\x91"),
|
||||
["\xaa\xe7\x0b\x7f\x3c\xb8\xd5\x66\xc8\x6c\x2f\x16\x97\x9c\x9f\x44\x5f\x69\xab\x0e\xb4\x53\x55\x89\xb2\xf7\x7a\x03\x01\x04\xf3\xcd"] = CTInfo($description="Nordu 'plausible' log", $operator="NORDUnet", $url="plausible.ct.nordu.net/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xf5\x45\x7d\xfa\x33\xb6\x30\x24\xf3\x91\xa6\xe8\x74\xed\x85\xec\xb3\x34\xdc\xc5\x01\x73\xc3\x2b\x74\x0b\x64\x71\x6e\xaf\xe8\x60\x3d\xb5\xa4\xd3\xc3\xd4\x09\xaa\x87\xe6\xd0\x16\xdd\x02\xc6\xed\x24\xbf\xee\x9f\x21\x1f\xd3\x32\x24\x46\x05\xe3\x8f\x36\x98\xa9"),
|
||||
["\xcf\x55\xe2\x89\x23\x49\x7c\x34\x0d\x52\x06\xd0\x53\x53\xae\xb2\x58\x34\xb5\x2f\x1f\x8d\xc9\x52\x68\x09\xf2\x12\xef\xdd\x7c\xa6"] = CTInfo($description="SHECA CT log 1", $operator="SHECA", $url="ctlog.sheca.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x11\xa9\x60\x2b\xb4\x71\x45\x66\xe0\x2e\xde\xd5\x87\x3b\xd5\xfe\xf0\x92\x37\xf4\x68\xc6\x92\xdd\x3f\x1a\xe2\xbc\x0c\x22\xd6\x99\x63\x29\x6e\x32\x28\x14\xc0\x76\x2c\x80\xa8\x22\x51\x91\xd6\xeb\xa6\xd8\xf1\xec\xf0\x07\x7e\xb0\xfc\x76\x70\x76\x72\x7c\x91\xe9"),
|
||||
["\x32\xdc\x59\xc2\xd4\xc4\x19\x68\xd5\x6e\x14\xbc\x61\xac\x8f\x0e\x45\xdb\x39\xfa\xf3\xc1\x55\xaa\x42\x52\xf5\x00\x1f\xa0\xc6\x23"] = CTInfo($description="SHECA CT log 2", $operator="SHECA", $url="ct.sheca.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xb1\x8e\x1d\x8a\xaa\x3a\xac\xce\x86\xcb\x53\x76\xe8\xa8\x9d\x59\xbe\x17\x88\x03\x07\xf2\x27\xe0\x82\xbe\xb1\xfc\x67\x3b\x46\xee\xd3\xf1\x8d\xd6\x77\xe8\xa3\xb4\xdb\x09\x5c\xa0\x09\x43\xfc\x5f\xd0\x68\x34\x23\x24\x08\xc2\x4f\xd8\xd2\xb6\x9d\xed\xd5\x8c\xdb"),
|
||||
["\x96\x06\xc0\x2c\x69\x00\x33\xaa\x1d\x14\x5f\x59\xc6\xe2\x64\x8d\x05\x49\xf0\xdf\x96\xaa\xb8\xdb\x91\x5a\x70\xd8\xec\xf3\x90\xa5"] = CTInfo($description="Akamai CT Log", $operator="Akamai", $url="ct.akamai.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x43\x79\xeb\x49\x5c\x50\x2a\x4a\x6a\x8f\x59\x93\xbc\xc3\x42\x76\xc2\x99\xf8\x27\x81\x3c\x06\x6c\xd2\xc8\x04\x8f\x74\x7b\xb4\xb5\x21\xf2\xe3\xa8\xdc\x33\xb9\xfe\x25\xe9\x3d\x04\xfc\x3f\xb4\xae\x40\xe3\x45\x7e\x84\x92\x2a\xd8\x52\xeb\x1f\x3f\x73\x13\xd0\xc8"),
|
||||
["\x39\x37\x6f\x54\x5f\x7b\x46\x07\xf5\x97\x42\xd7\x68\xcd\x5d\x24\x37\xbf\x34\x73\xb6\x53\x4a\x48\x34\xbc\xf7\x2e\x68\x1c\x83\xc9"] = CTInfo($description="Alpha CT Log", $operator="Matt Palmer", $url="alpha.ctlogs.org/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xa2\xf7\xed\x13\xe1\xd3\x5c\x02\x08\xc4\x8e\x8b\x9b\x8b\x3b\x39\x68\xc7\x92\x6a\x38\xa1\x4f\x23\xc5\xa5\x6f\x6f\xd7\x65\x81\xf8\xc1\x9b\xf4\x9f\xa9\x8b\x45\xf4\xb9\x4e\x1b\xc9\xa2\x69\x17\xa5\x78\x87\xd9\xce\x88\x6f\x41\x03\xbb\xa3\x2a\xe3\x77\x97\x8d\x78"),
|
||||
["\x29\x6a\xfa\x2d\x56\x8b\xca\x0d\x2e\xa8\x44\x95\x6a\xe9\x72\x1f\xc3\x5f\xa3\x55\xec\xda\x99\x69\x3a\xaf\xd4\x58\xa7\x1a\xef\xdd"] = CTInfo($description="Let's Encrypt 'Clicky' log", $operator="Let's Encrypt", $url="clicky.ct.letsencrypt.org/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x1f\x1a\x15\x83\x77\x00\x75\x62\xb9\x9f\xf6\x06\x05\xed\x95\x89\x83\x41\x81\x97\xe7\xe0\xd4\x33\xfe\x76\xba\x3b\xc9\x49\xc2\xcd\xf1\xcf\xfe\x12\x70\xd7\xbe\xa8\x22\x5f\xb2\xa4\x67\x02\x7b\x71\xae\x1d\xac\xa8\xe9\xd1\x08\xd5\xce\xef\x33\x7a\xc3\x5f\x00\xdc"),
|
||||
["\xb0\xb7\x84\xbc\x81\xc0\xdd\xc4\x75\x44\xe8\x83\xf0\x59\x85\xbb\x90\x77\xd1\x34\xd8\xab\x88\xb2\xb2\xe5\x33\x98\x0b\x8e\x50\x8b"] = CTInfo($description="Up In The Air 'Behind the Sofa' log", $operator="Up In The Air Consulting", $url="ct.filippo.io/behindthesofa/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x59\x39\xb2\xa6\x94\xc6\x32\xb9\xfe\x63\x69\x1e\x30\x3b\xa3\x5b\xd5\xb0\x43\xc9\x50\x1e\x95\xa5\x2d\xa7\x4c\x4a\x49\x8e\x8b\x8f\xb7\xf8\xcc\xe2\x5b\x97\x72\xd5\xea\x3f\xb1\x21\x48\xe8\x44\x6b\x7f\xea\xef\x22\xff\xdf\xf4\x5f\x3b\x6d\x77\x04\xb1\xaf\x90\x8f"),
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
signature dpd_ssl_server {
|
||||
ip-proto == tcp
|
||||
# Server hello.
|
||||
payload /^((\x15\x03[\x00\x01\x02\x03]....)?\x16\x03[\x00\x01\x02\x03]..\x02...\x03[\x00\x01\x02\x03]|...?\x04..\x00\x02).*/
|
||||
payload /^((\x15\x03[\x00\x01\x02\x03]....)?\x16\x03[\x00\x01\x02\x03]..\x02...((\x03[\x00\x01\x02\x03\x04])|(\x7F[\x00-\x50]))|...?\x04..\x00\x02).*/
|
||||
requires-reverse-signature dpd_ssl_client
|
||||
enable "ssl"
|
||||
tcp-state responder
|
||||
|
@ -10,7 +10,7 @@ signature dpd_ssl_server {
|
|||
signature dpd_ssl_client {
|
||||
ip-proto == tcp
|
||||
# Client hello.
|
||||
payload /^(\x16\x03[\x00\x01\x02\x03]..\x01...\x03[\x00\x01\x02\x03]|...?\x01[\x00\x03][\x00\x01\x02\x03]).*/
|
||||
payload /^(\x16\x03[\x00\x01\x02\x03]..\x01...\x03[\x00\x01\x02\x03]|...?\x01[\x00\x03][\x00\x01\x02\x03\x04]).*/
|
||||
tcp-state originator
|
||||
}
|
||||
|
||||
|
|
|
@ -91,11 +91,26 @@ event bro_init() &priority=5
|
|||
$describe = SSL::describe_file]);
|
||||
}
|
||||
|
||||
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5
|
||||
event file_sniff(f: fa_file, meta: fa_metadata) &priority=5
|
||||
{
|
||||
if ( ! c?$ssl )
|
||||
if ( |f$conns| != 1 )
|
||||
return;
|
||||
|
||||
if ( ! f?$info || ! f$info?$mime_type )
|
||||
return;
|
||||
|
||||
if ( ! ( f$info$mime_type == "application/x-x509-ca-cert" || f$info$mime_type == "application/x-x509-user-cert"
|
||||
|| f$info$mime_type == "application/pkix-cert" ) )
|
||||
return;
|
||||
|
||||
for ( cid in f$conns )
|
||||
{
|
||||
if ( ! f$conns[cid]?$ssl )
|
||||
return;
|
||||
|
||||
local c = f$conns[cid];
|
||||
}
|
||||
|
||||
if ( ! c$ssl?$cert_chain )
|
||||
{
|
||||
c$ssl$cert_chain = vector();
|
||||
|
@ -104,22 +119,16 @@ event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priori
|
|||
c$ssl$client_cert_chain_fuids = string_vec();
|
||||
}
|
||||
|
||||
if ( is_orig )
|
||||
if ( f$is_orig )
|
||||
{
|
||||
c$ssl$client_cert_chain[|c$ssl$client_cert_chain|] = f$info;
|
||||
c$ssl$client_cert_chain_fuids[|c$ssl$client_cert_chain_fuids|] = f$id;
|
||||
c$ssl$client_cert_chain += f$info;
|
||||
c$ssl$client_cert_chain_fuids += f$id;
|
||||
}
|
||||
else
|
||||
{
|
||||
c$ssl$cert_chain[|c$ssl$cert_chain|] = f$info;
|
||||
c$ssl$cert_chain_fuids[|c$ssl$cert_chain_fuids|] = f$id;
|
||||
c$ssl$cert_chain += f$info;
|
||||
c$ssl$cert_chain_fuids += f$id;
|
||||
}
|
||||
|
||||
Files::add_analyzer(f, Files::ANALYZER_X509);
|
||||
# Always calculate hashes. They are not necessary for base scripts
|
||||
# but very useful for identification, and required for policy scripts.
|
||||
Files::add_analyzer(f, Files::ANALYZER_MD5);
|
||||
Files::add_analyzer(f, Files::ANALYZER_SHA1);
|
||||
}
|
||||
|
||||
event ssl_established(c: connection) &priority=6
|
||||
|
|
|
@ -64,7 +64,6 @@ export {
|
|||
## Flag to indicate if this ssl session has been established
|
||||
## successfully, or if it was aborted during the handshake.
|
||||
established: bool &log &default=F;
|
||||
|
||||
## Flag to indicate if this record already has been logged, to
|
||||
## prevent duplicates.
|
||||
logged: bool &default=F;
|
||||
|
@ -74,6 +73,26 @@ export {
|
|||
## script sets this to Mozilla's root CA list.
|
||||
const root_certs: table[string] of string = {} &redef;
|
||||
|
||||
## The record type which contains the field for the Certificate
|
||||
## Transparency log bundle.
|
||||
type CTInfo: record {
|
||||
## Description of the Log
|
||||
description: string;
|
||||
## Operator of the Log
|
||||
operator: string;
|
||||
## Public key of the Log.
|
||||
key: string;
|
||||
## Maximum merge delay of the Log
|
||||
maximum_merge_delay: count;
|
||||
## URL of the Log
|
||||
url: string;
|
||||
};
|
||||
|
||||
## The Certificate Transparency log bundle. By default, the ct-list.bro
|
||||
## script sets this to the current list of known logs. Entries
|
||||
## are indexed by (binary) log-id.
|
||||
const ct_logs: table[string] of CTInfo = {} &redef;
|
||||
|
||||
## If true, detach the SSL analyzer from the connection to prevent
|
||||
## continuing to process encrypted traffic. Helps with performance
|
||||
## (especially with large file transfers).
|
||||
|
@ -90,6 +109,10 @@ export {
|
|||
## Event that can be handled to access the SSL
|
||||
## record as it is sent on to the logging framework.
|
||||
global log_ssl: event(rec: Info);
|
||||
|
||||
# Hook that can be used to perform actions right before the log record
|
||||
# is written.
|
||||
global ssl_finishing: hook(c: connection);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
|
@ -193,15 +216,30 @@ event ssl_server_hello(c: connection, version: count, possible_ts: time, server_
|
|||
{
|
||||
set_session(c);
|
||||
|
||||
c$ssl$version_num = version;
|
||||
c$ssl$version = version_strings[version];
|
||||
# If it is already filled, we saw a supported_versions extensions which overrides this.
|
||||
if ( ! c$ssl?$version_num )
|
||||
{
|
||||
c$ssl$version_num = version;
|
||||
c$ssl$version = version_strings[version];
|
||||
}
|
||||
c$ssl$cipher = cipher_desc[cipher];
|
||||
|
||||
if ( c$ssl?$session_id && c$ssl$session_id == bytestring_to_hexstr(session_id) )
|
||||
c$ssl$resumed = T;
|
||||
}
|
||||
|
||||
event ssl_server_curve(c: connection, curve: count) &priority=5
|
||||
event ssl_extension_supported_versions(c: connection, is_orig: bool, versions: index_vec)
|
||||
{
|
||||
if ( is_orig || |versions| != 1 )
|
||||
return;
|
||||
|
||||
set_session(c);
|
||||
|
||||
c$ssl$version_num = versions[0];
|
||||
c$ssl$version = version_strings[versions[0]];
|
||||
}
|
||||
|
||||
event ssl_ecdh_server_params(c: connection, curve: count, point: string) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
|
||||
|
@ -281,11 +319,22 @@ event ssl_established(c: connection) &priority=7
|
|||
c$ssl$established = T;
|
||||
}
|
||||
|
||||
event ssl_established(c: connection) &priority=20
|
||||
{
|
||||
hook ssl_finishing(c);
|
||||
}
|
||||
|
||||
event ssl_established(c: connection) &priority=-5
|
||||
{
|
||||
finish(c, T);
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=20
|
||||
{
|
||||
if ( c?$ssl && ! c$ssl$logged )
|
||||
hook ssl_finishing(c);
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( c?$ssl )
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -118,7 +118,7 @@ function extract_ip_addresses(input: string): string_vec
|
|||
for ( i in parts )
|
||||
{
|
||||
if ( i % 2 == 1 && is_valid_ip(parts[i]) )
|
||||
output[|output|] = parts[i];
|
||||
output += parts[i];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ function extract_email_addrs_vec(str: string): string_vec
|
|||
|
||||
local raw_addrs = find_all(str, /(^|[<,:[:blank:]])[^<,:[:blank:]@]+"@"[^>,;[:blank:]]+([>,;[:blank:]]|$)/);
|
||||
for ( raw_addr in raw_addrs )
|
||||
addrs[|addrs|] = gsub(raw_addr, /[<>,:;[:blank:]]/, "");
|
||||
addrs += gsub(raw_addr, /[<>,:;[:blank:]]/, "");
|
||||
|
||||
return addrs;
|
||||
}
|
||||
|
|
|
@ -69,14 +69,14 @@ event Exec::line(description: Input::EventDescription, tpe: Input::Event, s: str
|
|||
if ( ! result?$stderr )
|
||||
result$stderr = vector(s);
|
||||
else
|
||||
result$stderr[|result$stderr|] = s;
|
||||
result$stderr += s;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! result?$stdout )
|
||||
result$stdout = vector(s);
|
||||
else
|
||||
result$stdout[|result$stdout|] = s;
|
||||
result$stdout += s;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ event Exec::file_line(description: Input::EventDescription, tpe: Input::Event, s
|
|||
if ( track_file !in result$files )
|
||||
result$files[track_file] = vector(s);
|
||||
else
|
||||
result$files[track_file][|result$files[track_file]|] = s;
|
||||
result$files[track_file] += s;
|
||||
}
|
||||
|
||||
event Input::end_of_data(orig_name: string, source:string)
|
||||
|
|
76
scripts/base/utils/hash_hrw.bro
Normal file
76
scripts/base/utils/hash_hrw.bro
Normal file
|
@ -0,0 +1,76 @@
|
|||
##! An implementation of highest random weight (HRW) hashing, also called
|
||||
##! rendezvous hashing. See
|
||||
##! `<https://en.wikipedia.org/wiki/Rendezvous_hashing>`_.
|
||||
|
||||
module HashHRW;
|
||||
|
||||
export {
|
||||
## A site/node is a unique location to which you want a subset of keys
|
||||
## to be distributed.
|
||||
type Site: record {
|
||||
## A unique identifier for the site, should not exceed what
|
||||
## can be contained in a 32-bit integer.
|
||||
id: count;
|
||||
## Other data to associate with the site.
|
||||
user_data: any &optional;
|
||||
};
|
||||
|
||||
## A table of sites, indexed by their id.
|
||||
type SiteTable: table[count] of Site;
|
||||
|
||||
## A collection of sites to distribute keys across.
|
||||
type Pool: record {
|
||||
sites: SiteTable &default=SiteTable();
|
||||
};
|
||||
|
||||
## Add a site to a pool.
|
||||
##
|
||||
## Returns: F is the site is already in the pool, else T.
|
||||
global add_site: function(pool: Pool, site: Site): bool;
|
||||
|
||||
## Remove a site from a pool.
|
||||
##
|
||||
## Returns: F if the site is not in the pool, else T.
|
||||
global rem_site: function(pool: Pool, site: Site): bool;
|
||||
|
||||
## Returns: the site to which the key maps.
|
||||
global get_site: function(pool: Pool, key: any): Site;
|
||||
}
|
||||
|
||||
function add_site(pool: Pool, site: Site): bool
|
||||
{
|
||||
if ( site$id in pool$sites )
|
||||
return F;
|
||||
|
||||
pool$sites[site$id] = site;
|
||||
return T;
|
||||
}
|
||||
|
||||
function rem_site(pool: Pool, site: Site): bool
|
||||
{
|
||||
if ( site$id !in pool$sites )
|
||||
return F;
|
||||
|
||||
delete pool$sites[site$id];
|
||||
return T;
|
||||
}
|
||||
|
||||
function get_site(pool: Pool, key: any): Site
|
||||
{
|
||||
local best_site_id = 0;
|
||||
local best_weight = -1;
|
||||
local d = fnv1a32(key);
|
||||
|
||||
for ( site_id in pool$sites )
|
||||
{
|
||||
local w = hrw_weight(d, site_id);
|
||||
|
||||
if ( w > best_weight || (w == best_weight && site_id > best_site_id) )
|
||||
{
|
||||
best_weight = w;
|
||||
best_site_id = site_id;
|
||||
}
|
||||
}
|
||||
|
||||
return pool$sites[best_site_id];
|
||||
}
|
|
@ -25,6 +25,10 @@ function to_json(v: any, only_loggable: bool &default=F, field_escape_pattern: p
|
|||
case "port":
|
||||
return cat(port_to_count(to_port(cat(v))));
|
||||
|
||||
case "enum":
|
||||
fallthrough;
|
||||
case "interval":
|
||||
fallthrough;
|
||||
case "addr":
|
||||
fallthrough;
|
||||
case "subnet":
|
||||
|
@ -35,14 +39,15 @@ function to_json(v: any, only_loggable: bool &default=F, field_escape_pattern: p
|
|||
case "count":
|
||||
fallthrough;
|
||||
case "time":
|
||||
fallthrough;
|
||||
case "double":
|
||||
fallthrough;
|
||||
case "bool":
|
||||
fallthrough;
|
||||
case "enum":
|
||||
return cat(v);
|
||||
|
||||
case "double":
|
||||
return fmt("%.16g", v);
|
||||
|
||||
case "bool":
|
||||
local bval: bool = v;
|
||||
return bval ? "true" : "false";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -61,7 +66,7 @@ function to_json(v: any, only_loggable: bool &default=F, field_escape_pattern: p
|
|||
if ( field_desc?$value && (!only_loggable || field_desc$log) )
|
||||
{
|
||||
local onepart = cat("\"", field, "\": ", to_json(field_desc$value, only_loggable));
|
||||
rec_parts[|rec_parts|] = onepart;
|
||||
rec_parts += onepart;
|
||||
}
|
||||
}
|
||||
return cat("{", join_string_vec(rec_parts, ", "), "}");
|
||||
|
@ -74,7 +79,7 @@ function to_json(v: any, only_loggable: bool &default=F, field_escape_pattern: p
|
|||
local sa: set[bool] = v;
|
||||
for ( sv in sa )
|
||||
{
|
||||
set_parts[|set_parts|] = to_json(sv, only_loggable);
|
||||
set_parts += to_json(sv, only_loggable);
|
||||
}
|
||||
return cat("[", join_string_vec(set_parts, ", "), "]");
|
||||
}
|
||||
|
@ -86,7 +91,7 @@ function to_json(v: any, only_loggable: bool &default=F, field_escape_pattern: p
|
|||
{
|
||||
local ts = to_json(ti);
|
||||
local if_quotes = (ts[0] == "\"") ? "" : "\"";
|
||||
tab_parts[|tab_parts|] = cat(if_quotes, ts, if_quotes, ": ", to_json(ta[ti], only_loggable));
|
||||
tab_parts += cat(if_quotes, ts, if_quotes, ": ", to_json(ta[ti], only_loggable));
|
||||
}
|
||||
return cat("{", join_string_vec(tab_parts, ", "), "}");
|
||||
}
|
||||
|
@ -96,7 +101,7 @@ function to_json(v: any, only_loggable: bool &default=F, field_escape_pattern: p
|
|||
local va: vector of any = v;
|
||||
for ( vi in va )
|
||||
{
|
||||
vec_parts[|vec_parts|] = to_json(va[vi], only_loggable);
|
||||
vec_parts += to_json(va[vi], only_loggable);
|
||||
}
|
||||
return cat("[", join_string_vec(vec_parts, ", "), "]");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue