void SnortEventqNew(EventQueueConfig*);
void SnortEventqFree();
-void SnortEventqReset(void);
+SO_PUBLIC void SnortEventqReset(void);
void SnortEventqResetCounts(void);
SO_PUBLIC int SnortEventqLog(struct Packet*);
#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"
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))
}
}
+//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()
*
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)
{
{
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;
}
}
}
-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)
{
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;
}
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);
}
}
break;
default:
- LogMessage("%s(%d) Invalid policy: %d\n",
- __FILE__, __LINE__, policy);
+ DebugFormat(DEBUG_DCE_COMMON, "Invalid policy: %d\n", policy);
return;
}
break;
default:
- LogMessage("%s(%d) Invalid policy: %d\n",
- __FILE__, __LINE__, policy);
+ DebugFormat(DEBUG_DCE_COMMON, "Invalid policy: %d\n", policy);
break;
}
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()
*
*
********************************************************************/
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);
/* 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);
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));
+ }
}
}
********************************************************************/
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;
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;
}
/* 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 */
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));
+ }
}
}
*
********************************************************************/
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;
return;
}
- DCE2_CoRequest(sd, cot, co_hdr, frag_ptr, frag_len, p);
+ DCE2_CoRequest(sd, cot, co_hdr, frag_ptr, frag_len);
break;
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:
}
}
+/********************************************************************
+ * 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()
*
*
********************************************************************/
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;
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 */
}
}
- // FIXIT-M add reassemble logic
+ if (DCE2_GcReassembleEarly(sd) && !co_reassembled)
+ DCE2_CoEarlyReassemble(sd, cot);
}
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)
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;
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
#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"
#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)
{
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);
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;
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;
#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"
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
{
struct dce2CommonProtoConf
{
bool disable_defrag;
- uint16_t max_frag_len;
+ int max_frag_len;
DCE2_Policy policy;
};
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 */
public:
DceEndianness();
virtual bool get_offset_endianness(int32_t offset, int8_t& endian);
+ void reset();
};
inline void DCE2_ResetRopts(DCE2_Roptions* ropts)
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)
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*);
#include "dce_list.h"
#include "dce_utils.h"
-#include "log/messages.h"
#include "utils/util.h"
+#include "main/snort_debug.h"
/********************************************************************
* Private function prototyes
return nullptr;
list = (DCE2_List*)SnortAlloc(sizeof(DCE2_List));
- if (list == nullptr)
- return nullptr;
list->type = type;
list->compare = 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()
*
* 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)
}
n = (DCE2_ListNode*)SnortAlloc(sizeof(DCE2_ListNode));
- if (n == nullptr)
- return DCE2_RET__ERROR;
n->key = key;
n->data = data;
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()
*
* 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)
DCE2_Queue* queue;
queue = (DCE2_Queue*)SnortAlloc(sizeof(DCE2_Queue));
- if (queue == nullptr)
- return nullptr;
-
queue->data_free = df;
return queue;
*
* 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.
*
return DCE2_RET__ERROR;
n = (DCE2_QueueNode*)SnortAlloc(sizeof(DCE2_QueueNode));
- if (n == nullptr)
- return DCE2_RET__ERROR;
n->data = data;
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()
*
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);
+}
+
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
********************************************************************/
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()
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
#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;
{
Dce2SmbFlowData* fd = new Dce2SmbFlowData;
+ memset(&fd->dce2_smb_session,0,sizeof(DCE2_SmbSsnData));
p->flow->set_application_data(fd);
return(&fd->dce2_smb_session);
}
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; i<DCE2_SMB_RPKT_TYPE_MAX; i++)
+ {
+ if ( dce2_smb_rpkt[i] != nullptr )
+ {
+ Packet* p = dce2_smb_rpkt[i];
+ if (p->data)
+ {
+ 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 =
{
{
"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
#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
};
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;
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
{
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
#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;
{
Dce2TcpFlowData* fd = new Dce2TcpFlowData;
+ memset(&fd->dce2_tcp_session,0,sizeof(DCE2_TcpSsnData));
p->flow->set_application_data(fd);
return(&fd->dce2_tcp_session);
}
}
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;
}
}
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();
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; i<DCE2_TCP_RPKT_TYPE_MAX; i++)
+ {
+ if ( dce2_tcp_rpkt[i] != nullptr )
+ {
+ Packet* p = dce2_tcp_rpkt[i];
+ if (p->data)
+ {
+ 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 =
{
{
"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
#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
{
};
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;
{
public:
Dce2TcpFlowData();
+ ~Dce2TcpFlowData();
static void init()
{
#include "dce_utils.h"
#include "main/snort_debug.h"
+#include "utils/util.h"
/********************************************************************
* Function: DCE2_GetValue()
#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);
+}
+
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
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()