zeek/src/analyzer/protocol/snmp/snmp-analyzer.pac
2020-04-16 16:46:36 -07:00

500 lines
15 KiB
JavaScript

%extern{
#include <cstdlib>
#include <vector>
#include <string>
#include "net_util.h"
#include "util.h"
%}
%header{
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{
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(ntohl(network_order));
}
Val* asn1_obj_to_val(const ASN1Encoding* obj)
{
RecordVal* rval = new RecordVal(BifType::Record::SNMP::ObjectValue);
uint8 tag = obj->meta()->tag();
rval->Assign(0, val_mgr->Count(tag));
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;
}
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, val_mgr->Count(header->version()));
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, val_mgr->Count(flags_byte));
v3->Assign(3, val_mgr->Bool(flags_byte & 0x01));
v3->Assign(4, val_mgr->Bool(flags_byte & 0x02));
v3->Assign(5, val_mgr->Bool(flags_byte & 0x04));
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.is_orig} )
bro_analyzer()->ProtocolConfirmation();
if ( rec->unknown() )
return false;
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);
};