From: Michael Altizer (mialtize) Date: Tue, 11 Oct 2016 16:43:23 +0000 (-0400) Subject: Merge pull request #664 in SNORT/snort3 from dce_udp_processing to master X-Git-Tag: 3.0.0-233~228 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9a99bf1d118442d6eddb6bd61217f492e58b2b45;p=thirdparty%2Fsnort3.git Merge pull request #664 in SNORT/snort3 from dce_udp_processing to master Squashed commit of the following: commit 83de4f6329512279a3c41924ce6260cf0e903c5a Author: mdagon Date: Tue Oct 11 08:30:04 2016 -0400 Code review comments commit 945cab36b195269ff5eebd7f6ea9cbbd8de0d99b Author: mdagon Date: Fri Sep 30 15:42:39 2016 -0400 dce_udp packet processing --- diff --git a/src/service_inspectors/dce_rpc/CMakeLists.txt b/src/service_inspectors/dce_rpc/CMakeLists.txt index 4f560a6b4..32bcb2836 100644 --- a/src/service_inspectors/dce_rpc/CMakeLists.txt +++ b/src/service_inspectors/dce_rpc/CMakeLists.txt @@ -32,6 +32,7 @@ set( FILE_LIST dce_udp.h dce_udp_module.cc dce_udp_module.h + dce_udp_processing.cc dce_utils.cc dce_utils.h ips_dce_iface.cc diff --git a/src/service_inspectors/dce_rpc/Makefile.am b/src/service_inspectors/dce_rpc/Makefile.am index 5d563eeb7..3bd481622 100644 --- a/src/service_inspectors/dce_rpc/Makefile.am +++ b/src/service_inspectors/dce_rpc/Makefile.am @@ -31,6 +31,7 @@ dce_udp.cc\ dce_udp.h \ dce_udp_module.cc \ dce_udp_module.h \ +dce_udp_processing.cc \ dce_utils.cc \ dce_utils.h \ ips_dce_iface.cc \ diff --git a/src/service_inspectors/dce_rpc/dce_common.cc b/src/service_inspectors/dce_rpc/dce_common.cc index a4b372907..9b5d8c478 100644 --- a/src/service_inspectors/dce_rpc/dce_common.cc +++ b/src/service_inspectors/dce_rpc/dce_common.cc @@ -27,6 +27,7 @@ #include "dce_smb_utils.h" #include "dce_tcp.h" +#include "dce_udp.h" THREAD_LOCAL int dce2_detected = 0; THREAD_LOCAL DCE2_CStack* dce2_pkt_stack = nullptr; @@ -177,11 +178,15 @@ static void dce2_protocol_detect(DCE2_SsnData* sd, Packet* pkt) { Profile profile(dce2_tcp_pstat_detect); } - else + else if (sd->trans == DCE2_TRANS_TYPE__SMB) { Profile profile(dce2_smb_pstat_detect); - } - // FIXIT-M add HTTP, UDP cases when these are ported + } + else + { + Profile profile(dce2_udp_pstat_detect); + } + // FIXIT-M add HTTP case when these are ported // Same for all other instances of profiling SnortEventqPush(); @@ -234,7 +239,14 @@ DCE2_SsnData* get_dce2_session_data(Packet* p) return sd; } - // FIXIT-L add checks for http, udp once ported + DCE2_UdpSsnData* udp_data = get_dce2_udp_session_data(p->flow); + sd = (udp_data != nullptr) ? &(udp_data->sd) : nullptr; + if ((sd != nullptr) && (sd->trans == DCE2_TRANS_TYPE__UDP)) + { + return sd; + } + + // FIXIT-L add checks for http once ported return nullptr; } @@ -299,10 +311,14 @@ static void dce_push_pkt_log(Packet* pkt,DCE2_SsnData* sd) { Profile profile(dce2_tcp_pstat_log); } - else + else if (sd->trans == DCE2_TRANS_TYPE__SMB) { Profile profile(dce2_smb_pstat_log); } + else + { + Profile profile(dce2_udp_pstat_log); + } SnortEventqPush(); SnortEventqLog(pkt); @@ -334,6 +350,10 @@ void DCE2_PopPkt(DCE2_SsnData* sd) if (sd->trans == DCE2_TRANS_TYPE__TCP) { Profile profile(dce2_tcp_pstat_log); + } + else if (sd->trans == DCE2_TRANS_TYPE__UDP) + { + Profile profile(dce2_udp_pstat_log); } else { diff --git a/src/service_inspectors/dce_rpc/dce_udp.cc b/src/service_inspectors/dce_rpc/dce_udp.cc index 284e3c176..07b54562d 100644 --- a/src/service_inspectors/dce_rpc/dce_udp.cc +++ b/src/service_inspectors/dce_rpc/dce_udp.cc @@ -206,10 +206,30 @@ void Dce2Udp::eval(Packet* p) if (dce2_udp_sess) { - dce2_udp_stats.udp_pkts++; + if (DCE2_PushPkt(p,&dce2_udp_sess->sd) != DCE2_RET__SUCCESS) + { + DebugMessage(DEBUG_DCE_UDP, "Failed to push packet onto packet stack.\n"); + return; + } + p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT; + dce2_detected = 0; + + p->endianness = (Endianness*)new DceEndianness(); + + dce2_udp_stats.udp_pkts++; + DCE2_ClProcess(&dce2_udp_sess->sd, &dce2_udp_sess->cl_tracker); + + if (!dce2_detected) + DCE2_Detect(&dce2_udp_sess->sd); + + DCE2_ResetRopts(&dce2_udp_sess->sd.ropts); + DCE2_PopPkt(&dce2_udp_sess->sd); if (!DCE2_SsnAutodetected(&dce2_udp_sess->sd)) DisableInspection(); + + delete p->endianness; + p->endianness = nullptr; } } @@ -247,12 +267,25 @@ static void dce2_udp_init() static void dce2_udp_thread_init() { + if (dce2_inspector_instances == 0) + { + dce2_pkt_stack = DCE2_CStackNew(DCE2_PKT_STACK__SIZE, nullptr); + } + dce2_udp_inspector_instances++; + dce2_inspector_instances++; } static void dce2_udp_thread_term() { + dce2_inspector_instances--; dce2_udp_inspector_instances--; + + if (dce2_inspector_instances == 0) + { + DCE2_CStackDestroy(dce2_pkt_stack); + dce2_pkt_stack = nullptr; + } } const InspectApi dce2_udp_api = diff --git a/src/service_inspectors/dce_rpc/dce_udp.h b/src/service_inspectors/dce_rpc/dce_udp.h index bfefeafa1..a2f6fd528 100644 --- a/src/service_inspectors/dce_rpc/dce_udp.h +++ b/src/service_inspectors/dce_rpc/dce_udp.h @@ -42,7 +42,6 @@ struct dce2UdpStats /*DCE UDP specific*/ PegCount udp_sessions; PegCount udp_pkts; - PegCount cl_pkts; PegCount cl_request; PegCount cl_ack; PegCount cl_cancel; @@ -97,6 +96,18 @@ struct DceRpcClHdr /* Connectionless header */ }; +enum DceRpcClFlags1 +{ + DCERPC_CL_FLAGS1__RESERVED_01 = 0x01, + DCERPC_CL_FLAGS1__LASTFRAG = 0x02, + DCERPC_CL_FLAGS1__FRAG = 0x04, + DCERPC_CL_FLAGS1__NOFACK = 0x08, + DCERPC_CL_FLAGS1__MAYBE = 0x10, + DCERPC_CL_FLAGS1__IDEMPOTENT = 0x20, + DCERPC_CL_FLAGS1__BROADCAST = 0x40, + DCERPC_CL_FLAGS1__RESERVED_80 = 0x80 +}; + inline uint8_t DceRpcClRpcVers(const DceRpcClHdr *cl) { return cl->rpc_vers; @@ -117,6 +128,59 @@ inline uint16_t DceRpcClLen(const DceRpcClHdr *cl) return DceRpcNtohs(&cl->len, DceRpcClByteOrder(cl)); } +inline uint16_t DceRpcClOpnum(const DceRpcClHdr *cl) +{ + return DceRpcNtohs(&cl->opnum, DceRpcClByteOrder(cl)); +} + +inline uint32_t DceRpcClSeqNum(const DceRpcClHdr *cl) +{ + return DceRpcNtohl(&cl->seqnum, DceRpcClByteOrder(cl)); +} + +inline const Uuid * DceRpcClIface(const DceRpcClHdr *cl) +{ + return &cl->if_id; +} + +inline uint32_t DceRpcClIfaceVers(const DceRpcClHdr *cl) +{ + return DceRpcNtohl(&cl->if_vers, DceRpcClByteOrder(cl)); +} + +inline uint16_t DceRpcClFragNum(const DceRpcClHdr *cl) +{ + return DceRpcNtohs(&cl->fragnum, DceRpcClByteOrder(cl)); +} + +inline int DceRpcClFragFlag(const DceRpcClHdr *cl) +{ + return cl->flags1 & DCERPC_CL_FLAGS1__FRAG; +} + +inline bool DceRpcClFirstFrag(const DceRpcClHdr *cl) +{ + return (DceRpcClFragFlag(cl) && (DceRpcClFragNum(cl) == 0)); +} + +inline int DceRpcClLastFrag(const DceRpcClHdr *cl) +{ + return cl->flags1 & DCERPC_CL_FLAGS1__LASTFRAG; +} + +inline bool DceRpcClFrag(const DceRpcClHdr *cl) +{ + if (DceRpcClFragFlag(cl)) + { + if (DceRpcClLastFrag(cl) && (DceRpcClFragNum(cl) == 0)) + return false; + + return true; + } + + return false; +} + struct DCE2_ClTracker { DCE2_List* act_trackers; /* List of activity trackers */ @@ -139,12 +203,13 @@ public: flow_id = FlowData::get_flow_id(); } -public: static unsigned flow_id; DCE2_UdpSsnData dce2_udp_session; }; DCE2_UdpSsnData* get_dce2_udp_session_data(Flow*); +void DCE2_ClProcess(DCE2_SsnData *sd, DCE2_ClTracker *clt); + #endif diff --git a/src/service_inspectors/dce_rpc/dce_udp_processing.cc b/src/service_inspectors/dce_rpc/dce_udp_processing.cc new file mode 100644 index 000000000..ec0211ed4 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_udp_processing.cc @@ -0,0 +1,379 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved. +// Copyright (C) 2008-2013 Sourcefire, Inc. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// dce_udp_processing.cc author Todd Wease +// modifications for snort3 by Maya Dagon + +// Module for handling connectionless DCE/RPC processing. Provides +// functionality for tracking sub-sessions or activities within a +// connectionless conversation and for tracking and reassembling fragments +// within each activity. Also sets appropriate data for use with +// preprocessor rule options. + +#include "dce_udp.h" + +#include "flow/session.h" +#include "main/snort_debug.h" +#include "utils/util.h" + +#include "dce_common.h" +#include "dce_udp_module.h" + +/******************************************************************** + * Macros + ********************************************************************/ +#define DCE2_CL__MAX_SEQ_NUM UINT32_MAX + +/******************************************************************** + * Structures + ********************************************************************/ +struct DCE2_ClFragTracker +{ + Uuid iface; /* only set on first fragment received */ + uint32_t iface_vers; /* only set on first fragment received */ + int opnum; /* set to that of first fragment, i.e fragment number == 0. + * initialize to a sentinel */ + int data_byte_order; /* set to that of first fragment, i.e fragment number == 0. + * initialize to sentinel */ + + DCE2_List* frags; /* sorted by fragment number */ + int num_expected_frags; /* set when we get last frag */ +}; + +struct DCE2_ClActTracker +{ + Uuid act; + uint32_t seq_num; + uint8_t seq_num_invalid; + + DCE2_ClFragTracker frag_tracker; +}; + +/******************************************************************** + * Private function prototypes + ********************************************************************/ +static DCE2_Ret DCE2_ClHdrChecks(DCE2_SsnData*, const DceRpcClHdr*); +static DCE2_ClActTracker* DCE2_ClGetActTracker(DCE2_ClTracker*, DceRpcClHdr*); +static DCE2_ClActTracker* DCE2_ClInsertActTracker(DCE2_ClTracker*, DceRpcClHdr*); +static void DCE2_ClRequest(DCE2_SsnData*, DCE2_ClActTracker*, DceRpcClHdr*, + const uint8_t*, uint16_t); + +/* Callbacks */ +static void DCE2_ClActDataFree(void*); +static void DCE2_ClActKeyFree(void*); + +// Main entry point for connectionless DCE/RPC processing. Gets +// the activity tracker associated with this session and passes +// along to client or server handling. +void DCE2_ClProcess(DCE2_SsnData* sd, DCE2_ClTracker* clt) +{ + DceRpcClHdr* cl_hdr; + DCE2_ClActTracker* at; + const uint8_t* data_ptr = sd->wire_pkt->data; + uint16_t data_len = sd->wire_pkt->dsize; + + DebugMessage(DEBUG_DCE_UDP, "Cl processing ...\n"); + + if (data_len < sizeof(DceRpcClHdr)) + { + // FIXIT-M currently we always do autodetect. Uncomment once + // detect/autodetect is supported. +/* + if (!DCE2_SsnAutodetected(sd)) + dce_alert(GID_DCE2, DCE2_CL_DATA_LT_HDR, (dce2CommonStats*)&dce2_udp_stats); +*/ + return; + } + + cl_hdr = (DceRpcClHdr*)data_ptr; + + DCE2_MOVE(data_ptr, data_len, sizeof(DceRpcClHdr)); + + if (DCE2_ClHdrChecks(sd, cl_hdr) != DCE2_RET__SUCCESS) + return; + + Profile profile(dce2_udp_pstat_cl_acts); + at = DCE2_ClGetActTracker(clt, cl_hdr); + if (at == nullptr) + return; + + if (DCE2_SsnFromClient(sd->wire_pkt)) + { + switch (DceRpcClPduType(cl_hdr)) + { + case DCERPC_PDU_TYPE__REQUEST: + DebugMessage(DEBUG_DCE_UDP, "Request\n"); + dce2_udp_stats.cl_request++; + DCE2_ClRequest(sd, at, cl_hdr, data_ptr, data_len); + break; + + case DCERPC_PDU_TYPE__ACK: + DebugMessage(DEBUG_DCE_UDP, "Ack\n"); + dce2_udp_stats.cl_ack++; + break; + + case DCERPC_PDU_TYPE__CL_CANCEL: + DebugMessage(DEBUG_DCE_UDP, "Cancel\n"); + dce2_udp_stats.cl_cancel++; + break; + + case DCERPC_PDU_TYPE__FACK: + DebugMessage(DEBUG_DCE_UDP, "Fack\n"); + dce2_udp_stats.cl_cli_fack++; + break; + + case DCERPC_PDU_TYPE__PING: + DebugMessage(DEBUG_DCE_UDP, "Ping\n"); + dce2_udp_stats.cl_ping++; + break; + + case DCERPC_PDU_TYPE__RESPONSE: + { + DebugMessage(DEBUG_DCE_UDP, "Response from client. Changing stream direction."); + Packet* p = sd->wire_pkt; + ip::IpApi* ip_api = &p->ptrs.ip_api; + + p->flow->session->update_direction(SSN_DIR_FROM_SERVER, + ip_api->get_src(), + p->ptrs.sp); + + break; + } + default: + DebugMessage(DEBUG_DCE_UDP, "Other pdu type\n"); + dce2_udp_stats.cl_other_req++; + break; + } + } + else + { + switch (DceRpcClPduType(cl_hdr)) + { + case DCERPC_PDU_TYPE__RESPONSE: + DebugMessage(DEBUG_DCE_UDP, "Response\n"); + dce2_udp_stats.cl_response++; + break; + + case DCERPC_PDU_TYPE__REJECT: + DebugMessage(DEBUG_DCE_UDP, "Reject\n"); + dce2_udp_stats.cl_reject++; + + if (DceRpcClSeqNum(cl_hdr) == at->seq_num) + { + // FIXIT-M uncomment once fragments is ported + //DCE2_ClResetFragTracker(&at->frag_tracker); + at->seq_num_invalid = 1; + } + + break; + + case DCERPC_PDU_TYPE__CANCEL_ACK: + DebugMessage(DEBUG_DCE_UDP, "Cancel Ack\n"); + dce2_udp_stats.cl_cancel_ack++; + break; + + case DCERPC_PDU_TYPE__FACK: + DebugMessage(DEBUG_DCE_UDP, "Fack\n"); + dce2_udp_stats.cl_srv_fack++; + break; + + case DCERPC_PDU_TYPE__FAULT: + DebugMessage(DEBUG_DCE_UDP, "Fault\n"); + dce2_udp_stats.cl_fault++; + break; + + case DCERPC_PDU_TYPE__NOCALL: + DebugMessage(DEBUG_DCE_UDP, "No call\n"); + dce2_udp_stats.cl_nocall++; + break; + + case DCERPC_PDU_TYPE__WORKING: + DebugMessage(DEBUG_DCE_UDP, "Working\n"); + dce2_udp_stats.cl_working++; + break; + + default: + DebugMessage(DEBUG_DCE_UDP, "Other pdu type\n"); + dce2_udp_stats.cl_other_resp++; + break; + } + } +} + +// Checks to make sure header fields are sane. If they aren't, +// alert on the header anomaly. If we've autodetected the session, +// however, don't alert, but set a header anomaly flag, so we can +// re-autodetect on the next go around. +static DCE2_Ret DCE2_ClHdrChecks(DCE2_SsnData*, const DceRpcClHdr* cl_hdr) +{ + if (DceRpcClRpcVers(cl_hdr) != DCERPC_PROTO_MAJOR_VERS__4) + { + // FIXIT-M currently we always do autodetect. Uncomment once + // detect/autodetect is supported. + /* If we autodetected the session, we probably guessed wrong */ + /* if (!DCE2_SsnAutodetected(sd)) + dce_alert(GID_DCE2, DCE2_CL_BAD_MAJOR_VERSION, (dce2CommonStats*)&dce2_udp_stats); + */ + return DCE2_RET__ERROR; + } + + if (DceRpcClPduType(cl_hdr) >= DCERPC_PDU_TYPE__MAX) + { + // FIXIT-M currently we always do autodetect. Uncomment once + // detect/autodetect is supported. +/* + if (!DCE2_SsnAutodetected(sd)) + dce_alert(GID_DCE2, DCE2_CL_BAD_PDU_TYPE, (dce2CommonStats*)&dce2_udp_stats); +*/ + return DCE2_RET__ERROR; + } + + return DCE2_RET__SUCCESS; +} + +// Searches for activity tracker in list using activity UUID in +// packet. +static DCE2_ClActTracker* DCE2_ClGetActTracker(DCE2_ClTracker* clt, DceRpcClHdr* cl_hdr) +{ + DCE2_ClActTracker* at = nullptr; + + /* Try to find a currently active activity tracker */ + if (clt->act_trackers != nullptr) + { + Uuid uuid; + + DCE2_CopyUuid(&uuid, &cl_hdr->act_id, DceRpcClByteOrder(cl_hdr)); + at = (DCE2_ClActTracker*)DCE2_ListFind(clt->act_trackers, (void*)&uuid); + } + else + { + /* Create a new activity tracker list */ + clt->act_trackers = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_UuidCompare, + DCE2_ClActDataFree, DCE2_ClActKeyFree, + DCE2_LIST_FLAG__NO_DUPS); + if (clt->act_trackers == nullptr) + return nullptr; + } + + /* Didn't find a currently active activity tracker */ + if (at == nullptr) + { + /* Insert a new activity tracker */ + at = DCE2_ClInsertActTracker(clt, cl_hdr); + if (at == nullptr) + return nullptr; + } + + return at; +} + +static DCE2_ClActTracker* DCE2_ClInsertActTracker(DCE2_ClTracker* clt, DceRpcClHdr* cl_hdr) +{ + Uuid* uuid = (Uuid*)snort_calloc(sizeof(Uuid)); + DCE2_ClActTracker* at = (DCE2_ClActTracker*)snort_calloc(sizeof(DCE2_ClActTracker)); + + DCE2_CopyUuid(uuid, &cl_hdr->act_id, DceRpcClByteOrder(cl_hdr)); + DCE2_CopyUuid(&at->act, &cl_hdr->act_id, DceRpcClByteOrder(cl_hdr)); + + DCE2_Ret status = DCE2_ListInsert(clt->act_trackers, (void*)uuid, (void*)at); + if (status != DCE2_RET__SUCCESS) + { + snort_free((void*)uuid); + snort_free((void*)at); + return nullptr; + } + + return at; +} + +static void DCE2_ClRequest(DCE2_SsnData* sd, DCE2_ClActTracker* at, DceRpcClHdr* cl_hdr, + const uint8_t*, uint16_t) +{ + const uint32_t seq_num = DceRpcClSeqNum(cl_hdr); + + DebugMessage(DEBUG_DCE_UDP, "Processing Request ...\n"); + + if (seq_num > at->seq_num) + { + /* This is the normal case where the sequence number is incremented + * for each request. Set the new sequence number and mark it valid. */ + at->seq_num = seq_num; + at->seq_num_invalid = 0; + + /* If there are any fragments, the new sequence number invalidates + * all of the frags that might be currently stored. */ + // FIXIT-M uncomment when porting fragments support + // DCE2_ClResetFragTracker(&at->frag_tracker); + } + else if ((seq_num < at->seq_num) || at->seq_num_invalid) + { + return; + } + + DCE2_ResetRopts(&sd->ropts); + + if (!DceRpcClFrag(cl_hdr)) + { + // FIXIT-M add fragments cleanup + + if (seq_num != DCE2_CL__MAX_SEQ_NUM) + { + /* This sequence number is now invalid. 0xffffffff is the end of + * the sequence number space and can be reused */ + at->seq_num_invalid = 1; + } + else + { + /* Got the last sequence number in the sequence number space */ + dce2_udp_stats.cl_max_seqnum++; + } + } // FIXIT-M add else - fragments path + + /* Cache relevant values for rule option processing */ + sd->ropts.first_frag = DceRpcClFirstFrag(cl_hdr); + DCE2_CopyUuid(&sd->ropts.iface, DceRpcClIface(cl_hdr), DceRpcClByteOrder(cl_hdr)); + sd->ropts.iface_vers = DceRpcClIfaceVers(cl_hdr); + sd->ropts.opnum = DceRpcClOpnum(cl_hdr); + sd->ropts.stub_data = (uint8_t*)cl_hdr + sizeof(DceRpcClHdr); + DceEndianness* endianness = (DceEndianness*)sd->wire_pkt->endianness; + endianness->hdr_byte_order = DceRpcClByteOrder(cl_hdr); + endianness->data_byte_order = DceRpcClByteOrder(cl_hdr); + DCE2_Detect(sd); +} + +static void DCE2_ClActDataFree(void* data) +{ + DCE2_ClActTracker* at = (DCE2_ClActTracker*)data; + + if (at == nullptr) + return; + + DCE2_ListDestroy(at->frag_tracker.frags); + at->frag_tracker.frags = nullptr; + snort_free((void*)at); +} + +static void DCE2_ClActKeyFree(void* key) +{ + if (key == nullptr) + return; + + snort_free(key); +} +