Merge remote-tracking branch 'origin/topic/bernhard/software'

* origin/topic/bernhard/software:
  change software framework interface again. At the moment everything should worl.
  start reworking interface of software framework. working apart from detect-webapps.bro, which direcly manipulates a no longer available interface...
  after talking to seth - change host_a field in record back to host.
  forgotten policy files.
  Software framework stores ports for server software.
This commit is contained in:
Seth Hall 2012-02-03 16:17:04 -05:00
commit 2cd88ee4f6
8 changed files with 203 additions and 196 deletions

View file

@ -36,15 +36,17 @@ export {
## The record type that is used for representing and logging software.
type Info: record {
## The time at which the software was detected.
ts: time &log;
ts: time &log &optional;
## The IP address detected running the software.
host: addr &log;
## The Port on which the software is running. Only sensible for server software.
host_p: port &log &optional;
## The type of software detected (e.g. :bro:enum:`HTTP::SERVER`).
software_type: Type &log &default=UNKNOWN;
## Name of the software (e.g. Apache).
name: string &log;
name: string &log &optional;
## Version of the software.
version: Version &log;
version: Version &log &optional;
## The full unparsed version string found because the version parsing
## doesn't always work reliably in all cases and this acts as a
## fallback in the logs.
@ -65,31 +67,12 @@ export {
const asset_tracking = LOCAL_HOSTS &redef;
## Other scripts should call this function when they detect software.
## unparsed_version: This is the full string from which the
## :bro:type:`Software::Info` was extracted.
##
## id: The connection id where the software was discovered.
##
## info: A record representing the software discovered.
##
## Returns: T if the software was logged, F otherwise.
global found: function(id: conn_id, info: Software::Info): bool;
## Take many common software version strings and parse them
## into a sensible :bro:type:`Software::Version` record. There are
## still many cases where scripts may have to have their own specific
## version parsing though.
##
## unparsed_version: The raw version string.
##
## host: The host where the software was discovered.
##
## software_type: The type of software.
##
## Returns: A complete record ready for the :bro:id:`Software::found` function.
global parse: function(unparsed_version: string,
host: addr,
software_type: Type): Info;
global found: function(id: conn_id, info: Info): bool;
## Compare two version records.
##
@ -117,112 +100,23 @@ export {
global log_software: event(rec: Info);
}
event bro_init()
event bro_init() &priority=5
{
Log::create_stream(Software::LOG, [$columns=Info, $ev=log_software]);
}
function parse_mozilla(unparsed_version: string,
host: addr,
software_type: Type): Info
{
local software_name = "<unknown browser>";
local v: Version;
local parts: table[count] of string;
if ( /Opera [0-9\.]*$/ in unparsed_version )
{
software_name = "Opera";
parts = split_all(unparsed_version, /Opera [0-9\.]*$/);
if ( 2 in parts )
v = parse(parts[2], host, software_type)$version;
}
else if ( / MSIE / in unparsed_version )
{
software_name = "MSIE";
if ( /Trident\/4\.0/ in unparsed_version )
v = [$major=8,$minor=0];
else if ( /Trident\/5\.0/ in unparsed_version )
v = [$major=9,$minor=0];
else if ( /Trident\/6\.0/ in unparsed_version )
v = [$major=10,$minor=0];
else
{
parts = split_all(unparsed_version, /MSIE [0-9]{1,2}\.*[0-9]*b?[0-9]*/);
if ( 2 in parts )
v = parse(parts[2], host, software_type)$version;
}
}
else if ( /Version\/.*Safari\// in unparsed_version )
{
software_name = "Safari";
parts = split_all(unparsed_version, /Version\/[0-9\.]*/);
if ( 2 in parts )
{
v = parse(parts[2], host, software_type)$version;
if ( / Mobile\/?.* Safari/ in unparsed_version )
v$addl = "Mobile";
}
}
else if ( /(Firefox|Netscape|Thunderbird)\/[0-9\.]*/ in unparsed_version )
{
parts = split_all(unparsed_version, /(Firefox|Netscape|Thunderbird)\/[0-9\.]*/);
if ( 2 in parts )
{
local tmp_s = parse(parts[2], host, software_type);
software_name = tmp_s$name;
v = tmp_s$version;
}
}
else if ( /Chrome\/.*Safari\// in unparsed_version )
{
software_name = "Chrome";
parts = split_all(unparsed_version, /Chrome\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2], host, software_type)$version;
}
else if ( /^Opera\// in unparsed_version )
{
if ( /Opera M(ini|obi)\// in unparsed_version )
{
parts = split_all(unparsed_version, /Opera M(ini|obi)/);
if ( 2 in parts )
software_name = parts[2];
parts = split_all(unparsed_version, /Version\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2], host, software_type)$version;
else
{
parts = split_all(unparsed_version, /Opera Mini\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2], host, software_type)$version;
}
}
else
{
software_name = "Opera";
parts = split_all(unparsed_version, /Version\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2], host, software_type)$version;
}
}
else if ( /AppleWebKit\/[0-9\.]*/ in unparsed_version )
{
software_name = "Unspecified WebKit";
parts = split_all(unparsed_version, /AppleWebKit\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2], host, software_type)$version;
}
type Description: record {
name: string;
version: Version;
unparsed_version: string;
};
return [$ts=network_time(), $host=host, $name=software_name, $version=v,
$software_type=software_type, $unparsed_version=unparsed_version];
}
# Defining this here because of a circular dependency between two functions.
global parse_mozilla: function(unparsed_version: string): Description;
# Don't even try to understand this now, just make sure the tests are
# working.
function parse(unparsed_version: string,
host: addr,
software_type: Type): Info
function parse(unparsed_version: string): Description
{
local software_name = "<parse error>";
local v: Version;
@ -230,7 +124,7 @@ function parse(unparsed_version: string,
# Parse browser-alike versions separately
if ( /^(Mozilla|Opera)\/[0-9]\./ in unparsed_version )
{
return parse_mozilla(unparsed_version, host, software_type);
return parse_mozilla(unparsed_version);
}
else
{
@ -255,7 +149,7 @@ function parse(unparsed_version: string,
if ( 4 in version_numbers && version_numbers[4] != "" )
v$addl = strip(version_numbers[4]);
else if ( 3 in version_parts && version_parts[3] != "" &&
version_parts[3] != ")" )
version_parts[3] != ")" )
{
if ( /^[[:blank:]]*\([a-zA-Z0-9\-\._[:blank:]]*\)/ in version_parts[3] )
{
@ -292,9 +186,102 @@ function parse(unparsed_version: string,
v$major = extract_count(version_numbers[1]);
}
}
return [$ts=network_time(), $host=host, $name=software_name,
$version=v, $unparsed_version=unparsed_version,
$software_type=software_type];
return [$version=v, $unparsed_version=unparsed_version, $name=software_name];
}
function parse_mozilla(unparsed_version: string): Description
{
local software_name = "<unknown browser>";
local v: Version;
local parts: table[count] of string;
if ( /Opera [0-9\.]*$/ in unparsed_version )
{
software_name = "Opera";
parts = split_all(unparsed_version, /Opera [0-9\.]*$/);
if ( 2 in parts )
v = parse(parts[2])$version;
}
else if ( / MSIE / in unparsed_version )
{
software_name = "MSIE";
if ( /Trident\/4\.0/ in unparsed_version )
v = [$major=8,$minor=0];
else if ( /Trident\/5\.0/ in unparsed_version )
v = [$major=9,$minor=0];
else if ( /Trident\/6\.0/ in unparsed_version )
v = [$major=10,$minor=0];
else
{
parts = split_all(unparsed_version, /MSIE [0-9]{1,2}\.*[0-9]*b?[0-9]*/);
if ( 2 in parts )
v = parse(parts[2])$version;
}
}
else if ( /Version\/.*Safari\// in unparsed_version )
{
software_name = "Safari";
parts = split_all(unparsed_version, /Version\/[0-9\.]*/);
if ( 2 in parts )
{
v = parse(parts[2])$version;
if ( / Mobile\/?.* Safari/ in unparsed_version )
v$addl = "Mobile";
}
}
else if ( /(Firefox|Netscape|Thunderbird)\/[0-9\.]*/ in unparsed_version )
{
parts = split_all(unparsed_version, /(Firefox|Netscape|Thunderbird)\/[0-9\.]*/);
if ( 2 in parts )
{
local tmp_s = parse(parts[2]);
software_name = tmp_s$name;
v = tmp_s$version;
}
}
else if ( /Chrome\/.*Safari\// in unparsed_version )
{
software_name = "Chrome";
parts = split_all(unparsed_version, /Chrome\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2])$version;
}
else if ( /^Opera\// in unparsed_version )
{
if ( /Opera M(ini|obi)\// in unparsed_version )
{
parts = split_all(unparsed_version, /Opera M(ini|obi)/);
if ( 2 in parts )
software_name = parts[2];
parts = split_all(unparsed_version, /Version\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2])$version;
else
{
parts = split_all(unparsed_version, /Opera Mini\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2])$version;
}
}
else
{
software_name = "Opera";
parts = split_all(unparsed_version, /Version\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2])$version;
}
}
else if ( /AppleWebKit\/[0-9\.]*/ in unparsed_version )
{
software_name = "Unspecified WebKit";
parts = split_all(unparsed_version, /AppleWebKit\/[0-9\.]*/);
if ( 2 in parts )
v = parse(parts[2])$version;
}
return [$version=v, $unparsed_version=unparsed_version, $name=software_name];
}
@ -407,6 +394,30 @@ 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 software_register(id, info);
return T;
}