Merge remote-tracking branch 'origin/topic/jsiwek/snmp'

* origin/topic/jsiwek/snmp:
  Add memory leak unit test for SNMP.
  Fix compiler nitpicks from new SNMP code.
  Add SNMP datagram parsing support.

BIT-1142
This commit is contained in:
Robin Sommer 2014-04-08 15:19:21 -07:00
commit cf7e25643e
43 changed files with 2374 additions and 7 deletions

View file

@ -28,6 +28,7 @@ add_subdirectory(ntp)
add_subdirectory(pia)
add_subdirectory(pop3)
add_subdirectory(rpc)
add_subdirectory(snmp)
add_subdirectory(smb)
add_subdirectory(smtp)
add_subdirectory(socks)

View file

@ -0,0 +1,11 @@
include(BroPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR})
bro_plugin_begin(Bro SNMP)
bro_plugin_cc(SNMP.cc Plugin.cc)
bro_plugin_bif(types.bif)
bro_plugin_bif(events.bif)
bro_plugin_pac(snmp.pac snmp-protocol.pac snmp-analyzer.pac)
bro_plugin_end()

View file

@ -0,0 +1,10 @@
#include "plugin/Plugin.h"
#include "SNMP.h"
BRO_PLUGIN_BEGIN(Bro, SNMP)
BRO_PLUGIN_DESCRIPTION("SNMP Analyzer");
BRO_PLUGIN_ANALYZER("SNMP", snmp::SNMP_Analyzer);
BRO_PLUGIN_BIF_FILE(types);
BRO_PLUGIN_BIF_FILE(events);
BRO_PLUGIN_END

View file

@ -0,0 +1,40 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "SNMP.h"
#include "Func.h"
#include "types.bif.h"
#include "events.bif.h"
using namespace analyzer::snmp;
SNMP_Analyzer::SNMP_Analyzer(Connection* conn)
: Analyzer("SNMP", conn)
{
interp = new binpac::SNMP::SNMP_Conn(this);
}
SNMP_Analyzer::~SNMP_Analyzer()
{
delete interp;
}
void SNMP_Analyzer::Done()
{
Analyzer::Done();
Event(udp_session_done);
}
void SNMP_Analyzer::DeliverPacket(int len, const u_char* data, bool orig,
int seq, const IP_Hdr* ip, int caplen)
{
Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen);
try
{
interp->NewData(orig, data, data + len);
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(e.c_msg());
}
}

View file

@ -0,0 +1,31 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef ANALYZER_PROTOCOL_SNMP_SNMP_H
#define ANALYZER_PROTOCOL_SNMP_SNMP_H
#include "snmp_pac.h"
namespace analyzer { namespace snmp {
class SNMP_Analyzer : public analyzer::Analyzer {
public:
SNMP_Analyzer(Connection* conn);
virtual ~SNMP_Analyzer();
virtual void Done();
virtual void DeliverPacket(int len, const u_char* data, bool orig,
int seq, const IP_Hdr* ip, int caplen);
static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn)
{ return new SNMP_Analyzer(conn); }
protected:
binpac::SNMP::SNMP_Conn* interp;
};
} } // namespace analyzer::*
#endif

View file

@ -0,0 +1,165 @@
## An SNMP ``GetRequest-PDU`` message from either :rfc:`1157` or :rfc:`3416`.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## pdu: An SNMP PDU data structure.
event snmp_get_request%(c: connection, is_orig: bool, header: SNMP::Header,
pdu: SNMP::PDU%);
## An SNMP ``GetNextRequest-PDU`` message from either :rfc:`1157` or
## :rfc:`3416`.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## pdu: An SNMP PDU data structure.
event snmp_get_next_request%(c: connection, is_orig: bool,
header: SNMP::Header, pdu: SNMP::PDU%);
## An SNMP ``GetResponse-PDU`` message from :rfc:`1157` or a
## ``Response-PDU`` from :rfc:`3416`.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## pdu: An SNMP PDU data structure.
event snmp_response%(c: connection, is_orig: bool, header: SNMP::Header,
pdu: SNMP::PDU%);
## An SNMP ``SetRequest-PDU`` message from either :rfc:`1157` or :rfc:`3416`.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## pdu: An SNMP PDU data structure.
event snmp_set_request%(c: connection, is_orig: bool, header: SNMP::Header,
pdu: SNMP::PDU%);
## An SNMP ``Trap-PDU`` message from :rfc:`1157`.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## pdu: An SNMP PDU data structure.
event snmp_trap%(c: connection, is_orig: bool, header: SNMP::Header,
pdu: SNMP::TrapPDU%);
## An SNMP ``GetBulkRequest-PDU`` message from :rfc:`3416`.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## pdu: An SNMP PDU data structure.
event snmp_get_bulk_request%(c: connection, is_orig: bool,
header: SNMP::Header, pdu: SNMP::BulkPDU%);
## An SNMP ``InformRequest-PDU`` message from :rfc:`3416`.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## pdu: An SNMP PDU data structure.
event snmp_inform_request%(c: connection, is_orig: bool, header: SNMP::Header,
pdu: SNMP::PDU%);
## An SNMP ``SNMPv2-Trap-PDU`` message from :rfc:`1157`.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## pdu: An SNMP PDU data structure.
event snmp_trapV2%(c: connection, is_orig: bool, header: SNMP::Header,
pdu: SNMP::PDU%);
## An SNMP ``Report-PDU`` message from :rfc:`3416`.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## pdu: An SNMP PDU data structure.
event snmp_report%(c: connection, is_orig: bool, header: SNMP::Header,
pdu: SNMP::PDU%);
## An SNMP PDU message of unknown type.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## tag: The tag of the unknown SNMP PDU.
event snmp_unknown_pdu%(c: connection, is_orig: bool, header: SNMP::Header,
tag: count%);
## An SNMPv3 ``ScopedPDUData`` of unknown type (neither plaintext or
## an encrypted PDU was in the datagram).
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
##
## tag: The tag of the unknown SNMP PDU scope.
event snmp_unknown_scoped_pdu%(c: connection, is_orig: bool,
header: SNMP::Header, tag: count%);
## An SNMPv3 encrypted PDU message.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## header: SNMP version-dependent data that precedes PDU data in the top-level
## SNMP message structure.
event snmp_encrypted_pdu%(c: connection, is_orig: bool, header: SNMP::Header%);
## A datagram with an unknown SNMP version.
##
## c: The connection overwhich the SNMP datagram is sent.
##
## is_orig: The endpoint which sent the SNMP datagram.
##
## version: The value of the unknown SNMP version.
event snmp_unknown_header_version%(c: connection, is_orig: bool,
version: count%);

View file

@ -0,0 +1,598 @@
%extern{
#include <cstdlib>
#include <vector>
#include <string>
#include "net_util.h"
#include "util.h"
%}
%header{
StringVal* asn1_oid_to_val(const ASN1Encoding* oid);
StringVal* asn1_oid_to_val(const ASN1ObjectIdentifier* oid);
Val* asn1_integer_to_val(const ASN1Encoding* i, TypeTag t);
Val* asn1_integer_to_val(const ASN1Integer* i, TypeTag t);
StringVal* asn1_octet_string_to_val(const ASN1Encoding* s);
StringVal* asn1_octet_string_to_val(const ASN1OctetString* s);
AddrVal* network_address_to_val(const ASN1Encoding* na);
AddrVal* network_address_to_val(const NetworkAddress* na);
Val* asn1_obj_to_val(const ASN1Encoding* obj);
RecordVal* build_hdr(const Header* header);
RecordVal* build_hdrV3(const Header* header);
VectorVal* build_bindings(const VarBindList* vbl);
RecordVal* build_pdu(const CommonPDU* pdu);
RecordVal* build_trap_pdu(const TrapPDU* pdu);
RecordVal* build_bulk_pdu(const GetBulkRequestPDU* pdu);
%}
%code{
StringVal* asn1_oid_to_val(const ASN1ObjectIdentifier* oid)
{
return asn1_oid_to_val(oid->encoding());
}
StringVal* asn1_oid_to_val(const ASN1Encoding* oid)
{
vector<uint64> oid_components;
vector<vector<uint8> > subidentifiers;
vector<uint64> subidentifier_values;
vector<uint8> subidentifier;
bytestring const& bs = oid->content();
for ( int i = 0; i < bs.length(); ++i )
{
if ( bs[i] & 0x80 )
subidentifier.push_back(bs[i] & 0x7f);
else
{
subidentifier.push_back(bs[i]);
subidentifiers.push_back(subidentifier);
subidentifier.clear();
}
}
if ( ! subidentifier.empty() || subidentifiers.size() < 1 )
// Underflow.
return new StringVal("");
for ( size_t i = 0; i < subidentifiers.size(); ++i )
{
subidentifier = subidentifiers[i];
uint64 value = 0;
for ( size_t j = 0; j < subidentifier.size(); ++j )
{
uint64 byte = subidentifier[j];
value |= byte << (7 * (subidentifier.size() - (j + 1)));
}
subidentifier_values.push_back(value);
}
string rval;
for ( size_t i = 0; i < subidentifier_values.size(); ++i )
{
char tmp[32];
if ( i > 0 )
{
rval += ".";
snprintf(tmp, sizeof(tmp), "%"PRIu64, subidentifier_values[i]);
rval += tmp;
}
else
{
std::div_t result = std::div(subidentifier_values[i], 40);
snprintf(tmp, sizeof(tmp), "%d", result.quot);
rval += tmp;
rval += ".";
snprintf(tmp, sizeof(tmp), "%d", result.rem);
rval += tmp;
}
}
return new StringVal(rval);
}
Val* asn1_obj_to_val(const ASN1Encoding* obj)
{
RecordVal* rval = new RecordVal(BifType::Record::SNMP::ObjectValue);
uint8 tag = obj->meta()->tag();
rval->Assign(0, new Val(tag, TYPE_COUNT));
switch ( tag ) {
case VARBIND_UNSPECIFIED_TAG:
case VARBIND_NOSUCHOBJECT_TAG:
case VARBIND_NOSUCHINSTANCE_TAG:
case VARBIND_ENDOFMIBVIEW_TAG:
break;
case ASN1_OBJECT_IDENTIFIER_TAG:
rval->Assign(1, asn1_oid_to_val(obj));
break;
case ASN1_INTEGER_TAG:
rval->Assign(2, asn1_integer_to_val(obj, TYPE_INT));
break;
case APP_COUNTER32_TAG:
case APP_UNSIGNED32_TAG:
case APP_TIMETICKS_TAG:
case APP_COUNTER64_TAG:
rval->Assign(3, asn1_integer_to_val(obj, TYPE_COUNT));
break;
case APP_IPADDRESS_TAG:
rval->Assign(4, network_address_to_val(obj));
break;
case ASN1_OCTET_STRING_TAG:
case APP_OPAQUE_TAG:
default:
rval->Assign(5, asn1_octet_string_to_val(obj));
break;
}
return rval;
}
StringVal* asn1_octet_string_to_val(const ASN1OctetString* s)
{
return asn1_octet_string_to_val(s->encoding());
}
StringVal* asn1_octet_string_to_val(const ASN1Encoding* s)
{
bytestring const& bs = s->content();
return new StringVal(bs.length(), reinterpret_cast<const char*>(bs.data()));
}
Val* asn1_integer_to_val(const ASN1Integer* i, TypeTag t)
{
return asn1_integer_to_val(i->encoding(), t);
}
Val* asn1_integer_to_val(const ASN1Encoding* i, TypeTag t)
{
return new Val(binary_to_int64(i->content()), t);
}
AddrVal* network_address_to_val(const NetworkAddress* na)
{
return network_address_to_val(na->encoding());
}
AddrVal* network_address_to_val(const ASN1Encoding* na)
{
bytestring const& bs = na->content();
// IPv6 can probably be presumed to be a octet string of length 16,
// but standards don't seem to currently make any provisions for IPv6,
// so ignore anything that can't be IPv4.
if ( bs.length() != 4 )
return new AddrVal(IPAddr());
const u_char* data = reinterpret_cast<const u_char*>(bs.data());
uint32 network_order = extract_uint32(data);
return new AddrVal(network_order);
}
Val* time_ticks_to_val(const TimeTicks* tt)
{
return asn1_integer_to_val(tt->asn1_integer(), TYPE_COUNT);
}
RecordVal* build_hdr(const Header* header)
{
RecordVal* rv = new RecordVal(BifType::Record::SNMP::Header);
rv->Assign(0, new Val(header->version(), TYPE_COUNT));
switch ( header->version() ) {
case SNMPV1_TAG:
{
RecordVal* v1 = new RecordVal(BifType::Record::SNMP::HeaderV1);
v1->Assign(0, asn1_octet_string_to_val(header->v1()->community()));
rv->Assign(1, v1);
}
break;
case SNMPV2_TAG:
{
RecordVal* v2 = new RecordVal(BifType::Record::SNMP::HeaderV2);
v2->Assign(0, asn1_octet_string_to_val(header->v2()->community()));
rv->Assign(2, v2);
}
break;
case SNMPV3_TAG:
{
rv->Assign(3, build_hdrV3(header));
}
break;
}
return rv;
}
RecordVal* build_hdrV3(const Header* header)
{
RecordVal* v3 = new RecordVal(BifType::Record::SNMP::HeaderV3);
const v3Header* v3hdr = header->v3();
const v3HeaderData* global_data = v3hdr->global_data();
bytestring const& flags = global_data->flags()->encoding()->content();
uint8 flags_byte = flags.length() > 0 ? flags[0] : 0;
v3->Assign(0, asn1_integer_to_val(global_data->id(), TYPE_COUNT));
v3->Assign(1, asn1_integer_to_val(global_data->max_size(),
TYPE_COUNT));
v3->Assign(2, new Val(flags_byte, TYPE_COUNT));
v3->Assign(3, new Val(flags_byte & 0x01, TYPE_BOOL));
v3->Assign(4, new Val(flags_byte & 0x02, TYPE_BOOL));
v3->Assign(5, new Val(flags_byte & 0x04, TYPE_BOOL));
v3->Assign(6, asn1_integer_to_val(global_data->security_model(),
TYPE_COUNT));
v3->Assign(7, asn1_octet_string_to_val(v3hdr->security_parameters()));
if ( v3hdr->next()->tag() == ASN1_SEQUENCE_TAG )
{
const v3ScopedPDU* spdu = v3hdr->plaintext_pdu();
RecordVal* rv = new RecordVal(BifType::Record::SNMP::ScopedPDU_Context);
rv->Assign(0, asn1_octet_string_to_val(spdu->context_engine_id()));
rv->Assign(1, asn1_octet_string_to_val(spdu->context_name()));
v3->Assign(8, rv);
}
return v3;
}
VectorVal* build_bindings(const VarBindList* vbl)
{
VectorVal* vv = new VectorVal(BifType::Vector::SNMP::Bindings);
for ( size_t i = 0; i < vbl->bindings()->size(); ++i )
{
VarBind* vb = (*vbl->bindings())[i];
RecordVal* binding = new RecordVal(BifType::Record::SNMP::Binding);
binding->Assign(0, asn1_oid_to_val(vb->name()->oid()));
binding->Assign(1, asn1_obj_to_val(vb->value()->encoding()));
vv->Assign(i, binding);
}
return vv;
}
RecordVal* build_pdu(const CommonPDU* pdu)
{
RecordVal* rv = new RecordVal(BifType::Record::SNMP::PDU);
rv->Assign(0, asn1_integer_to_val(pdu->request_id(), TYPE_INT));
rv->Assign(1, asn1_integer_to_val(pdu->error_status(), TYPE_INT));
rv->Assign(2, asn1_integer_to_val(pdu->error_index(), TYPE_INT));
rv->Assign(3, build_bindings(pdu->var_bindings()));
return rv;
}
RecordVal* build_trap_pdu(const TrapPDU* pdu)
{
RecordVal* rv = new RecordVal(BifType::Record::SNMP::TrapPDU);
rv->Assign(0, asn1_oid_to_val(pdu->enterprise()));
rv->Assign(1, network_address_to_val(pdu->agent_addr()));
rv->Assign(2, asn1_integer_to_val(pdu->generic_trap(), TYPE_INT));
rv->Assign(3, asn1_integer_to_val(pdu->specific_trap(), TYPE_INT));
rv->Assign(4, time_ticks_to_val(pdu->time_stamp()));
rv->Assign(5, build_bindings(pdu->var_bindings()));
return rv;
}
RecordVal* build_bulk_pdu(const GetBulkRequestPDU* pdu)
{
RecordVal* rv = new RecordVal(BifType::Record::SNMP::BulkPDU);
rv->Assign(0, asn1_integer_to_val(pdu->request_id(), TYPE_INT));
rv->Assign(1, asn1_integer_to_val(pdu->non_repeaters(), TYPE_COUNT));
rv->Assign(2, asn1_integer_to_val(pdu->max_repititions(), TYPE_COUNT));
rv->Assign(3, build_bindings(pdu->var_bindings()));
return rv;
}
%}
refine connection SNMP_Conn += {
function proc_get_request(pdu: GetRequestPDU): bool
%{
if ( ! snmp_get_request )
return false;
BifEvent::generate_snmp_get_request(bro_analyzer(),
bro_analyzer()->Conn(),
${pdu.header.is_orig},
build_hdr(${pdu.header}),
build_pdu(${pdu.pdu}));
return true;
%}
function proc_get_next_request(pdu: GetNextRequestPDU): bool
%{
if ( ! snmp_get_next_request )
return false;
BifEvent::generate_snmp_get_next_request(bro_analyzer(),
bro_analyzer()->Conn(),
${pdu.header.is_orig},
build_hdr(${pdu.header}),
build_pdu(${pdu.pdu}));
return true;
%}
function proc_response(pdu: ResponsePDU): bool
%{
if ( ! snmp_response )
return false;
BifEvent::generate_snmp_response(bro_analyzer(),
bro_analyzer()->Conn(),
${pdu.header.is_orig},
build_hdr(${pdu.header}),
build_pdu(${pdu.pdu}));
return true;
%}
function proc_set_request(pdu: SetRequestPDU): bool
%{
if ( ! snmp_set_request )
return false;
BifEvent::generate_snmp_set_request(bro_analyzer(),
bro_analyzer()->Conn(),
${pdu.header.is_orig},
build_hdr(${pdu.header}),
build_pdu(${pdu.pdu}));
return true;
%}
function proc_trap(pdu: TrapPDU): bool
%{
if ( ! snmp_trap )
return false;
BifEvent::generate_snmp_trap(bro_analyzer(),
bro_analyzer()->Conn(),
${pdu.header.is_orig},
build_hdr(${pdu.header}),
build_trap_pdu(${pdu}));
return true;
%}
function proc_get_bulk_request(pdu: GetBulkRequestPDU): bool
%{
if ( ! snmp_get_bulk_request )
return false;
BifEvent::generate_snmp_get_bulk_request(bro_analyzer(),
bro_analyzer()->Conn(),
${pdu.header.is_orig},
build_hdr(${pdu.header}),
build_bulk_pdu(${pdu}));
return true;
%}
function proc_inform_request(pdu: InformRequestPDU): bool
%{
if ( ! snmp_inform_request )
return false;
BifEvent::generate_snmp_inform_request(bro_analyzer(),
bro_analyzer()->Conn(),
${pdu.header.is_orig},
build_hdr(${pdu.header}),
build_pdu(${pdu.pdu}));
return true;
%}
function proc_v2_trap(pdu: v2TrapPDU): bool
%{
if ( ! snmp_trapV2 )
return false;
BifEvent::generate_snmp_trapV2(bro_analyzer(),
bro_analyzer()->Conn(),
${pdu.header.is_orig},
build_hdr(${pdu.header}),
build_pdu(${pdu.pdu}));
return true;
%}
function proc_report(pdu: ReportPDU): bool
%{
if ( ! snmp_report )
return false;
BifEvent::generate_snmp_report(bro_analyzer(),
bro_analyzer()->Conn(),
${pdu.header.is_orig},
build_hdr(${pdu.header}),
build_pdu(${pdu.pdu}));
return true;
%}
function proc_unknown_version_header(rec: UnknownVersionHeader): bool
%{
if ( ! snmp_unknown_header_version )
return false;
BifEvent::generate_snmp_unknown_header_version(bro_analyzer(),
bro_analyzer()->Conn(),
${rec.header.is_orig},
${rec.header.version});
return true;
%}
function proc_unknown_pdu(rec: UnknownPDU): bool
%{
if ( ! snmp_unknown_pdu )
return false;
BifEvent::generate_snmp_unknown_pdu(bro_analyzer(),
bro_analyzer()->Conn(),
${rec.header.is_orig},
build_hdr(${rec.header}),
${rec.tag});
return true;
%}
function proc_unknown_scoped_pdu(rec: UnknownScopedPDU): bool
%{
if ( ! snmp_unknown_scoped_pdu )
return false;
BifEvent::generate_snmp_unknown_scoped_pdu(bro_analyzer(),
bro_analyzer()->Conn(),
${rec.header.is_orig},
build_hdr(${rec.header}),
${rec.tag});
return true;
%}
function proc_encrypted_pdu(rec: EncryptedPDU): bool
%{
if ( ! snmp_encrypted_pdu )
return false;
BifEvent::generate_snmp_encrypted_pdu(bro_analyzer(),
bro_analyzer()->Conn(),
${rec.header.is_orig},
build_hdr(${rec.header}));
return true;
%}
function proc_header(rec: Header): bool
%{
if ( rec->unknown() )
return false;
bro_analyzer()->ProtocolConfirmation();
return true;
%}
function proc_v3_header_data(rec: v3HeaderData): bool
%{
if ( rec->flags()->encoding()->content().length() == 1 )
return true;
bro_analyzer()->ProtocolViolation("Invalid v3 HeaderData msgFlags");
return false;
%}
function check_tag(rec: ASN1EncodingMeta, expect: uint8): bool
%{
if ( rec->tag() == expect )
return true;
// Unwind now to stop parsing because it's definitely the
// wrong protocol and parsing further could be expensive.
// Upper layer of analyzer will catch and call ProtocolViolation().
throw binpac::Exception(fmt("Got ASN.1 tag %d, expect %d",
rec->tag(), expect));
return false;
%}
function check_int_width(rec: ASN1Integer): bool
%{
int len = rec->encoding()->content().length();
if ( len <= 9 )
// All integers use two's complement form, so an unsigned 64-bit
// integer value can require 9 octets to encode if the highest
// order bit is set.
return true;
throw binpac::Exception(fmt("ASN.1 integer width overflow: %d", len));
return false;
%}
function check_int(rec: ASN1Integer): bool
%{
return check_tag(rec->encoding()->meta(), ASN1_INTEGER_TAG) &&
check_int_width(rec);
%}
};
refine typeattr GetRequestPDU += &let {
proc: bool = $context.connection.proc_get_request(this);
};
refine typeattr GetNextRequestPDU += &let {
proc: bool = $context.connection.proc_get_next_request(this);
};
refine typeattr ResponsePDU += &let {
proc: bool = $context.connection.proc_response(this);
};
refine typeattr SetRequestPDU += &let {
proc: bool = $context.connection.proc_set_request(this);
};
refine typeattr TrapPDU += &let {
proc: bool = $context.connection.proc_trap(this);
};
refine typeattr GetBulkRequestPDU += &let {
proc: bool = $context.connection.proc_get_bulk_request(this);
};
refine typeattr InformRequestPDU += &let {
proc: bool = $context.connection.proc_inform_request(this);
};
refine typeattr v2TrapPDU += &let {
proc: bool = $context.connection.proc_v2_trap(this);
};
refine typeattr ReportPDU += &let {
proc: bool = $context.connection.proc_report(this);
};
refine typeattr UnknownVersionHeader += &let {
proc: bool = $context.connection.proc_unknown_version_header(this);
};
refine typeattr UnknownPDU += &let {
proc: bool = $context.connection.proc_unknown_pdu(this);
};
refine typeattr UnknownScopedPDU += &let {
proc: bool = $context.connection.proc_unknown_scoped_pdu(this);
};
refine typeattr EncryptedPDU += &let {
proc: bool = $context.connection.proc_encrypted_pdu(this);
};
refine typeattr Header += &let {
proc: bool = $context.connection.proc_header(this);
};
refine typeattr v3HeaderData += &let {
proc: bool = $context.connection.proc_v3_header_data(this);
};
refine typeattr NetworkAddress += &let {
valid: bool = $context.connection.check_tag(encoding.meta,
APP_IPADDRESS_TAG);
};
refine typeattr TimeTicks += &let {
valid: bool = $context.connection.check_tag(asn1_integer.meta,
APP_TIMETICKS_TAG);
};
refine typeattr ASN1SequenceMeta += &let {
valid: bool = $context.connection.check_tag(encoding,
ASN1_SEQUENCE_TAG);
};
refine typeattr ASN1Integer += &let {
valid: bool = $context.connection.check_int(this);
};
refine typeattr ASN1OctetString += &let {
valid: bool = $context.connection.check_tag(encoding.meta,
ASN1_OCTET_STRING_TAG);
};
refine typeattr ASN1ObjectIdentifier += &let {
valid: bool = $context.connection.check_tag(encoding.meta,
ASN1_OBJECT_IDENTIFIER_TAG);
};

View file

@ -0,0 +1,272 @@
# SNMPv1: RFC 1157
# SNMPv2: RFC 1901 and 3416
# SNMPv3: RFC 3412
# Variable Bindings use definitions from RFC 1155 (and 3416).
#
# The SNMP protocol uses a well-defined subset of ASN.1 with the
# Basic Encoding Rules (BER). Definite-length encodings are always
# used. Primitive or non-constructor encodings are preferred over
# constructor encodings.
type TopLevelMessage(is_orig: bool) = record {
asn1_sequence_meta: ASN1SequenceMeta;
version: ASN1Integer;
header: Header(version_value, is_orig);
pdu_or_not: case have_plaintext_pdu(header) of {
false -> none: empty;
true -> pdu: PDU_Choice(header);
};
} &let {
version_value: int64 = binary_to_int64(version.encoding.content);
};
############################## SNMP Header Versions
enum SNMP_VersionTag {
SNMPV1_TAG = 0,
SNMPV2_TAG = 1,
SNMPV3_TAG = 3,
};
type Header(version: int64, is_orig: bool) = case version of {
SNMPV1_TAG -> v1: v1Header(this);
SNMPV2_TAG -> v2: v2Header(this);
SNMPV3_TAG -> v3: v3Header(this);
default -> unknown: UnknownVersionHeader(this);
};
function have_plaintext_pdu(header: Header): bool =
case header.version of {
SNMPV1_TAG -> true;
SNMPV2_TAG -> true;
SNMPV3_TAG -> header.v3.next.tag == ASN1_SEQUENCE_TAG;
default -> false;
};
type PDU_Choice(header: Header) = record {
choice: ASN1EncodingMeta;
pdu: PDU(choice.tag, header);
};
type PDU(choice: uint8, header: Header) = case choice of {
default -> unknown: UnknownPDU(choice, header);
};
refine casetype PDU += {
# PDU choices from RFC 1157.
0xa0 -> get_request: GetRequestPDU(header);
0xa1 -> get_next_request: GetNextRequestPDU(header);
0xa2 -> response: ResponsePDU(header);
0xa3 -> set_request: SetRequestPDU(header);
0xa4 -> trap: TrapPDU(header);
};
refine casetype PDU += {
# PDU choices from RFC 3416.
0xa5 -> get_bulk_request: GetBulkRequestPDU(header);
0xa6 -> inform_request: InformRequestPDU(header);
0xa7 -> v2_trap: v2TrapPDU(header);
0xa8 -> report: ReportPDU(header);
};
type v1Header(header: Header) = record {
community: ASN1OctetString;
};
type v2Header(header: Header) = record {
community: ASN1OctetString;
};
type v3Header(header: Header) = record {
global_data: v3HeaderData;
security_parameters: ASN1OctetString;
next: ASN1EncodingMeta;
scoped_pdu_data: case next.tag of {
ASN1_SEQUENCE_TAG -> plaintext_pdu: v3ScopedPDU;
ASN1_OCTET_STRING_TAG -> encrypted_pdu: EncryptedPDU(header);
default -> unknown_pdu: UnknownScopedPDU(next.tag,
header);
};
};
type v3HeaderData = record {
asn1_sequence_meta: ASN1SequenceMeta;
id: ASN1Integer;
max_size: ASN1Integer;
flags: ASN1OctetString;
security_model: ASN1Integer;
};
type v3ScopedPDU = record {
context_engine_id: ASN1OctetString;
context_name: ASN1OctetString;
};
type EncryptedPDU(header: Header) = record {
data: bytestring &restofdata &transient;
};
type UnknownScopedPDU(tag: uint8, header: Header) = record {
data: bytestring &restofdata &transient;
};
type UnknownVersionHeader(header: Header) = record {
data: bytestring &restofdata &transient;
};
############################## SNMP PDUs
type CommonPDU(header: Header) = record {
request_id: ASN1Integer;
error_status: ASN1Integer;
error_index: ASN1Integer;
var_bindings: VarBindList;
};
type GetRequestPDU(header: Header) = record {
pdu: CommonPDU(header);
};
type GetNextRequestPDU(header: Header) = record {
pdu: CommonPDU(header);
};
type ResponsePDU(header: Header) = record {
pdu: CommonPDU(header);
};
type SetRequestPDU(header: Header) = record {
pdu: CommonPDU(header);
};
type TrapPDU(header: Header) = record {
enterprise: ASN1ObjectIdentifier;
agent_addr: NetworkAddress;
generic_trap: ASN1Integer;
specific_trap: ASN1Integer;
time_stamp: TimeTicks;
var_bindings: VarBindList;
};
type GetBulkRequestPDU(header: Header) = record {
request_id: ASN1Integer;
non_repeaters: ASN1Integer;
max_repititions: ASN1Integer;
var_bindings: VarBindList;
};
type InformRequestPDU(header: Header) = record {
pdu: CommonPDU(header);
};
type v2TrapPDU(header: Header) = record {
pdu: CommonPDU(header);
};
type ReportPDU(header: Header) = record {
pdu: CommonPDU(header);
};
type UnknownPDU(tag: uint8, header: Header) = record {
data: bytestring &restofdata &transient;
};
type VarBindList = record {
asn1_sequence_meta: ASN1SequenceMeta;
bindings: VarBind[];
};
type VarBind = record {
asn1_sequence_meta: ASN1SequenceMeta;
name: ObjectName;
value: ObjectSyntax;
};
############################## Variable Binding Encodings (RFC 1155 and 3416)
type ObjectName = record {
oid: ASN1ObjectIdentifier;
};
type ObjectSyntax = record {
encoding: ASN1Encoding; # The tag may be a CHOICE among several;
};
type NetworkAddress = record {
encoding: ASN1Encoding;
};
type TimeTicks = record {
asn1_integer: ASN1Encoding;
};
enum AppSyntaxTypeTag {
APP_IPADDRESS_TAG = 0x40,
APP_COUNTER32_TAG = 0x41,
APP_UNSIGNED32_TAG = 0x42,
APP_TIMETICKS_TAG = 0x43,
APP_OPAQUE_TAG = 0x44,
APP_COUNTER64_TAG = 0x46,
};
enum VarBindNullTag {
VARBIND_UNSPECIFIED_TAG = 0x05,
VARBIND_NOSUCHOBJECT_TAG = 0x80,
VARBIND_NOSUCHINSTANCE_TAG = 0x81,
VARBIND_ENDOFMIBVIEW_TAG = 0x82,
};
############################## ASN.1 Encodings
enum ASN1TypeTag {
ASN1_INTEGER_TAG = 0x02,
ASN1_OCTET_STRING_TAG = 0x04,
ASN1_NULL_TAG = 0x05,
ASN1_OBJECT_IDENTIFIER_TAG = 0x06,
ASN1_SEQUENCE_TAG = 0x30,
};
type ASN1Encoding = record {
meta: ASN1EncodingMeta;
content: bytestring &length = meta.length;
};
type ASN1EncodingMeta = record {
tag: uint8;
len: uint8;
more_len: bytestring &length = long_len ? len & 0x7f : 0;
} &let {
long_len: bool = len & 0x80;
length: uint64 = long_len ? binary_to_int64(more_len) : len & 0x7f;
};
type ASN1SequenceMeta = record {
encoding: ASN1EncodingMeta;
};
type ASN1Integer = record {
encoding: ASN1Encoding;
};
type ASN1OctetString = record {
encoding: ASN1Encoding;
};
type ASN1ObjectIdentifier = record {
encoding: ASN1Encoding;
};
############################## ASN.1 Conversion Functions
function binary_to_int64(bs: bytestring): int64
%{
int64 rval = 0;
for ( int i = 0; i < bs.length(); ++i )
{
uint64 byte = bs[i];
rval |= byte << (8 * (bs.length() - (i + 1)));
}
return rval;
%}

View file

@ -0,0 +1,25 @@
%include binpac.pac
%include bro.pac
%extern{
#include "types.bif.h"
#include "events.bif.h"
%}
analyzer SNMP withcontext {
connection: SNMP_Conn;
flow: SNMP_Flow;
};
connection SNMP_Conn(bro_analyzer: BroAnalyzer) {
upflow = SNMP_Flow(true);
downflow = SNMP_Flow(false);
};
%include snmp-protocol.pac
flow SNMP_Flow(is_orig: bool) {
datagram = TopLevelMessage(is_orig) withcontext(connection, this);
};
%include snmp-analyzer.pac

View file

@ -0,0 +1,17 @@
module SNMP;
type Header: record;
type HeaderV1: record;
type HeaderV2: record;
type HeaderV3: record;
type PDU: record;
type TrapPDU: record;
type BulkPDU: record;
type ScopedPDU_Context: record;
type ObjectValue: record;
type Binding: record;
type Bindings: vector;
module GLOBAL;