From: Russ Combs (rucombs) Date: Wed, 23 Mar 2016 16:41:34 +0000 (-0400) Subject: Merge pull request #362 in SNORT/snort3 from dcefrag to master X-Git-Tag: 3.0.0-233~514 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=364c2ff1bd8d34439568f53e90bb7496ceb6ecee;p=thirdparty%2Fsnort3.git Merge pull request #362 in SNORT/snort3 from dcefrag to master Squashed commit of the following: commit e22bd15dbef6f88cff1d411d940acb748e2d46e6 Author: rrp Date: Wed Mar 23 12:20:19 2016 -0400 Changes to address more comments commit 116950fe5ed12427000ced35a76e05d4f99396ff Author: rrp Date: Tue Mar 22 23:32:37 2016 -0400 address code review comments for DCE fragmentation code commit a6da6b463f004c2314be400e64f3d48c88fbd20e Author: rrp Date: Thu Mar 10 16:34:15 2016 -0500 Changes to support DCE packet fragmentation --- diff --git a/src/events/event_queue.h b/src/events/event_queue.h index 0b4681f62..04f1a93ee 100644 --- a/src/events/event_queue.h +++ b/src/events/event_queue.h @@ -50,7 +50,7 @@ void EventQueueConfigFree(EventQueueConfig*); void SnortEventqNew(EventQueueConfig*); void SnortEventqFree(); -void SnortEventqReset(void); +SO_PUBLIC void SnortEventqReset(void); void SnortEventqResetCounts(void); SO_PUBLIC int SnortEventqLog(struct Packet*); diff --git a/src/service_inspectors/dce_rpc/dce_co.cc b/src/service_inspectors/dce_rpc/dce_co.cc index b7f69549c..72ed8a57b 100644 --- a/src/service_inspectors/dce_rpc/dce_co.cc +++ b/src/service_inspectors/dce_rpc/dce_co.cc @@ -22,6 +22,8 @@ #include "dce_co.h" #include "dce_tcp.h" #include "dce_smb.h" +#include "dce_tcp_module.h" +#include "dce_smb_module.h" #include "dce_list.h" #include "dce_utils.h" #include "profiler/profiler.h" @@ -99,6 +101,103 @@ static inline void DCE2_CoResetTracker(DCE2_CoTracker* cot) DCE2_CoResetFragTracker(&cot->frag_tracker); } +/******************************************************************** + * Function: DCE2_CoCleanTracker() + * + * Destroys all dynamically allocated data associated with + * connection-oriented tracker. + * + ********************************************************************/ +void DCE2_CoCleanTracker(DCE2_CoTracker* cot) +{ + if (cot == nullptr) + return; + + DCE2_BufferDestroy(cot->frag_tracker.cli_stub_buf); + cot->frag_tracker.cli_stub_buf = nullptr; + + DCE2_BufferDestroy(cot->frag_tracker.srv_stub_buf); + cot->frag_tracker.srv_stub_buf = nullptr; + + DCE2_BufferDestroy(cot->cli_seg.buf); + cot->cli_seg.buf = nullptr; + + DCE2_BufferDestroy(cot->srv_seg.buf); + cot->srv_seg.buf = nullptr; + + DCE2_ListDestroy(cot->ctx_ids); + cot->ctx_ids = nullptr; + + DCE2_QueueDestroy(cot->pending_ctx_ids); + cot->pending_ctx_ids = nullptr; + + DCE2_CoInitTracker(cot); +} + +/******************************************************************** + * Function: DCE2_CoSetRdata() + * + * Sets relevant fields in the defragmentation reassembly packet + * based on data gathered from the session and reassembly phase. + * The reassembly buffer used is big enough for the headers. + * + ********************************************************************/ +static inline void DCE2_CoSetRdata(DCE2_SsnData* sd, DCE2_CoTracker* cot, + uint8_t* co_ptr, uint16_t stub_len) +{ + DceRpcCoHdr* co_hdr = (DceRpcCoHdr*)co_ptr; + /* If we've set the fragment tracker context id or opnum, use them. */ + uint16_t ctx_id = + (cot->frag_tracker.ctx_id != DCE2_SENTINEL) ? + (uint16_t)cot->frag_tracker.ctx_id : (uint16_t)cot->ctx_id; + uint16_t opnum = + (cot->frag_tracker.opnum != DCE2_SENTINEL) ? + (uint16_t)cot->frag_tracker.opnum : (uint16_t)cot->opnum; + + if (DCE2_SsnFromClient(sd->wire_pkt)) + { + DceRpcCoRequest* co_req = (DceRpcCoRequest*)((uint8_t*)co_hdr + sizeof(DceRpcCoHdr)); + /* Doesn't really matter if this wraps ... it is basically just for presentation */ + uint16_t flen = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest) + stub_len; + + co_hdr->frag_length = DceRpcHtons(&flen, DCERPC_BO_FLAG__LITTLE_ENDIAN); + co_req->context_id = DceRpcHtons(&ctx_id, DCERPC_BO_FLAG__LITTLE_ENDIAN); + co_req->opnum = DceRpcHtons(&opnum, DCERPC_BO_FLAG__LITTLE_ENDIAN); + } + else + { + DceRpcCoResponse* co_resp = (DceRpcCoResponse*)((uint8_t*)co_hdr + sizeof(DceRpcCoHdr)); + uint16_t flen = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoResponse) + stub_len; + + co_hdr->frag_length = DceRpcHtons(&flen, DCERPC_BO_FLAG__LITTLE_ENDIAN); + co_resp->context_id = DceRpcHtons(&ctx_id, DCERPC_BO_FLAG__LITTLE_ENDIAN); + } +} + +/******************************************************************** + * Function: DCE2_CoInitRdata() + * + * Initializes header of defragmentation reassembly packet. + * Sets relevant fields in header that will not have to change + * from reassembly to reassembly. The reassembly buffer used is + * big enough for the header. + * + ********************************************************************/ +void DCE2_CoInitRdata(uint8_t* co_ptr, int dir) +{ + DceRpcCoHdr* co_hdr = (DceRpcCoHdr*)co_ptr; + + /* Set some relevant fields. These should never get reset */ + co_hdr->pversion.major = DCERPC_PROTO_MAJOR_VERS__5; + co_hdr->pfc_flags = (DCERPC_CO_PFC_FLAGS__FIRST_FRAG | DCERPC_CO_PFC_FLAGS__LAST_FRAG); + co_hdr->packed_drep[0] = DCE2_LITTLE_ENDIAN; /* Little endian */ + + if (dir == PKT_FROM_CLIENT) + co_hdr->ptype = DCERPC_PDU_TYPE__REQUEST; + else + co_hdr->ptype = DCERPC_PDU_TYPE__RESPONSE; +} + static inline DCE2_CoSeg* DCE2_CoGetSegPtr(DCE2_SsnData* sd, DCE2_CoTracker* cot) { if (DCE2_SsnFromServer(sd->wire_pkt)) @@ -208,6 +307,40 @@ static inline dce2CommonStats* dce_get_proto_stats_ptr(DCE2_SsnData* sd) } } +//FIXIT-L Revisit to check if early reassembly functionality is required +static inline bool DCE2_GcReassembleEarly(DCE2_SsnData* sd) +{ + void* config = sd->config; + if (sd->trans == DCE2_TRANS_TYPE__TCP) + { + if (((dce2TcpProtoConf*)config)->co_reassemble_threshold > 0) + return true; + } + else + { + if (((dce2SmbProtoConf*)config)->co_reassemble_threshold > 0) + return true; + } + return false; +} + +static inline uint16_t DCE2_GcReassembleThreshold(DCE2_SsnData* sd) +{ + void* config = sd->config; + if (DCE2_GcReassembleEarly(sd)) + { + if (sd->trans == DCE2_TRANS_TYPE__TCP) + { + return ((dce2TcpProtoConf*)config)->co_reassemble_threshold; + } + else + { + return ((dce2SmbProtoConf*)config)->co_reassemble_threshold; + } + } + return UINT16_MAX; +} + /******************************************************************** * Function: DCE2_CoHdrChecks() * @@ -384,7 +517,7 @@ static inline void DCE2_CoEraseCtxIds(DCE2_CoTracker* cot) DCE2_ListEmpty(cot->ctx_ids); } -static DCE2_CoCtxIdNode* dce_process_ctx_id(DCE2_SsnData* sd,DCE2_CoTracker* cot, +static DCE2_CoCtxIdNode* dce_co_process_ctx_id(DCE2_SsnData* sd,DCE2_CoTracker* cot, const DceRpcCoHdr* co_hdr,DCE2_Policy policy, const uint8_t* frag_ptr, uint16_t frag_len) { @@ -503,8 +636,8 @@ static void DCE2_CoCtxReq(DCE2_SsnData* sd, DCE2_CoTracker* cot, const DceRpcCoH { DCE2_CoCtxIdNode* ctx_node; - ctx_node = dce_process_ctx_id(sd,cot,co_hdr,policy,frag_ptr,frag_len); - if (ctx_node == nullptr) + ctx_node = dce_co_process_ctx_id(sd,cot,co_hdr,policy,frag_ptr,frag_len); + if ((ctx_node == nullptr)) { return; } @@ -534,7 +667,7 @@ static void DCE2_CoCtxReq(DCE2_SsnData* sd, DCE2_CoTracker* cot, const DceRpcCoH } } -static void dce_process_ctx_result(DCE2_SsnData* sd,DCE2_CoTracker* cot, +static void dce_co_process_ctx_result(DCE2_SsnData* sd,DCE2_CoTracker* cot, const DceRpcCoHdr* co_hdr,DCE2_Policy policy, uint16_t result) { @@ -555,8 +688,7 @@ static void dce_process_ctx_result(DCE2_SsnData* sd,DCE2_CoTracker* cot, ctx_node = (DCE2_CoCtxIdNode*)DCE2_QueueDequeue(cot->pending_ctx_ids); if (ctx_node == nullptr) { - LogMessage("%s(%d) Failed to dequeue a context id node.\n", - __FILE__, __LINE__); + DebugMessage(DEBUG_DCE_COMMON, "Failed to dequeue a context id node.\n"); return; } @@ -740,7 +872,7 @@ static void DCE2_CoBindAck(DCE2_SsnData* sd, DCE2_CoTracker* cot, if (DCE2_QueueIsEmpty(cot->pending_ctx_ids)) return; - dce_process_ctx_result(sd,cot,co_hdr,policy,result); + dce_co_process_ctx_result(sd,cot,co_hdr,policy,result); } } @@ -802,8 +934,7 @@ static void DCE2_CoBind(DCE2_SsnData* sd, DCE2_CoTracker* cot, break; default: - LogMessage("%s(%d) Invalid policy: %d\n", - __FILE__, __LINE__, policy); + DebugFormat(DEBUG_DCE_COMMON, "Invalid policy: %d\n", policy); return; } @@ -865,8 +996,7 @@ static void DCE2_CoAlterCtx(DCE2_SsnData* sd, DCE2_CoTracker* cot, break; default: - LogMessage("%s(%d) Invalid policy: %d\n", - __FILE__, __LINE__, policy); + DebugFormat(DEBUG_DCE_COMMON, "Invalid policy: %d\n", policy); break; } @@ -913,6 +1043,372 @@ static int DCE2_CoGetAuthLen(DCE2_SsnData* sd, const DceRpcCoHdr* co_hdr, return (int)auth_len; } +/******************************************************************** + * Function: DCE2_CoGetFragBuf() + * + * Returns the appropriate fragmentation buffer. + * + ********************************************************************/ +static DCE2_Buffer* DCE2_CoGetFragBuf(DCE2_SsnData* sd, DCE2_CoFragTracker* ft) +{ + if (DCE2_SsnFromServer(sd->wire_pkt)) + return ft->srv_stub_buf; + + return ft->cli_stub_buf; +} + +/******************************************************************** + * Function: DCE2_CoGetRpktType() + * + * Determines the type of reassembly packet we need to use + * based on the transport and buffer type. + * + ********************************************************************/ +static DCE2_RpktType DCE2_CoGetRpktType(DCE2_SsnData* sd, DCE2_BufType btype) +{ + DCE2_RpktType rtype = DCE2_RPKT_TYPE__NULL; + + switch (sd->trans) + { + case DCE2_TRANS_TYPE__SMB: + switch (btype) + { + case DCE2_BUF_TYPE__SEG: + rtype = DCE2_RPKT_TYPE__SMB_CO_SEG; + break; + + case DCE2_BUF_TYPE__FRAG: + rtype = DCE2_RPKT_TYPE__SMB_CO_FRAG; + break; + + default: + DebugFormat(DEBUG_DCE_COMMON, "Invalid buffer type: %d\n", btype); + break; + } + break; + + case DCE2_TRANS_TYPE__TCP: + switch (btype) + { + case DCE2_BUF_TYPE__SEG: + rtype = DCE2_RPKT_TYPE__TCP_CO_SEG; + break; + + case DCE2_BUF_TYPE__FRAG: + rtype = DCE2_RPKT_TYPE__TCP_CO_FRAG; + break; + + default: + DebugFormat(DEBUG_DCE_COMMON, "Invalid buffer type: %d\n", btype); + break; + } + break; + + default: + DebugFormat(DEBUG_DCE_COMMON, "Invalid transport type: %d", sd->trans); + break; + } + return rtype; +} + +/******************************************************************** + * Function: DCE2_CoGetRpkt() + * + * Creates a reassembled buffer based on the kind of data + * (fragment, segment or both) we want to put in the reassembled + * buffer. + * + ********************************************************************/ +static Packet* DCE2_CoGetRpkt(DCE2_SsnData* sd, DCE2_CoTracker* cot, + DCE2_CoRpktType co_rtype, DCE2_RpktType* rtype) +{ + DCE2_Buffer* frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker); + const uint8_t* frag_data = nullptr; + uint32_t frag_len = 0; + Packet* rpkt = nullptr; + + *rtype = DCE2_RPKT_TYPE__NULL; + + switch (co_rtype) + { + case DCE2_CO_RPKT_TYPE__ALL: + case DCE2_CO_RPKT_TYPE__SEG: + + //FIXIT-M add segmentation logic + break; + + case DCE2_CO_RPKT_TYPE__FRAG: + if (!DCE2_BufferIsEmpty(frag_buf)) + { + frag_data = DCE2_BufferData(frag_buf); + frag_len = DCE2_BufferLength(frag_buf); + } + + break; + + default: + DebugFormat(DEBUG_DCE_COMMON, "Invalid CO rpkt type: %d\n", co_rtype); + return nullptr; + } + + //FIXIT-M Add logic to deal with segment buffer + + if (frag_data != nullptr) + *rtype = DCE2_CoGetRpktType(sd, DCE2_BUF_TYPE__FRAG); + else + //FIXIT-M add seg buffer logic + return nullptr; + if (*rtype == DCE2_RPKT_TYPE__NULL) + return nullptr; + + if (frag_data != nullptr) + { + rpkt = DCE2_GetRpkt(sd->wire_pkt, *rtype, frag_data, frag_len); + if (rpkt == nullptr) + { + DebugMessage(DEBUG_DCE_COMMON, "Failed to create reassembly buffer.\n"); + return nullptr; + } + //FIXIT-M add seg buffer logic + } + //FIXIT-M add seg buffer logic + + return rpkt; +} + +static Packet* dce_co_reassemble(DCE2_SsnData* sd, DCE2_CoTracker* cot, + DCE2_CoRpktType co_rtype,DceRpcCoHdr** co_hdr) +{ + DCE2_RpktType rpkt_type; + Packet* rpkt; + dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd); + int co_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__CO_CLI : + DCE2_MOCK_HDR_LEN__CO_SRV; + + if (sd->trans == DCE2_TRANS_TYPE__TCP) + { + Profile profile(dce2_tcp_pstat_co_reass); + } + else + { + Profile profile(dce2_smb_pstat_co_reass); + } + + rpkt = DCE2_CoGetRpkt(sd, cot, co_rtype, &rpkt_type); + if (rpkt == nullptr) + { + DebugMessage(DEBUG_DCE_COMMON, "Could not create DCE/RPC frag reassembled buffer.\n"); + return nullptr; + } + + switch (rpkt_type) + { + case DCE2_RPKT_TYPE__SMB_CO_FRAG: + case DCE2_RPKT_TYPE__SMB_CO_SEG: + //FIXIT-M Add logic + return nullptr; + + case DCE2_RPKT_TYPE__TCP_CO_FRAG: + case DCE2_RPKT_TYPE__TCP_CO_SEG: + if (rpkt_type == DCE2_RPKT_TYPE__TCP_CO_FRAG) + { + DCE2_CoSetRdata(sd, cot, (uint8_t*)rpkt->data, (uint16_t)(rpkt->dsize - co_hdr_len)); + + if (DCE2_SsnFromClient(sd->wire_pkt)) + dce_common_stats->co_cli_frag_reassembled++; + else + dce_common_stats->co_srv_frag_reassembled++; + } + //FIXIT-M add seg logic + *co_hdr = (DceRpcCoHdr*)rpkt->data; + cot->stub_data = rpkt->data + co_hdr_len; + return rpkt; + + default: + DebugFormat(DEBUG_DCE_COMMON, "Invalid rpkt type: %d\n", rpkt_type); + return nullptr; + } +} + +/******************************************************************** + * Function: DCE2_CoReassemble() + * + * Gets a reassemly packet based on the transport and the type of + * reassembly we want to do. Sets rule options and calls detect + * on the reassembled packet. + * + * + ********************************************************************/ +static void DCE2_CoReassemble(DCE2_SsnData* sd, DCE2_CoTracker* cot, DCE2_CoRpktType co_rtype) +{ + DceRpcCoHdr* co_hdr; + Packet* rpkt = dce_co_reassemble(sd,cot,co_rtype,&co_hdr); + /* Push packet onto stack */ + if (DCE2_PushPkt(rpkt,sd) != DCE2_RET__SUCCESS) + { + DebugMessage(DEBUG_DCE_COMMON, "Failed to push packet onto packet stack.\n"); + return; + } + DCE2_CoSetRopts(sd, cot, co_hdr, rpkt); + + DebugMessage(DEBUG_DCE_COMMON, "Reassembled CO fragmented packet:\n"); + DCE2_PrintPktData(rpkt->data, rpkt->dsize); + + DCE2_Detect(sd); + DCE2_PopPkt(sd); + + co_reassembled = 1; +} + +static inline void DCE2_CoFragReassemble(DCE2_SsnData* sd, DCE2_CoTracker* cot) +{ + DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__FRAG); +} + +static DCE2_Ret dce_co_handle_frag(DCE2_SsnData* sd, DCE2_CoTracker* cot, + const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, + uint16_t frag_len, DCE2_Buffer* frag_buf, + uint16_t max_frag_data) +{ + uint32_t size = (frag_len < DCE2_CO__MIN_ALLOC_SIZE) ? DCE2_CO__MIN_ALLOC_SIZE : frag_len; + DCE2_BufferMinAddFlag mflag = DCE2_BUFFER_MIN_ADD_FLAG__USE; + DCE2_Ret status; + dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd); + + if (sd->trans == DCE2_TRANS_TYPE__TCP) + { + Profile profile(dce2_tcp_pstat_co_frag); + } + else + { + Profile profile(dce2_smb_pstat_co_frag); + } + + if (DCE2_SsnFromClient(sd->wire_pkt)) + { + if (frag_len > dce_common_stats->co_cli_max_frag_size) + dce_common_stats->co_cli_max_frag_size = frag_len; + + if (dce_common_stats->co_cli_min_frag_size == 0 || frag_len < + dce_common_stats->co_cli_min_frag_size) + dce_common_stats->co_cli_min_frag_size = frag_len; + } + else + { + if (frag_len > dce_common_stats->co_srv_max_frag_size) + dce_common_stats->co_srv_max_frag_size = frag_len; + + if (dce_common_stats->co_srv_min_frag_size == 0 || frag_len < + dce_common_stats->co_srv_min_frag_size) + dce_common_stats->co_srv_min_frag_size = frag_len; + } + + if (frag_buf == nullptr) + { + if (DCE2_SsnFromServer(sd->wire_pkt)) + { + cot->frag_tracker.srv_stub_buf = + DCE2_BufferNew(size, DCE2_CO__MIN_ALLOC_SIZE); + frag_buf = cot->frag_tracker.srv_stub_buf; + } + else + { + cot->frag_tracker.cli_stub_buf = + DCE2_BufferNew(size, DCE2_CO__MIN_ALLOC_SIZE); + frag_buf = cot->frag_tracker.cli_stub_buf; + } + + if (frag_buf == nullptr) + { + return DCE2_RET__ERROR; + } + } + /* If there's already data in the buffer and this is a first frag + * we probably missed packets */ + if (DceRpcCoFirstFrag(co_hdr) && !DCE2_BufferIsEmpty(frag_buf)) + { + DCE2_CoResetFragTracker(&cot->frag_tracker); + DCE2_BufferEmpty(frag_buf); + } + + /* Check for potential overflow */ + if (DCE2_GcMaxFrag((dce2CommonProtoConf*)sd->config) && (frag_len > DCE2_GcMaxFragLen( + (dce2CommonProtoConf*)sd->config))) + frag_len = DCE2_GcMaxFragLen((dce2CommonProtoConf*)sd->config); + + if ((DCE2_BufferLength(frag_buf) + frag_len) > max_frag_data) + frag_len = max_frag_data - (uint16_t)DCE2_BufferLength(frag_buf); + + if (frag_len != 0) + { + /* If it's the last fragment we're going to flush so just alloc + * exactly what we need ... or if there is more data than can fit + * in the reassembly buffer */ + if (DceRpcCoLastFrag(co_hdr) || (DCE2_BufferLength(frag_buf) == max_frag_data)) + mflag = DCE2_BUFFER_MIN_ADD_FLAG__IGNORE; + + status = DCE2_BufferAddData(frag_buf, frag_ptr, + frag_len, DCE2_BufferLength(frag_buf), mflag); + + if (status != DCE2_RET__SUCCESS) + { + /* memcpy failed - reassemble */ + DCE2_CoFragReassemble(sd, cot); + DCE2_BufferEmpty(frag_buf); + return DCE2_RET__ERROR; + } + } + return(DCE2_RET__SUCCESS); +} + +/******************************************************************** + * Function: DCE2_CoHandleFrag() + * + * Handles adding a fragment to the defragmentation buffer. + * Does overflow checking. Maximum length of fragmentation buffer + * is based on the maximum packet length Snort can handle. + * + ********************************************************************/ + +static void DCE2_CoHandleFrag(DCE2_SsnData* sd, DCE2_CoTracker* cot, + const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len) +{ + DCE2_Ret ret_val; + DCE2_Buffer* frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker); + uint16_t max_frag_data; + + // FIXIT-M add SMB max_frag_data + + max_frag_data = DCE2_GetRpktMaxData(sd, DCE2_RPKT_TYPE__TCP_CO_FRAG); + + ret_val = dce_co_handle_frag(sd, cot,co_hdr, frag_ptr, frag_len,frag_buf,max_frag_data); + if (ret_val == DCE2_RET__SUCCESS) + { + /* Reassemble if we got a last frag ... */ + if (DceRpcCoLastFrag(co_hdr)) + { + DCE2_CoFragReassemble(sd, cot); + DCE2_BufferEmpty(frag_buf); + + /* Set this for the server response since response doesn't + * contain client opnum used */ + cot->opnum = cot->frag_tracker.opnum; + DCE2_CoResetFragTracker(&cot->frag_tracker); + + /* Return early - rule opts will be set in reassembly handler */ + return; + } + else if (DCE2_BufferLength(frag_buf) == max_frag_data) + { + /* ... or can't fit any more data in the buffer + * Don't reset frag tracker */ + DCE2_CoFragReassemble(sd, cot); + DCE2_BufferEmpty(frag_buf); + return; + } + } +} + /******************************************************************** * Function: DCE2_CoRequest() * @@ -926,7 +1422,7 @@ static int DCE2_CoGetAuthLen(DCE2_SsnData* sd, const DceRpcCoHdr* co_hdr, * ********************************************************************/ static void DCE2_CoRequest(DCE2_SsnData* sd, DCE2_CoTracker* cot, - const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len, Packet* p) + const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len) { DceRpcCoRequest* rhdr = (DceRpcCoRequest*)frag_ptr; uint16_t req_size = sizeof(DceRpcCoRequest); @@ -964,7 +1460,14 @@ static void DCE2_CoRequest(DCE2_SsnData* sd, DCE2_CoTracker* cot, /* Move past header */ DCE2_MOVE(frag_ptr, frag_len, req_size); - //FIXIT-M frag stuff + /* If for some reason we had some fragments queued */ + if (DceRpcCoFirstFrag(co_hdr) && !DceRpcCoLastFrag(co_hdr) + && !DCE2_BufferIsEmpty(cot->frag_tracker.cli_stub_buf)) + { + DCE2_CoFragReassemble(sd, cot); + DCE2_BufferEmpty(cot->frag_tracker.cli_stub_buf); + DCE2_CoResetFragTracker(&cot->frag_tracker); + } cot->stub_data = frag_ptr; cot->opnum = DceRpcCoOpnum(co_hdr, rhdr); @@ -977,11 +1480,130 @@ static void DCE2_CoRequest(DCE2_SsnData* sd, DCE2_CoTracker* cot, DebugMessage(DEBUG_DCE_COMMON, "First and last fragment.\n"); if (auth_len == -1) return; - DCE2_CoSetRopts(sd, cot, co_hdr, p); + DCE2_CoSetRopts(sd, cot, co_hdr, sd->wire_pkt); } else { - //FIXIT-M frag stuff + DCE2_CoFragTracker* ft = &cot->frag_tracker; + int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len); + + dce_common_stats->co_req_fragments++; + + if (DceRpcCoFirstFrag(co_hdr)) + DebugMessage(DEBUG_DCE_COMMON, "First fragment.\n"); + else if (DceRpcCoLastFrag(co_hdr)) + DebugMessage(DEBUG_DCE_COMMON, "Last fragment.\n"); + else + { + DebugMessage(DEBUG_DCE_COMMON, "Middle fragment.\n"); + } + DCE2_PrintPktData(frag_ptr, frag_len); + + if (auth_len == -1) + return; + + if (DCE2_BufferIsEmpty(ft->cli_stub_buf)) + { + ft->expected_opnum = cot->opnum; + ft->expected_ctx_id = cot->ctx_id; + ft->expected_call_id = cot->call_id; + } + else + { + /* Don't return for these, because we can still process and servers + * will still accept and deal with the anomalies in their own way */ + if ((ft->expected_opnum != DCE2_SENTINEL) && + (ft->expected_opnum != cot->opnum)) + { + dce_alert(GID_DCE2, DCE2_CO_FRAG_DIFF_OPNUM,dce_common_stats); + } + + if ((ft->expected_ctx_id != DCE2_SENTINEL) && + (ft->expected_ctx_id != cot->ctx_id)) + { + dce_alert(GID_DCE2, DCE2_CO_FRAG_DIFF_CTX_ID,dce_common_stats); + } + + if ((ft->expected_call_id != DCE2_SENTINEL) && + (ft->expected_call_id != cot->call_id)) + { + dce_alert(GID_DCE2, DCE2_CO_FRAG_DIFF_CALL_ID,dce_common_stats); + } + } + + /* Possibly set opnum in frag tracker */ + switch (policy) + { + case DCE2_POLICY__WIN2000: + case DCE2_POLICY__WIN2003: + case DCE2_POLICY__WINXP: + case DCE2_POLICY__SAMBA: + case DCE2_POLICY__SAMBA_3_0_37: + case DCE2_POLICY__SAMBA_3_0_22: + case DCE2_POLICY__SAMBA_3_0_20: + if (DceRpcCoLastFrag(co_hdr)) + ft->opnum = cot->opnum; + break; + + case DCE2_POLICY__WINVISTA: + case DCE2_POLICY__WIN2008: + case DCE2_POLICY__WIN7: + if (DceRpcCoFirstFrag(co_hdr)) + ft->opnum = cot->opnum; + break; + + default: + DebugFormat(DEBUG_DCE_COMMON, "Invalid policy: %d\n", policy); + break; + } + + /* Possibly set context id in frag tracker */ + switch (policy) + { + case DCE2_POLICY__WIN2000: + case DCE2_POLICY__WIN2003: + case DCE2_POLICY__WINXP: + case DCE2_POLICY__WINVISTA: + case DCE2_POLICY__WIN2008: + case DCE2_POLICY__WIN7: + if (DceRpcCoFirstFrag(co_hdr)) + { + ft->ctx_id = cot->ctx_id; + } + else if ((ft->expected_call_id != DCE2_SENTINEL) && + (ft->expected_call_id != cot->call_id)) + { + /* Server won't accept frag */ + return; + } + + break; + + case DCE2_POLICY__SAMBA: + case DCE2_POLICY__SAMBA_3_0_37: + case DCE2_POLICY__SAMBA_3_0_22: + case DCE2_POLICY__SAMBA_3_0_20: + if (DceRpcCoLastFrag(co_hdr)) + { + ft->ctx_id = cot->ctx_id; + } + + break; + + default: + DebugFormat(DEBUG_DCE_COMMON, "Invalid policy: %d\n", policy); + break; + } + + DCE2_CoSetRopts(sd, cot, co_hdr, sd->wire_pkt); + + /* If we're configured to do defragmentation */ + if (DCE2_GcDceDefrag((dce2CommonProtoConf*)sd->config)) + { + /* Don't want to include authentication data in fragment */ + DCE2_CoHandleFrag(sd, cot, co_hdr, frag_ptr, + (uint16_t)(frag_len - (uint16_t)auth_len)); + } } } @@ -997,7 +1619,7 @@ static void DCE2_CoRequest(DCE2_SsnData* sd, DCE2_CoTracker* cot, ********************************************************************/ static void DCE2_CoResponse(DCE2_SsnData* sd, DCE2_CoTracker* cot, - const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len, Packet* p) + const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len) { DceRpcCoResponse* rhdr = (DceRpcCoResponse*)frag_ptr; uint16_t ctx_id; @@ -1038,8 +1660,7 @@ static void DCE2_CoResponse(DCE2_SsnData* sd, DCE2_CoTracker* cot, if (ctx_node == nullptr) { - LogMessage("%s(%d) Failed to dequeue a context id node.\n", - __FILE__, __LINE__); + DebugMessage(DEBUG_DCE_COMMON, "Failed to dequeue a context id node.\n"); return; } @@ -1060,7 +1681,13 @@ static void DCE2_CoResponse(DCE2_SsnData* sd, DCE2_CoTracker* cot, /* Move past header */ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoResponse)); - //FIXIT-M frag stuff + /* If for some reason we had some fragments queued */ + if (DceRpcCoFirstFrag(co_hdr) && !DCE2_BufferIsEmpty(cot->frag_tracker.srv_stub_buf)) + { + DCE2_CoFragReassemble(sd, cot); + DCE2_BufferEmpty(cot->frag_tracker.srv_stub_buf); + DCE2_CoResetFragTracker(&cot->frag_tracker); + } cot->stub_data = frag_ptr; /* Opnum not in response header - have to use previous client's */ @@ -1073,11 +1700,25 @@ static void DCE2_CoResponse(DCE2_SsnData* sd, DCE2_CoTracker* cot, DebugMessage(DEBUG_DCE_COMMON, "First and last fragment.\n"); if (auth_len == -1) return; - DCE2_CoSetRopts(sd, cot, co_hdr, p); + DCE2_CoSetRopts(sd, cot, co_hdr, sd->wire_pkt); } else { - /* FIXIT-M frag stuff */ + //DCE2_CoFragTracker *ft = &cot->frag_tracker; + int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len); + + dce_common_stats->co_resp_fragments++; + if (auth_len == -1) + return; + + DCE2_CoSetRopts(sd, cot, co_hdr, sd->wire_pkt); + + /* If we're configured to do defragmentation */ + if (DCE2_GcDceDefrag((dce2CommonProtoConf*)sd->config)) + { + DCE2_CoHandleFrag(sd, cot, co_hdr, frag_ptr, + (uint16_t)(frag_len - (uint16_t)auth_len)); + } } } @@ -1091,7 +1732,7 @@ static void DCE2_CoResponse(DCE2_SsnData* sd, DCE2_CoTracker* cot, * ********************************************************************/ static void DCE2_CoDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot, - const uint8_t* frag_ptr, uint16_t frag_len, Packet* p) + const uint8_t* frag_ptr, uint16_t frag_len) { /* Already checked that we have enough data for header */ const DceRpcCoHdr* co_hdr = (DceRpcCoHdr*)frag_ptr; @@ -1142,7 +1783,7 @@ static void DCE2_CoDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot, return; } - DCE2_CoRequest(sd, cot, co_hdr, frag_ptr, frag_len, p); + DCE2_CoRequest(sd, cot, co_hdr, frag_ptr, frag_len); break; @@ -1228,7 +1869,7 @@ static void DCE2_CoDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot, case DCERPC_PDU_TYPE__RESPONSE: DebugMessage(DEBUG_DCE_COMMON, "Response\n"); dce_common_stats->co_response++; - DCE2_CoResponse(sd, cot, co_hdr, frag_ptr, frag_len, p); + DCE2_CoResponse(sd, cot, co_hdr, frag_ptr, frag_len); break; case DCERPC_PDU_TYPE__FAULT: @@ -1270,6 +1911,40 @@ static void DCE2_CoDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot, } } +/******************************************************************** + * Function: DCE2_CoEarlyReassemble() + * + * Checks to see if we should send a reassembly packet based on + * the current data in fragmentation and segmentation buffers + * to the detection engine. Whether we do or not is based on + * whether or not we are configured to do so. The number of bytes + * in the fragmentation and segmentation buffers are calulated + * and if they exceed the amount we are configured for, we + * reassemble. + * + ********************************************************************/ +static void DCE2_CoEarlyReassemble(DCE2_SsnData* sd, DCE2_CoTracker* cot) +{ + DCE2_Buffer* frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker); + + if (DCE2_SsnFromServer(sd->wire_pkt)) + return; + + if (!DCE2_BufferIsEmpty(frag_buf)) + { + uint32_t bytes = DCE2_BufferLength(frag_buf); + //FIXIT-M Add seg buffer logic + + if (bytes >= DCE2_GcReassembleThreshold(sd)) + { + DebugMessage(DEBUG_DCE_COMMON, "Early reassemble - DCE/RPC fragments\n"); + DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__FRAG); + //FIXIT-M add seg buffer logic + } + } + //FIXIT-M Add seg buffer logic +} + /******************************************************************** * Function: DCE2_CoProcess() * @@ -1282,7 +1957,7 @@ static void DCE2_CoDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot, * ********************************************************************/ void DCE2_CoProcess(DCE2_SsnData* sd, DCE2_CoTracker* cot, - const uint8_t* data_ptr, uint16_t data_len, Packet* p) + const uint8_t* data_ptr, uint16_t data_len) { DCE2_CoSeg* seg = DCE2_CoGetSegPtr(sd, cot); uint32_t num_frags = 0; @@ -1326,7 +2001,7 @@ void DCE2_CoProcess(DCE2_SsnData* sd, DCE2_CoTracker* cot, DCE2_MOVE(data_ptr, data_len, frag_len); /* Got a full DCE/RPC pdu */ - DCE2_CoDecode(sd, cot, frag_ptr, frag_len, p); + DCE2_CoDecode(sd, cot, frag_ptr, frag_len); /* If we're configured to do defragmentation only detect on first frag * since we'll detect on reassembled */ @@ -1344,6 +2019,7 @@ void DCE2_CoProcess(DCE2_SsnData* sd, DCE2_CoTracker* cot, } } - // FIXIT-M add reassemble logic + if (DCE2_GcReassembleEarly(sd) && !co_reassembled) + DCE2_CoEarlyReassemble(sd, cot); } diff --git a/src/service_inspectors/dce_rpc/dce_co.h b/src/service_inspectors/dce_rpc/dce_co.h index 95fac3e0a..05e557f9c 100644 --- a/src/service_inspectors/dce_rpc/dce_co.h +++ b/src/service_inspectors/dce_rpc/dce_co.h @@ -70,6 +70,10 @@ from opnum established for fragmented request." from context id established for fragmented request." #define DCE2_MAX_XMIT_SIZE_FUZZ 500 +#define DCE2_MOCK_HDR_LEN__CO_CLI (sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest)) +#define DCE2_MOCK_HDR_LEN__CO_SRV (sizeof(DceRpcCoHdr) + sizeof(DceRpcCoResponse)) +#define DCE2_CO__MIN_ALLOC_SIZE 50 +#define DCE2_LITTLE_ENDIAN 0x10 #pragma pack(1) @@ -267,6 +271,14 @@ enum DceRpcCoContDefResult DCERPC_CO_CONT_DEF_RESULT__USER_REJECTION, DCERPC_CO_CONT_DEF_RESULT__PROVIDER_REJECTION }; + +enum DCE2_CoRpktType +{ + DCE2_CO_RPKT_TYPE__SEG, + DCE2_CO_RPKT_TYPE__FRAG, + DCE2_CO_RPKT_TYPE__ALL +}; + inline uint8_t DceRpcCoVersMaj(const DceRpcCoHdr* co) { return co->pversion.major; @@ -400,7 +412,9 @@ inline uint16_t DceRpcCoCtxId(const DceRpcCoHdr* co, const DceRpcCoRequest* cor) void DCE2_CoInitTracker(DCE2_CoTracker*); void DCE2_CoProcess(DCE2_SsnData*, DCE2_CoTracker*, - const uint8_t*, uint16_t, Packet* p); + const uint8_t*, uint16_t); +void DCE2_CoInitRdata(uint8_t*, int); +void DCE2_CoCleanTracker(DCE2_CoTracker*); #endif diff --git a/src/service_inspectors/dce_rpc/dce_common.cc b/src/service_inspectors/dce_rpc/dce_common.cc index 321755583..e878c37df 100644 --- a/src/service_inspectors/dce_rpc/dce_common.cc +++ b/src/service_inspectors/dce_rpc/dce_common.cc @@ -21,6 +21,7 @@ #include "dce_common.h" #include "dce_tcp.h" #include "dce_smb.h" +#include "dce_co.h" #include "framework/base_api.h" #include "framework/module.h" #include "flow/flow.h" @@ -28,8 +29,15 @@ #include "main/snort_debug.h" #include "detection/detect.h" #include "ips_options/extract.h" +#include "protocols/packet_manager.h" +#include "events/event_queue.h" +#include "framework/codec.h" +#include "main/snort.h" +#include "framework/endianness.h" THREAD_LOCAL int dce2_detected = 0; +THREAD_LOCAL DCE2_CStack* dce2_pkt_stack = nullptr; +THREAD_LOCAL int dce2_inspector_instances = 0; static const char* dce2_get_policy_name(DCE2_Policy policy) { @@ -163,19 +171,22 @@ static void dce2_protocol_detect(DCE2_SsnData* sd, Packet* pkt) Profile profile(dce2_smb_pstat_detect); } - // FIXIT - decide whether eventq push/pop is necessary once packet reassembly is supported - //SnortEventqPush(); + SnortEventqPush(); snort_detect(pkt); - //SnortEventqPop(); + SnortEventqPop(); dce2_detected = 1; } void DCE2_Detect(DCE2_SsnData* sd) { - Packet* top_pkt = sd->wire_pkt; - //FIXIT-M Get packet from stack - + Packet* top_pkt; + top_pkt = (Packet*)DCE2_CStackTop(dce2_pkt_stack); + if (top_pkt == nullptr) + { + DebugMessage(DEBUG_DCE_COMMON,"No packet on top of stack.\n"); + return; + } DebugMessage(DEBUG_DCE_COMMON, "Detecting ------------------------------------------------\n"); DebugMessage(DEBUG_DCE_COMMON, " Rule options:\n"); DCE2_PrintRoptions(&sd->ropts); @@ -222,6 +233,13 @@ DceEndianness::DceEndianness() stub_data_offset = DCE2_SENTINEL; } +void DceEndianness::reset() +{ + hdr_byte_order = DCE2_SENTINEL; + data_byte_order = DCE2_SENTINEL; + stub_data_offset = DCE2_SENTINEL; +} + bool DceEndianness::get_offset_endianness(int32_t offset, int8_t& endian) { int byte_order; @@ -262,6 +280,157 @@ bool DceEndianness::get_offset_endianness(int32_t offset, int8_t& endian) return true; } +static void dce_push_pkt_log(Packet* pkt,DCE2_SsnData* sd) +{ + if (sd->trans == DCE2_TRANS_TYPE__TCP) + { + Profile profile(dce2_tcp_pstat_log); + } + else + { + Profile profile(dce2_smb_pstat_log); + } + + SnortEventqPush(); + SnortEventqLog(pkt); + SnortEventqReset(); + SnortEventqPop(); +} + +DCE2_Ret DCE2_PushPkt(Packet* p,DCE2_SsnData* sd) +{ + Packet* top_pkt; + top_pkt = (Packet*)DCE2_CStackTop(dce2_pkt_stack); + + if (top_pkt != nullptr) + { + dce_push_pkt_log(top_pkt,sd); + } + if (DCE2_CStackPush(dce2_pkt_stack, (void*)p) != DCE2_RET__SUCCESS) + return DCE2_RET__ERROR; + + return DCE2_RET__SUCCESS; +} + +void DCE2_PopPkt(DCE2_SsnData* sd) +{ + Packet* pop_pkt = (Packet*)DCE2_CStackPop(dce2_pkt_stack); + + if (sd->trans == DCE2_TRANS_TYPE__TCP) + { + Profile profile(dce2_tcp_pstat_log); + } + else + { + Profile profile(dce2_smb_pstat_log); + } + + if (pop_pkt == nullptr) + { + DebugMessage(DEBUG_DCE_COMMON, "No packet to pop off stack.\n"); + return; + } + SnortEventqPush(); + SnortEventqLog(pop_pkt); + SnortEventqReset(); + SnortEventqPop(); +} + +uint16_t DCE2_GetRpktMaxData(DCE2_SsnData* sd, DCE2_RpktType rtype) +{ + Packet* p = sd->wire_pkt; + uint16_t overhead = 0; + + switch (rtype) + { + case DCE2_RPKT_TYPE__SMB_SEG: + case DCE2_RPKT_TYPE__SMB_TRANS: + case DCE2_RPKT_TYPE__SMB_CO_SEG: + case DCE2_RPKT_TYPE__SMB_CO_FRAG: + case DCE2_RPKT_TYPE__TCP_CO_SEG: + //FIXIT-M Add support for these + break; + + case DCE2_RPKT_TYPE__TCP_CO_FRAG: + if (DCE2_SsnFromClient(p)) + overhead += DCE2_MOCK_HDR_LEN__CO_CLI; + else + overhead += DCE2_MOCK_HDR_LEN__CO_SRV; + break; + + default: + DebugFormat(DEBUG_DCE_COMMON,"Invalid reassembly packet type: %d\n",rtype); + return 0; + } + return (DCE2_REASSEMBLY_BUF_SIZE - overhead); +} + +Packet* DCE2_GetRpkt(Packet* p,DCE2_RpktType rpkt_type, + const uint8_t* data, uint32_t data_len) +{ + Packet* rpkt; + DceEndianness* endianness; + uint16_t data_overhead = 0; + + switch (rpkt_type) + { + case DCE2_RPKT_TYPE__SMB_SEG: + case DCE2_RPKT_TYPE__SMB_TRANS: + case DCE2_RPKT_TYPE__SMB_CO_SEG: + case DCE2_RPKT_TYPE__SMB_CO_FRAG: + case DCE2_RPKT_TYPE__TCP_CO_SEG: + case DCE2_RPKT_TYPE__UDP_CL_FRAG: + //FIXIT-M add support later + + case DCE2_RPKT_TYPE__TCP_CO_FRAG: + rpkt = dce2_tcp_rpkt[rpkt_type - DCE2_TCP_RPKT_TYPE_START]; + endianness = (DceEndianness*)rpkt->endianness; + rpkt->reset(); + rpkt->endianness = (Endianness *)endianness; + ((DceEndianness *)rpkt->endianness)->reset(); + rpkt->pkth = p->pkth; + rpkt->ptrs = p->ptrs; + rpkt->flow = p->flow; + rpkt->proto_bits = p->proto_bits; + rpkt->pseudo_type = PSEUDO_PKT_DCE_FRAG; + rpkt->packet_flags = p->packet_flags; + rpkt->packet_flags |= PKT_PSEUDO; + rpkt->user_policy_id = p->user_policy_id; + + if (DCE2_SsnFromClient(p)) + { + data_overhead = DCE2_MOCK_HDR_LEN__CO_CLI; + memset((void*)rpkt->data, 0, data_overhead); + DCE2_CoInitRdata((uint8_t*)rpkt->data, PKT_FROM_CLIENT); + } + else + { + data_overhead = DCE2_MOCK_HDR_LEN__CO_SRV; + memset((void*)rpkt->data, 0, data_overhead); + DCE2_CoInitRdata((uint8_t*)rpkt->data, PKT_FROM_SERVER); + } + break; + + default: + DebugFormat(DEBUG_DCE_COMMON, "Invalid reassembly packet type: %d\n",rpkt_type); + return nullptr; + } + + if ((data_overhead + data_len) > DCE2_REASSEMBLY_BUF_SIZE) + data_len -= (data_overhead + data_len) - DCE2_REASSEMBLY_BUF_SIZE; + + if (SafeMemcpy((void*)(rpkt->data + data_overhead), + (void*)data, (size_t)data_len, (void*)rpkt->data, + (void*)((uint8_t*)rpkt->data + DCE2_REASSEMBLY_BUF_SIZE)) != SAFEMEM_SUCCESS) + { + DebugMessage(DEBUG_DCE_COMMON, "Failed to copy data into reassembly buffer.\n"); + return nullptr; + } + + rpkt->dsize = data_len + data_overhead; + return rpkt; +} + #ifdef BUILDING_SO extern const BaseApi* ips_dce_iface; diff --git a/src/service_inspectors/dce_rpc/dce_common.h b/src/service_inspectors/dce_rpc/dce_common.h index 2c8d712e1..c86016b40 100644 --- a/src/service_inspectors/dce_rpc/dce_common.h +++ b/src/service_inspectors/dce_rpc/dce_common.h @@ -22,6 +22,7 @@ #define DCE_COMMON_H #include "dce_utils.h" +#include "dce_list.h" #include "main/snort_types.h" #include "framework/module.h" #include "framework/inspector.h" @@ -31,8 +32,12 @@ extern const InspectApi dce2_smb_api; extern const InspectApi dce2_tcp_api; extern THREAD_LOCAL int dce2_detected; +extern THREAD_LOCAL int dce2_inspector_instances; +extern THREAD_LOCAL DCE2_CStack* dce2_pkt_stack; -#define GID_DCE2 145 +#define GID_DCE2 133 +#define DCE2_PKT_STACK__SIZE 10 +#define DCE2_REASSEMBLY_BUF_SIZE 65535 enum DCE2_Policy { @@ -96,7 +101,7 @@ struct dce2CommonStats struct dce2CommonProtoConf { bool disable_defrag; - uint16_t max_frag_len; + int max_frag_len; DCE2_Policy policy; }; @@ -142,6 +147,19 @@ enum DceRpcProtoMinorVers DCERPC_PROTO_MINOR_VERS__1 = 1 }; +enum DCE2_RpktType +{ + DCE2_RPKT_TYPE__NULL = 0, + DCE2_RPKT_TYPE__SMB_SEG, + DCE2_RPKT_TYPE__SMB_TRANS, + DCE2_RPKT_TYPE__SMB_CO_SEG, + DCE2_RPKT_TYPE__SMB_CO_FRAG, + DCE2_RPKT_TYPE__TCP_CO_SEG, + DCE2_RPKT_TYPE__TCP_CO_FRAG, + DCE2_RPKT_TYPE__UDP_CL_FRAG, + DCE2_RPKT_TYPE__MAX +}; + struct DCE2_Roptions { /* dce_iface */ @@ -197,6 +215,7 @@ public: public: DceEndianness(); virtual bool get_offset_endianness(int32_t offset, int8_t& endian); + void reset(); }; inline void DCE2_ResetRopts(DCE2_Roptions* ropts) @@ -240,7 +259,21 @@ inline int DCE2_SsnNoInspect(DCE2_SsnData* sd) inline bool DCE2_GcDceDefrag(dce2CommonProtoConf* config) { - return config->disable_defrag; + return (config->disable_defrag ? false : true); +} + +inline bool DCE2_GcMaxFrag(dce2CommonProtoConf* config) +{ + if (config->max_frag_len != DCE2_SENTINEL) + return true; + return false; +} + +inline uint16_t DCE2_GcMaxFragLen(dce2CommonProtoConf* config) +{ + if (DCE2_GcMaxFrag(config)) + return (uint16_t)config->max_frag_len; + return UINT16_MAX; } inline int DCE2_SsnFromServer(Packet* p) @@ -268,6 +301,11 @@ bool dce2_set_common_config(Value&, dce2CommonProtoConf&); void print_dce2_common_config(dce2CommonProtoConf&); bool dce2_paf_abort(Flow*, DCE2_SsnData*); void DCE2_Detect(DCE2_SsnData*); +Packet* DCE2_GetRpkt(Packet*, DCE2_RpktType, + const uint8_t*, uint32_t); +DCE2_Ret DCE2_PushPkt(Packet*,DCE2_SsnData*); +void DCE2_PopPkt(DCE2_SsnData*); +uint16_t DCE2_GetRpktMaxData(DCE2_SsnData*, DCE2_RpktType); DCE2_SsnData* get_dce2_session_data(Packet*); diff --git a/src/service_inspectors/dce_rpc/dce_list.cc b/src/service_inspectors/dce_rpc/dce_list.cc index 55753b177..bd4debd0b 100644 --- a/src/service_inspectors/dce_rpc/dce_list.cc +++ b/src/service_inspectors/dce_rpc/dce_list.cc @@ -28,8 +28,8 @@ #include "dce_list.h" #include "dce_utils.h" -#include "log/messages.h" #include "utils/util.h" +#include "main/snort_debug.h" /******************************************************************** * Private function prototyes @@ -78,8 +78,6 @@ DCE2_List* DCE2_ListNew(DCE2_ListType type, DCE2_ListKeyCompare kc, return nullptr; list = (DCE2_List*)SnortAlloc(sizeof(DCE2_List)); - if (list == nullptr) - return nullptr; list->type = type; list->compare = kc; @@ -90,6 +88,122 @@ DCE2_List* DCE2_ListNew(DCE2_ListType type, DCE2_ListKeyCompare kc, return list; } +/******************************************************************** + * Function: DCE2_ListInsertTail() + * + * Private function for inserting a node at the end of the list. + * + * Arguments: + * DCE2_List * + * A pointer to the list object. + * DCE2_ListNode * + * A pointer to the list node to insert. + * + * Returns: None + * + ********************************************************************/ +static void DCE2_ListInsertTail(DCE2_List* list, DCE2_ListNode* n) +{ + if ((list == NULL) || (n == NULL)) + { + DebugMessage(DEBUG_DCE_COMMON, "List and/or list node passed in was NULL\n"); + return; + } + + if (list->tail == NULL) + { + list->tail = list->head = n; + n->prev = n->next = NULL; + } + else + { + n->prev = list->tail; + n->next = NULL; + list->tail->next = n; + list->tail = n; + } + + list->num_nodes++; +} + +/******************************************************************** + * Function: DCE2_ListInsertHead() + * + * Private function for inserting a node at the front of the list. + * + * Arguments: + * DCE2_List * + * A pointer to the list object. + * DCE2_ListNode * + * A pointer to the list node to insert. + * + * Returns: None + * + ********************************************************************/ +static void DCE2_ListInsertHead(DCE2_List* list, DCE2_ListNode* n) +{ + if ((list == NULL) || (n == NULL)) + { + DebugMessage(DEBUG_DCE_COMMON, "List and/or list node passed in was NULL\n"); + return; + } + + if (list->head == NULL) + { + list->head = list->tail = n; + n->prev = n->next = NULL; + } + else + { + n->prev = NULL; + n->next = list->head; + list->head->prev = n; + list->head = n; + } + + list->num_nodes++; +} + +/******************************************************************** + * Function: DCE2_ListInsertBefore() + * + * Private function for inserting a node before a given node in + * the list. + * + * Arguments: + * DCE2_List * + * A pointer to the list object. + * DCE2_ListNode * + * A pointer to the list node to insert. + * DCE2_ListNode * + * A pointer to the list node to insert this node before. + * + * Returns: None + * + ********************************************************************/ +static void DCE2_ListInsertBefore(DCE2_List* list, DCE2_ListNode* insert, DCE2_ListNode* front) +{ + if ((list == NULL) || (insert == NULL) || (front == NULL)) + { + DebugMessage(DEBUG_DCE_COMMON, "List, insert node and/or front node passed in was NULL\n"); + return; + } + + if (front == list->head) + { + DCE2_ListInsertHead(list, insert); + } + else + { + insert->prev = front->prev; + insert->next = front; + front->prev->next = insert; + front->prev = insert; + + list->num_nodes++; + } +} + /******************************************************************** * Function: DCE2_ListInsert() * @@ -113,8 +227,7 @@ DCE2_List* DCE2_ListNew(DCE2_ListType type, DCE2_ListKeyCompare kc, * in the list and no duplicates are allowed. * DCE2_RET__SUCCESS if a new node with key and data is * successfully inserted into the list. - * DCE2_RET__ERROR if memory cannot be allocated for the - * new node or a NULL list object was passed in. + * DCE2_RET__ERROR if a NULL list object was passed in. * ********************************************************************/ DCE2_Ret DCE2_ListInsert(DCE2_List* list, void* key, void* data) @@ -148,8 +261,6 @@ DCE2_Ret DCE2_ListInsert(DCE2_List* list, void* key, void* data) } n = (DCE2_ListNode*)SnortAlloc(sizeof(DCE2_ListNode)); - if (n == nullptr) - return DCE2_RET__ERROR; n->key = key; n->data = data; @@ -324,127 +435,6 @@ void DCE2_ListDestroy(DCE2_List* list) free(list); } -/******************************************************************** - * Function: DCE2_ListInsertTail() - * - * Private function for inserting a node at the end of the list. - * - * Arguments: - * DCE2_List * - * A pointer to the list object. - * DCE2_ListNode * - * A pointer to the list node to insert. - * - * Returns: None - * - ********************************************************************/ -static void DCE2_ListInsertTail(DCE2_List* list, DCE2_ListNode* n) -{ - if ((list == nullptr) || (n == nullptr)) - { - ErrorMessage("%s(%d) List and/or list node passed in was nullptr", - __FILE__, __LINE__); - - return; - } - - if (list->tail == nullptr) - { - list->tail = list->head = n; - n->prev = n->next = nullptr; - } - else - { - n->prev = list->tail; - n->next = nullptr; - list->tail->next = n; - list->tail = n; - } - - list->num_nodes++; -} - -/******************************************************************** - * Function: DCE2_ListInsertHead() - * - * Private function for inserting a node at the front of the list. - * - * Arguments: - * DCE2_List * - * A pointer to the list object. - * DCE2_ListNode * - * A pointer to the list node to insert. - * - * Returns: None - * - ********************************************************************/ -static void DCE2_ListInsertHead(DCE2_List* list, DCE2_ListNode* n) -{ - if ((list == nullptr) || (n == nullptr)) - { - ErrorMessage("%s(%d) List and/or list node passed in was NULL", - __FILE__, __LINE__); - - return; - } - - if (list->head == nullptr) - { - list->head = list->tail = n; - n->prev = n->next = nullptr; - } - else - { - n->prev = nullptr; - n->next = list->head; - list->head->prev = n; - list->head = n; - } - - list->num_nodes++; -} - -/******************************************************************** - * Function: DCE2_ListInsertBefore() - * - * Private function for inserting a node before a given node in - * the list. - * - * Arguments: - * DCE2_List * - * A pointer to the list object. - * DCE2_ListNode * - * A pointer to the list node to insert. - * DCE2_ListNode * - * A pointer to the list node to insert this node before. - * - * Returns: None - * - ********************************************************************/ -static void DCE2_ListInsertBefore(DCE2_List* list, DCE2_ListNode* insert, DCE2_ListNode* front) -{ - if ((list == nullptr) || (insert == nullptr) || (front == nullptr)) - { - ErrorMessage("%s(%d) List, insert node and/or front node passed in " - "was NULL", __FILE__, __LINE__); - return; - } - - if (front == list->head) - { - DCE2_ListInsertHead(list, insert); - } - else - { - insert->prev = front->prev; - insert->next = front; - front->prev->next = insert; - front->prev = insert; - - list->num_nodes++; - } -} - /******************************************************************** * Function: DCE2_ListFind() * @@ -526,7 +516,6 @@ void* DCE2_ListFind(DCE2_List* list, void* key) * Returns: * DCE2_Queue * * Pointer to a new queue object. - * NULL if unable to allocate memory for the object. * ********************************************************************/ DCE2_Queue* DCE2_QueueNew(DCE2_QueueDataFree df) @@ -534,9 +523,6 @@ DCE2_Queue* DCE2_QueueNew(DCE2_QueueDataFree df) DCE2_Queue* queue; queue = (DCE2_Queue*)SnortAlloc(sizeof(DCE2_Queue)); - if (queue == nullptr) - return nullptr; - queue->data_free = df; return queue; @@ -555,8 +541,7 @@ DCE2_Queue* DCE2_QueueNew(DCE2_QueueDataFree df) * * Returns: * DCE2_Ret - * DCE2_RET__ERROR if memory cannot be allocated for a new - * queue node or the queue object passed in is NULL. + * DCE2_RET__ERROR if the queue object passed in is NULL. * DCE2_RET__SUCCESS if the data is successfully added to * the queue. * @@ -569,8 +554,6 @@ DCE2_Ret DCE2_QueueEnqueue(DCE2_Queue* queue, void* data) return DCE2_RET__ERROR; n = (DCE2_QueueNode*)SnortAlloc(sizeof(DCE2_QueueNode)); - if (n == nullptr) - return DCE2_RET__ERROR; n->data = data; @@ -681,6 +664,27 @@ void DCE2_QueueEmpty(DCE2_Queue* queue) queue->num_nodes = 0; } +/******************************************************************** + * Function: DCE2_QueueDestroy() + * + * Destroys the queue object and all of the data associated with it. + * + * Arguments: + * DCE2_Queue * + * A pointer to the queue object. + * + * Returns: None + * + ********************************************************************/ +void DCE2_QueueDestroy(DCE2_Queue* queue) +{ + if (queue == NULL) + return; + + DCE2_QueueEmpty(queue); + free((void*)queue); +} + /******************************************************************** * Function: DCE2_QueueFirst() * @@ -753,3 +757,209 @@ void* DCE2_QueueNext(DCE2_Queue* queue) return nullptr; } +/******************************************************************** + * Function: DCE2_CStackNew() + * + * Creates and initializes a new static sized stack object. The + * static stack uses a fixed size array and uses indexes to + * indicate the start and end of the stack. This type of + * stack can become full since it is a fixed size. Used for + * performance reasons since new nodes do not need to be + * allocated on the fly. + * + * Arguments: + * int + * The size that should be allocated for the static + * stack storage. + * DCE2_CStackDataFree + * An optional free function for the data inserted into + * the stack. If NULL is passed in, the user will be + * responsible for freeing data left in the stack. + * + * Returns: + * DCE2_CStack * + * Pointer to a new stack object. + * + ********************************************************************/ +DCE2_CStack* DCE2_CStackNew(int size, DCE2_CStackDataFree df) +{ + DCE2_CStack* cstack; + + if (size <= 0) + return nullptr; + + cstack = (DCE2_CStack*)SnortAlloc(sizeof(DCE2_CStack)); + + cstack->data_free = df; + + cstack->stack = (void**)SnortAlloc(size * sizeof(void*)); + + cstack->size = size; + cstack->tail_idx = DCE2_SENTINEL; + cstack->cur_idx = DCE2_SENTINEL; + + return cstack; +} + +/******************************************************************** + * Function: DCE2_CStackPush() + * + * Inserts data into the static stack. + * + * Arguments: + * DCE2_CStack * + * A pointer to the stack object. + * void * + * Pointer to the data to insert into the stack. + * + * Returns: + * DCE2_Ret + * DCE2_RET__ERROR if the stack is full or the stack object + * passed in is NULL. + * DCE2_RET__SUCCESS if the data is successfully added to + * the stack. + * + ********************************************************************/ +DCE2_Ret DCE2_CStackPush(DCE2_CStack* cstack, void* data) +{ + if (cstack == nullptr) + return DCE2_RET__ERROR; + + if (cstack->num_nodes == (uint32_t)cstack->size) + return DCE2_RET__ERROR; + + if (cstack->tail_idx == DCE2_SENTINEL) + cstack->tail_idx = 0; + else + cstack->tail_idx++; + + cstack->stack[cstack->tail_idx] = data; + cstack->num_nodes++; + + return DCE2_RET__SUCCESS; +} + +/******************************************************************** + * Function: DCE2_CStackPop() + * + * Removes and returns the data in the last node in the stack. + * Note that the user will have to free the data returned. The + * data free function only applies to data that is in the stack + * when it is emptied or destroyed. + * + * Arguments: + * DCE2_CStack * + * A pointer to the stack object. + * + * Returns: + * void * + * The data in the last node in the stack. + * NULL if there are no items in the stack or the stack object + * passed in is NULL. + * + ********************************************************************/ +void* DCE2_CStackPop(DCE2_CStack* cstack) +{ + void* data; + + if (cstack == nullptr) + return nullptr; + + if (cstack->num_nodes == 0) + return nullptr; + + data = cstack->stack[cstack->tail_idx]; + cstack->stack[cstack->tail_idx] = nullptr; + + if (cstack->tail_idx == 0) + cstack->tail_idx = DCE2_SENTINEL; + else + cstack->tail_idx--; + + cstack->num_nodes--; + + return data; +} + +/******************************************************************** + * Function: DCE2_CStackTop() + * + * Returns the data on top of the stack. Does not remove the data + * from the stack. + * + * Arguments: + * DCE2_CStack * + * A pointer to the stack object. + * + * Returns: + * void * + * The data on top of the stack. + * NULL if there are no items in the stack or the stack object + * passed in is NULL. + * + ********************************************************************/ +void* DCE2_CStackTop(DCE2_CStack* cstack) +{ + if (cstack == nullptr) + return nullptr; + + if (cstack->num_nodes == 0) + return nullptr; + + return cstack->stack[cstack->tail_idx]; +} + +/******************************************************************** + * Function: DCE2_CStackEmpty() + * + * Removes all of the nodes in a stack. Does not delete the stack + * object itself or the storage array. Calls data free function + * for data if it is not NULL. + * + * Arguments: + * DCE2_CStack * + * A pointer to the stack object. + * + * Returns: None + * + ********************************************************************/ +void DCE2_CStackEmpty(DCE2_CStack* cstack) +{ + if (cstack == nullptr) + return; + + while (!DCE2_CStackIsEmpty(cstack)) + { + void* data = DCE2_CStackPop(cstack); + + if ((data != nullptr) && (cstack->data_free != nullptr)) + cstack->data_free(data); + } + + cstack->num_nodes = 0; + cstack->tail_idx = DCE2_SENTINEL; + cstack->cur_idx = DCE2_SENTINEL; +} + +/******************************************************************** + * Function: DCE2_CStackDestroy() + * + * Destroys the stack object and all of the data associated with it. + * + * Arguments: + * DCE2_CStack * + * A pointer to the stack object. + * + * Returns: None + * + ********************************************************************/ +void DCE2_CStackDestroy(DCE2_CStack* cstack) +{ + if (cstack == nullptr) + return; + + DCE2_CStackEmpty(cstack); + free((void*)cstack->stack); + free((void*)cstack); +} + diff --git a/src/service_inspectors/dce_rpc/dce_list.h b/src/service_inspectors/dce_rpc/dce_list.h index 035866b99..e6117d235 100644 --- a/src/service_inspectors/dce_rpc/dce_list.h +++ b/src/service_inspectors/dce_rpc/dce_list.h @@ -102,6 +102,18 @@ struct DCE2_Queue DCE2_QueueNode* prev; }; +typedef DCE2_ListDataFree DCE2_CStackDataFree; + +struct DCE2_CStack +{ + uint32_t num_nodes; + DCE2_CStackDataFree data_free; + int size; + void** stack; + int tail_idx; + int cur_idx; +}; + /******************************************************************** * Public function prototypes ********************************************************************/ @@ -122,6 +134,15 @@ static inline bool DCE2_QueueIsEmpty(DCE2_Queue*); void DCE2_QueueEmpty(DCE2_Queue*); void* DCE2_QueueFirst(DCE2_Queue*); void* DCE2_QueueNext(DCE2_Queue*); +void DCE2_QueueDestroy(DCE2_Queue*); + +DCE2_CStack* DCE2_CStackNew(int, DCE2_CStackDataFree); +DCE2_Ret DCE2_CStackPush(DCE2_CStack*, void*); +void* DCE2_CStackPop(DCE2_CStack*); +void* DCE2_CStackTop(DCE2_CStack*); +static inline int DCE2_CStackIsEmpty(DCE2_CStack*); +void DCE2_CStackEmpty(DCE2_CStack*); +void DCE2_CStackDestroy(DCE2_CStack*); /******************************************************************** * Function: DCE2_ListIsEmpty() @@ -165,5 +186,21 @@ inline bool DCE2_QueueIsEmpty(DCE2_Queue* queue) return 0; } +/******************************************************************** + * Function: DCE2_CStackIsEmpty() + * + * Determines whether or not the stack has any items in it + * currently. + * + ********************************************************************/ +static inline int DCE2_CStackIsEmpty(DCE2_CStack* cstack) +{ + if (cstack == nullptr) + return 1; + if (cstack->num_nodes == 0) + return 1; + return 0; +} + #endif diff --git a/src/service_inspectors/dce_rpc/dce_smb.cc b/src/service_inspectors/dce_rpc/dce_smb.cc index a00e5e2f3..90da1d154 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.cc +++ b/src/service_inspectors/dce_rpc/dce_smb.cc @@ -24,8 +24,12 @@ #include "dce_list.h" #include "main/snort_debug.h" #include "file_api/file_service.h" +#include "utils/util.h" + +THREAD_LOCAL int dce2_smb_inspector_instances = 0; THREAD_LOCAL dce2SmbStats dce2_smb_stats; +THREAD_LOCAL Packet* dce2_smb_rpkt[DCE2_SMB_RPKT_TYPE_MAX] = { NULL, NULL, NULL, NULL }; THREAD_LOCAL ProfileStats dce2_smb_pstat_main; THREAD_LOCAL ProfileStats dce2_smb_pstat_session; @@ -65,6 +69,7 @@ static DCE2_SmbSsnData* set_new_dce2_smb_session(Packet* p) { Dce2SmbFlowData* fd = new Dce2SmbFlowData; + memset(&fd->dce2_smb_session,0,sizeof(DCE2_SmbSsnData)); p->flow->set_application_data(fd); return(&fd->dce2_smb_session); } @@ -247,6 +252,54 @@ static void dce2_smb_dtor(Inspector* p) delete p; } +static void dce2_smb_thread_init() +{ + if (dce2_inspector_instances == 0) + { + dce2_pkt_stack = DCE2_CStackNew(DCE2_PKT_STACK__SIZE, nullptr); + } + if (dce2_smb_inspector_instances == 0) + { + for (int i=0; i < DCE2_SMB_RPKT_TYPE_MAX; i++) + { + Packet* p = (Packet*)SnortAlloc(sizeof(Packet)); + p->data = (uint8_t*)SnortAlloc(DCE2_REASSEMBLY_BUF_SIZE); + p->dsize = DCE2_REASSEMBLY_BUF_SIZE; + dce2_smb_rpkt[i] = p; + } + } + dce2_smb_inspector_instances++; + dce2_inspector_instances++; +} + +static void dce2_smb_thread_term() +{ + dce2_inspector_instances--; + dce2_smb_inspector_instances--; + + if (dce2_smb_inspector_instances == 0) + { + for (int i=0; idata) + { + free((void *)p->data); + } + free(p); + dce2_smb_rpkt[i] = nullptr; + } + } + } + if (dce2_inspector_instances == 0) + { + DCE2_CStackDestroy(dce2_pkt_stack); + dce2_pkt_stack = nullptr; + } +} + const InspectApi dce2_smb_api = { { @@ -267,8 +320,8 @@ const InspectApi dce2_smb_api = "dce_smb", dce2_smb_init, nullptr, // pterm - nullptr, // tinit - nullptr, // tterm + dce2_smb_thread_init, // tinit + dce2_smb_thread_term, // tterm dce2_smb_ctor, dce2_smb_dtor, nullptr, // ssn diff --git a/src/service_inspectors/dce_rpc/dce_smb.h b/src/service_inspectors/dce_rpc/dce_smb.h index d11408604..73fe4eea5 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.h +++ b/src/service_inspectors/dce_rpc/dce_smb.h @@ -30,6 +30,7 @@ #define DCE2_SMB_NAME "dce_smb" #define DCE2_SMB_HELP "dce over smb inspection" +#define DCE2_SMB_RPKT_TYPE_MAX 4 #define DCE2_SMB_BAD_NBSS_TYPE 2 #define DCE2_SMB_BAD_TYPE 3 @@ -180,7 +181,7 @@ struct dce2SmbStats }; extern THREAD_LOCAL dce2SmbStats dce2_smb_stats; - +extern THREAD_LOCAL Packet* dce2_smb_rpkt[DCE2_SMB_RPKT_TYPE_MAX]; extern THREAD_LOCAL ProfileStats dce2_smb_pstat_main; extern THREAD_LOCAL ProfileStats dce2_smb_pstat_session; extern THREAD_LOCAL ProfileStats dce2_smb_pstat_new_session; @@ -248,6 +249,43 @@ struct SmbNtHdr uint16_t smb_mid; /* multiplex id */ }; +struct SmbWriteAndXReq /* smb_wct = 12 */ +{ + uint8_t smb_wct; /* count of 16-bit words that follow */ + uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ + uint8_t smb_reh2; /* reserved (must be zero) */ + uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ + uint16_t smb_fid; /* file handle */ + uint32_t smb_offset; /* offset in file to begin write */ + uint32_t smb_timeout; /* number of milliseconds to wait for completion */ + uint16_t smb_wmode; /* write mode: + bit0 - complete write before return (write through) + bit1 - return smb_remaining (pipes/devices only) + bit2 - use WriteRawNamedPipe (pipes only) + bit3 - this is the start of a message (pipes only) */ + uint16_t smb_countleft; /* bytes remaining to write to satisfy user’s request */ + uint16_t smb_dsize_high; /* high bytes of data size */ + uint16_t smb_dsize; /* number of data bytes in buffer (min value = 0) */ + uint16_t smb_doff; /* offset (from start of SMB hdr) to data bytes */ + uint16_t smb_bcc; /* total bytes (including pad bytes) following */ +}; + +struct SmbReadAndXResp /* smb_wct = 12 */ +{ + uint8_t smb_wct; /* count of 16-bit words that follow */ + uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ + uint8_t smb_res2; /* reserved (pad to word) */ + uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ + uint16_t smb_remaining; /* bytes remaining to be read (pipes/devices only) */ + uint32_t smb_rsvd; /* reserved */ + uint16_t smb_dsize; /* number of data bytes (minimum value = 0) */ + uint16_t smb_doff; /* offset (from start of SMB hdr) to data bytes */ + uint16_t smb_dsize_high; /* high bytes of data size */ + uint32_t smb_rsvd1; /* reserved */ + uint32_t smb_rsvd2; /* reserved */ + uint16_t smb_bcc; /* total bytes (including pad bytes) following */ +}; + #pragma pack() enum DCE2_SmbSsnState { @@ -506,6 +544,12 @@ public: DCE2_SmbSsnData dce2_smb_session; }; +// Used for reassembled packets +#define DCE2_MOCK_HDR_LEN__SMB_CLI \ + (sizeof(NbssHdr) + sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq)) +#define DCE2_MOCK_HDR_LEN__SMB_SRV \ + (sizeof(NbssHdr) + sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp)) + DCE2_SmbSsnData* get_dce2_smb_session_data(Flow*); #endif diff --git a/src/service_inspectors/dce_rpc/dce_tcp.cc b/src/service_inspectors/dce_rpc/dce_tcp.cc index 531e4ecb1..8cca32896 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp.cc +++ b/src/service_inspectors/dce_rpc/dce_tcp.cc @@ -25,11 +25,26 @@ #include "dce_co.h" #include "main/snort_debug.h" #include "detection/detect.h" +#include "log/messages.h" +#include "protocols/packet_manager.h" +#include "utils/util.h" Dce2TcpFlowData::Dce2TcpFlowData() : FlowData(flow_id) { } +Dce2TcpFlowData::~Dce2TcpFlowData() +{ + DCE2_CoCleanTracker(&dce2_tcp_session.co_tracker); +} + +THREAD_LOCAL int dce2_tcp_inspector_instances = 0; + +//FIXIT-L Currently using separate buffers for segment and fragment reassembly +//as in Snort2x code. Doesn't seem necessary for TCP but may be the case for +//SMB/HTTP ..keeping logic consistent for now +THREAD_LOCAL Packet* dce2_tcp_rpkt[DCE2_TCP_RPKT_TYPE_MAX] = { NULL, NULL }; + THREAD_LOCAL dce2TcpStats dce2_tcp_stats; THREAD_LOCAL ProfileStats dce2_tcp_pstat_main; @@ -56,6 +71,7 @@ static DCE2_TcpSsnData* set_new_dce2_tcp_session(Packet* p) { Dce2TcpFlowData* fd = new Dce2TcpFlowData; + memset(&fd->dce2_tcp_session,0,sizeof(DCE2_TcpSsnData)); p->flow->set_application_data(fd); return(&fd->dce2_tcp_session); } @@ -127,13 +143,6 @@ static DCE2_TcpSsnData* dce2_handle_tcp_session(Packet* p, dce2TcpProtoConf* con } DebugFormat(DEBUG_DCE_TCP, "Session pointer: %p\n", (void*)dce2_tcp_sess); - if (dce2_tcp_sess) - { - //FIXIT-M Stack push - - p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT; - dce2_detected = 0; - } return dce2_tcp_sess; } @@ -192,18 +201,27 @@ void Dce2Tcp::eval(Packet* p) } dce2_tcp_sess = dce2_handle_tcp_session(p, &config); + if (dce2_tcp_sess) { + //FIXIT-L evaluate moving pushpkt out of session pstats + if (DCE2_PushPkt(p,&dce2_tcp_sess->sd) != DCE2_RET__SUCCESS) + { + DebugMessage(DEBUG_DCE_TCP, "Failed to push packet onto packet stack.\n"); + return; + } + p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT; + dce2_detected = 0; dce2_tcp_stats.tcp_pkts++; p->endianness = (Endianness*)new DceEndianness(); DCE2_CoProcess( - &dce2_tcp_sess->sd, &dce2_tcp_sess->co_tracker, p->data, p->dsize, p); + &dce2_tcp_sess->sd, &dce2_tcp_sess->co_tracker, p->data, p->dsize); if (!dce2_detected) DCE2_Detect(&dce2_tcp_sess->sd); DCE2_ResetRopts(&dce2_tcp_sess->sd.ropts); - //FIXIT-M DCE2_PopPkt(sd); + DCE2_PopPkt(&dce2_tcp_sess->sd); if (!DCE2_SsnAutodetected(&dce2_tcp_sess->sd)) DisableInspection(); @@ -245,6 +263,56 @@ static void dce2_tcp_init() Dce2TcpFlowData::init(); } +static void dce2_tcp_thread_init() +{ + if (dce2_inspector_instances == 0) + { + dce2_pkt_stack = DCE2_CStackNew(DCE2_PKT_STACK__SIZE, nullptr); + } + if (dce2_tcp_inspector_instances == 0) + { + for (int i=0; i < DCE2_TCP_RPKT_TYPE_MAX; i++) + { + Packet* p = (Packet*)SnortAlloc(sizeof(Packet)); + p->data = (uint8_t*)SnortAlloc(DCE2_REASSEMBLY_BUF_SIZE); + p->endianness = (Endianness*)new DceEndianness(); + p->dsize = DCE2_REASSEMBLY_BUF_SIZE; + dce2_tcp_rpkt[i] = p; + } + } + dce2_tcp_inspector_instances++; + dce2_inspector_instances++; +} + +static void dce2_tcp_thread_term() +{ + dce2_inspector_instances--; + dce2_tcp_inspector_instances--; + + if (dce2_tcp_inspector_instances == 0) + { + for (int i=0; idata) + { + free((void *)p->data); + } + delete p->endianness; + free(p); + dce2_tcp_rpkt[i] = nullptr; + } + } + } + if (dce2_inspector_instances == 0) + { + DCE2_CStackDestroy(dce2_pkt_stack); + dce2_pkt_stack = nullptr; + } +} + const InspectApi dce2_tcp_api = { { @@ -265,8 +333,8 @@ const InspectApi dce2_tcp_api = "dce_tcp", dce2_tcp_init, nullptr, // pterm - nullptr, // tinit - nullptr, // tterm + dce2_tcp_thread_init, // tinit + dce2_tcp_thread_term, // tterm dce2_tcp_ctor, dce2_tcp_dtor, nullptr, // ssn diff --git a/src/service_inspectors/dce_rpc/dce_tcp.h b/src/service_inspectors/dce_rpc/dce_tcp.h index 7c956d99b..c1375ddd8 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp.h +++ b/src/service_inspectors/dce_rpc/dce_tcp.h @@ -29,6 +29,8 @@ #define DCE2_TCP_NAME "dce_tcp" #define DCE2_TCP_HELP "dce over tcp inspection" +#define DCE2_TCP_RPKT_TYPE_MAX 2 +#define DCE2_TCP_RPKT_TYPE_START 5 struct dce2TcpStats { @@ -80,7 +82,7 @@ struct dce2TcpStats }; extern THREAD_LOCAL dce2TcpStats dce2_tcp_stats; - +extern THREAD_LOCAL Packet* dce2_tcp_rpkt[DCE2_TCP_RPKT_TYPE_MAX]; extern THREAD_LOCAL ProfileStats dce2_tcp_pstat_main; extern THREAD_LOCAL ProfileStats dce2_tcp_pstat_session; extern THREAD_LOCAL ProfileStats dce2_tcp_pstat_new_session; @@ -127,6 +129,7 @@ class Dce2TcpFlowData : public FlowData { public: Dce2TcpFlowData(); + ~Dce2TcpFlowData(); static void init() { diff --git a/src/service_inspectors/dce_rpc/dce_utils.cc b/src/service_inspectors/dce_rpc/dce_utils.cc index 348d901d3..145770a59 100644 --- a/src/service_inspectors/dce_rpc/dce_utils.cc +++ b/src/service_inspectors/dce_rpc/dce_utils.cc @@ -19,6 +19,7 @@ #include "dce_utils.h" #include "main/snort_debug.h" +#include "utils/util.h" /******************************************************************** * Function: DCE2_GetValue() @@ -308,3 +309,127 @@ void DCE2_PrintPktData(const uint8_t*, const uint16_t) #endif // DEBUG_MSGS +DCE2_Buffer* DCE2_BufferNew(uint32_t initial_size, uint32_t min_add_size) +{ + DCE2_Buffer* buf = (DCE2_Buffer*)SnortAlloc(sizeof(DCE2_Buffer)); + + if (buf == nullptr) + return nullptr; + + if (initial_size != 0) + { + buf->data = (uint8_t*)SnortAlloc(initial_size); + if (buf->data == nullptr) + { + free((void*)buf); + return nullptr; + } + } + + buf->size = initial_size; + buf->len = 0; + buf->min_add_size = min_add_size; + buf->offset = 0; + + return buf; +} + +void* DCE2_ReAlloc(void* old_mem, uint32_t old_size, uint32_t new_size) +{ + void* new_mem; + + if (old_mem == nullptr) + { + return nullptr; + } + else if (new_size < old_size) + { + DebugMessage(DEBUG_DCE_COMMON, "New size is less than old size.\n"); + return nullptr; + } + else if (new_size == old_size) + { + return old_mem; + } + + new_mem = SnortAlloc(new_size); + if (new_mem == nullptr) + return nullptr; + + if (SafeMemcpy(new_mem, old_mem, old_size, new_mem, + (void*)((uint8_t*)new_mem + new_size)) != SAFEMEM_SUCCESS) + { + DebugMessage(DEBUG_DCE_COMMON, "Failed to copy old memory into new memory.\n"); + free(new_mem); + return nullptr; + } + + free(old_mem); + + return new_mem; +} + +DCE2_Ret DCE2_BufferAddData(DCE2_Buffer* buf, const uint8_t* data, + uint32_t data_len, uint32_t data_offset, DCE2_BufferMinAddFlag mflag) +{ + if ((buf == nullptr) || (data == nullptr)) + return DCE2_RET__ERROR; + + /* Return success for this since ultimately nothing _was_ added */ + if (data_len == 0) + return DCE2_RET__SUCCESS; + + if (buf->data == nullptr) + { + uint32_t size = data_offset + data_len; + + if ((size < buf->min_add_size) && (mflag == DCE2_BUFFER_MIN_ADD_FLAG__USE)) + size = buf->min_add_size; + + buf->data = (uint8_t*)SnortAlloc(size); + if (buf->data == nullptr) + return DCE2_RET__ERROR; + + buf->size = size; + } + else if ((data_offset + data_len) > buf->size) + { + uint8_t* tmp; + uint32_t new_size = data_offset + data_len; + + if (((new_size - buf->size) < buf->min_add_size) && (mflag == + DCE2_BUFFER_MIN_ADD_FLAG__USE)) + new_size = buf->size + buf->min_add_size; + + tmp = (uint8_t*)DCE2_ReAlloc(buf->data, buf->size, new_size); + if (tmp == nullptr) + return DCE2_RET__ERROR; + + buf->data = tmp; + buf->size = new_size; + } + + if (SafeMemcpy(buf->data + data_offset, data, data_len, buf->data, + buf->data + buf->size) != SAFEMEM_SUCCESS) + { + DebugMessage(DEBUG_DCE_COMMON, "Failed to copy data into buffer.\n"); + return DCE2_RET__ERROR; + } + + if ((data_offset + data_len) > buf->len) + buf->len = data_offset + data_len; + + return DCE2_RET__SUCCESS; +} + +void DCE2_BufferDestroy(DCE2_Buffer* buf) +{ + if (buf == nullptr) + return; + + if (buf->data != nullptr) + free((void*)buf->data); + + free((void*)buf); +} + diff --git a/src/service_inspectors/dce_rpc/dce_utils.h b/src/service_inspectors/dce_rpc/dce_utils.h index 745a69cc2..447a79d98 100644 --- a/src/service_inspectors/dce_rpc/dce_utils.h +++ b/src/service_inspectors/dce_rpc/dce_utils.h @@ -77,6 +77,17 @@ enum DceRpcBoFlag DCERPC_BO_FLAG__BIG_ENDIAN, DCERPC_BO_FLAG__LITTLE_ENDIAN }; +enum DCE2_BufType +{ + DCE2_BUF_TYPE__NULL, + DCE2_BUF_TYPE__SEG, + DCE2_BUF_TYPE__FRAG +}; +enum DCE2_BufferMinAddFlag +{ + DCE2_BUFFER_MIN_ADD_FLAG__USE, + DCE2_BUFFER_MIN_ADD_FLAG__IGNORE +}; /******************************************************************** * Structures @@ -112,13 +123,18 @@ inline char* DCE2_PruneWhiteSpace(char*); inline bool DCE2_IsEmptyStr(char*); inline int DCE2_UuidCompare(const void*, const void*); -const char* DCE2_UuidToStr(const Uuid*, DceRpcBoFlag); -void DCE2_PrintPktData(const uint8_t*, const uint16_t); /******************************************************************** * Public function prototypes ********************************************************************/ DCE2_Ret DCE2_GetValue(char*, char*, void*, int, DCE2_IntType, uint8_t); +const char* DCE2_UuidToStr(const Uuid*, DceRpcBoFlag); +void DCE2_PrintPktData(const uint8_t*, const uint16_t); +DCE2_Buffer* DCE2_BufferNew(uint32_t, uint32_t); +void* DCE2_ReAlloc(void*, uint32_t, uint32_t); +DCE2_Ret DCE2_BufferAddData(DCE2_Buffer*, const uint8_t*, + uint32_t, uint32_t, DCE2_BufferMinAddFlag); +void DCE2_BufferDestroy(DCE2_Buffer* buf); /******************************************************************** * Function: DCE2_IsSpaceChar()