zeek/src/analyzer/protocol/rpc/Portmap.cc
Jon Siwek c44cbe1feb Prefix #includes of .bif.h files with zeek/
This enables locating the headers within the install-tree using the
dirs provided by `zeek-config --include_dir`.

To enable locating these headers within the build-tree, this change also
creates a 'build/src/include/zeek -> ..' symlink.
2021-02-02 19:15:05 -08:00

318 lines
6.7 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include "zeek/zeek-config.h"
#include "zeek/analyzer/protocol/rpc/Portmap.h"
#include "zeek/NetVar.h"
#include "zeek/Event.h"
#include "zeek/analyzer/protocol/rpc/XDR.h"
#include "zeek/analyzer/protocol/rpc/events.bif.h"
#define PMAPPROC_NULL 0
#define PMAPPROC_SET 1
#define PMAPPROC_UNSET 2
#define PMAPPROC_GETPORT 3
#define PMAPPROC_DUMP 4
#define PMAPPROC_CALLIT 5
namespace zeek::analyzer::rpc {
namespace detail {
bool PortmapperInterp::RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n)
{
if ( c->Program() != 100000 )
Weird("bad_RPC_program");
switch ( c->Proc() ) {
case PMAPPROC_NULL:
break;
case PMAPPROC_SET:
{
auto m = ExtractMapping(buf, n);
if ( ! m )
return false;
c->AddVal(std::move(m));
}
break;
case PMAPPROC_UNSET:
{
auto m = ExtractMapping(buf, n);
if ( ! m )
return false;
c->AddVal(std::move(m));
}
break;
case PMAPPROC_GETPORT:
{
auto pr = ExtractPortRequest(buf, n);
if ( ! pr )
return false;
c->AddVal(std::move(pr));
}
break;
case PMAPPROC_DUMP:
break;
case PMAPPROC_CALLIT:
{
auto call_it = ExtractCallItRequest(buf, n);
if ( ! call_it )
return false;
c->AddVal(std::move(call_it));
}
break;
default:
return false;
}
return true;
}
bool PortmapperInterp::RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status status,
const u_char*& buf, int& n,
double start_time, double last_time,
int reply_len)
{
EventHandlerPtr event;
ValPtr reply;
int success = (status == BifEnum::RPC_SUCCESS);
switch ( c->Proc() ) {
case PMAPPROC_NULL:
event = success ? pm_request_null : pm_attempt_null;
break;
case PMAPPROC_SET:
if ( success )
{
uint32_t status = extract_XDR_uint32(buf, n);
if ( ! buf )
return false;
reply = val_mgr->Bool(status);
event = pm_request_set;
}
else
event = pm_attempt_set;
break;
case PMAPPROC_UNSET:
if ( success )
{
uint32_t status = extract_XDR_uint32(buf, n);
if ( ! buf )
return false;
reply = val_mgr->Bool(status);
event = pm_request_unset;
}
else
event = pm_attempt_unset;
break;
case PMAPPROC_GETPORT:
if ( success )
{
uint32_t port = extract_XDR_uint32(buf, n);
if ( ! buf )
return false;
RecordVal* rv = c->RequestVal()->AsRecordVal();
const auto& is_tcp = rv->GetField(2);
reply = val_mgr->Port(CheckPort(port), is_tcp->IsOne() ?
TRANSPORT_TCP : TRANSPORT_UDP);
event = pm_request_getport;
}
else
event = pm_attempt_getport;
break;
case PMAPPROC_DUMP:
event = success ? pm_request_dump : pm_attempt_dump;
if ( success )
{
static auto pm_mappings = id::find_type<TableType>("pm_mappings");
auto mappings = make_intrusive<TableVal>(pm_mappings);
uint32_t nmap = 0;
// Each call in the loop test pulls the next "opted"
// element to see if there are more mappings.
while ( extract_XDR_uint32(buf, n) && buf )
{
auto m = ExtractMapping(buf, n);
if ( ! m )
break;
auto index = val_mgr->Count(++nmap);
mappings->Assign(std::move(index), std::move(m));
}
if ( ! buf )
return false;
reply = std::move(mappings);
event = pm_request_dump;
}
else
event = pm_attempt_dump;
break;
case PMAPPROC_CALLIT:
if ( success )
{
uint32_t port = extract_XDR_uint32(buf, n);
int reply_n;
const u_char* opaque_reply =
extract_XDR_opaque(buf, n, reply_n);
if ( ! opaque_reply )
return false;
reply = val_mgr->Port(CheckPort(port), TRANSPORT_UDP);
event = pm_request_callit;
}
else
event = pm_attempt_callit;
break;
default:
return false;
}
Event(event, c->TakeRequestVal(), status, std::move(reply));
return true;
}
ValPtr PortmapperInterp::ExtractMapping(const u_char*& buf, int& len)
{
static auto pm_mapping = id::find_type<RecordType>("pm_mapping");
auto mapping = make_intrusive<RecordVal>(pm_mapping);
mapping->Assign(0, val_mgr->Count(extract_XDR_uint32(buf, len)));
mapping->Assign(1, val_mgr->Count(extract_XDR_uint32(buf, len)));
bool is_tcp = extract_XDR_uint32(buf, len) == IPPROTO_TCP;
uint32_t port = extract_XDR_uint32(buf, len);
mapping->Assign(2, val_mgr->Port(CheckPort(port), is_tcp ? TRANSPORT_TCP : TRANSPORT_UDP));
if ( ! buf )
return nullptr;
return mapping;
}
ValPtr PortmapperInterp::ExtractPortRequest(const u_char*& buf, int& len)
{
static auto pm_port_request = id::find_type<RecordType>("pm_port_request");
auto pr = make_intrusive<RecordVal>(pm_port_request);
pr->Assign(0, val_mgr->Count(extract_XDR_uint32(buf, len)));
pr->Assign(1, val_mgr->Count(extract_XDR_uint32(buf, len)));
bool is_tcp = extract_XDR_uint32(buf, len) == IPPROTO_TCP;
pr->Assign(2, val_mgr->Bool(is_tcp));
(void) extract_XDR_uint32(buf, len); // consume the bogus port
if ( ! buf )
return nullptr;
return pr;
}
ValPtr PortmapperInterp::ExtractCallItRequest(const u_char*& buf, int& len)
{
static auto pm_callit_request = id::find_type<RecordType>("pm_callit_request");
auto c = make_intrusive<RecordVal>(pm_callit_request);
c->Assign(0, val_mgr->Count(extract_XDR_uint32(buf, len)));
c->Assign(1, val_mgr->Count(extract_XDR_uint32(buf, len)));
c->Assign(2, val_mgr->Count(extract_XDR_uint32(buf, len)));
int arg_n;
(void) extract_XDR_opaque(buf, len, arg_n);
c->Assign(3, val_mgr->Count(arg_n));
if ( ! buf )
return nullptr;
return c;
}
uint32_t PortmapperInterp::CheckPort(uint32_t port)
{
if ( port >= 65536 )
{
if ( pm_bad_port )
{
analyzer->EnqueueConnEvent(pm_bad_port,
analyzer->ConnVal(),
val_mgr->Count(port)
);
}
port = 0;
}
return port;
}
void PortmapperInterp::Event(EventHandlerPtr f, ValPtr request, BifEnum::rpc_status status, ValPtr reply)
{
if ( ! f )
return;
Args vl;
vl.emplace_back(analyzer->ConnVal());
if ( status == BifEnum::RPC_SUCCESS )
{
if ( request )
vl.emplace_back(std::move(request));
if ( reply )
vl.emplace_back(std::move(reply));
}
else
{
vl.emplace_back(BifType::Enum::rpc_status->GetEnumVal(status));
if ( request )
vl.emplace_back(std::move(request));
}
analyzer->EnqueueConnEvent(f, std::move(vl));
}
} // namespace detail
Portmapper_Analyzer::Portmapper_Analyzer(Connection* conn)
: RPC_Analyzer("PORTMAPPER", conn, new detail::PortmapperInterp(this))
{
orig_rpc = resp_rpc = nullptr;
}
Portmapper_Analyzer::~Portmapper_Analyzer()
{
}
void Portmapper_Analyzer::Init()
{
RPC_Analyzer::Init();
if ( Conn()->ConnTransport() == TRANSPORT_TCP )
{
orig_rpc = new Contents_RPC(Conn(), true, interp);
resp_rpc = new Contents_RPC(Conn(), false, interp);
AddSupportAnalyzer(orig_rpc);
AddSupportAnalyzer(resp_rpc);
}
}
} // namespace zeek::analyzer::rpc