]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #362 in SNORT/snort3 from dcefrag to master
authorRuss Combs (rucombs) <rucombs@cisco.com>
Wed, 23 Mar 2016 16:41:34 +0000 (12:41 -0400)
committerRuss Combs (rucombs) <rucombs@cisco.com>
Wed, 23 Mar 2016 16:41:34 +0000 (12:41 -0400)
Squashed commit of the following:

commit e22bd15dbef6f88cff1d411d940acb748e2d46e6
Author: rrp <rrp@cisco.com>
Date:   Wed Mar 23 12:20:19 2016 -0400

    Changes to address more comments

commit 116950fe5ed12427000ced35a76e05d4f99396ff
Author: rrp <rrp@cisco.com>
Date:   Tue Mar 22 23:32:37 2016 -0400

    address code review comments for DCE fragmentation code

commit a6da6b463f004c2314be400e64f3d48c88fbd20e
Author: rrp <rrp@cisco.com>
Date:   Thu Mar 10 16:34:15 2016 -0500

    Changes to support DCE packet fragmentation

13 files changed:
src/events/event_queue.h
src/service_inspectors/dce_rpc/dce_co.cc
src/service_inspectors/dce_rpc/dce_co.h
src/service_inspectors/dce_rpc/dce_common.cc
src/service_inspectors/dce_rpc/dce_common.h
src/service_inspectors/dce_rpc/dce_list.cc
src/service_inspectors/dce_rpc/dce_list.h
src/service_inspectors/dce_rpc/dce_smb.cc
src/service_inspectors/dce_rpc/dce_smb.h
src/service_inspectors/dce_rpc/dce_tcp.cc
src/service_inspectors/dce_rpc/dce_tcp.h
src/service_inspectors/dce_rpc/dce_utils.cc
src/service_inspectors/dce_rpc/dce_utils.h

index 0b4681f62bc3170b48c9a3f82d97f62dff44693b..04f1a93ee039c526e21bcfafec78056c76494ecb 100644 (file)
@@ -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*);
index b7f69549c4f88cfccf157712dad7f06a24ce2122..72ed8a57be9a3ab10c7de80dd9c1905b0f34a1cf 100644 (file)
@@ -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);
 }
 
index 95fac3e0ab3b1fb478f11ff0de5a4549319eda2f..05e557f9c4003a4913e3f2dfcb5d3a46cd7fd762 100644 (file)
@@ -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
 
index 321755583bd4e1a8a16c24078b5faeb16e41581c..e878c37df8472d5c2a89327d4ddf32fd551f74b1 100644 (file)
@@ -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"
 #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;
index 2c8d712e1a82fc21a30678c01ff8b95ada9d886a..c86016b40719f94ac0f2501cb0053b657e4b7936 100644 (file)
@@ -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"
 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*);
 
index 55753b17753bbc558dff32a236ffc1c864533c4a..bd4debd0b4c964270ca83d99b4a1556bd6f762f0 100644 (file)
@@ -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);
+}
+
index 035866b991321b47035584dd4aca83c39f86f800..e6117d235a552389b6ae8796d9672c294b6a9a7b 100644 (file)
@@ -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
 
index a00e5e2f3e6ac6d3d7fe3f23122754422dda32da..90da1d154b50e10369ccf5cf72dc0c8fe0750ee0 100644 (file)
 #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; 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 =
 {
     {
@@ -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
index d114086044d938de082781e42d1df57e352c8c40..73fe4eea5a2d9ea3834bc84e0782405b38a0eefa 100644 (file)
@@ -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
index 531e4ecb16fb2b971989ad60a54517bc9966668c..8cca32896fd82de4b700157a370458b9e65c1f42 100644 (file)
 #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; 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 =
 {
     {
@@ -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
index 7c956d99b5b24bf04d55ad724a1dcd68982bdb2e..c1375ddd8269f2209b01315fa004da76469a8c90 100644 (file)
@@ -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()
     {
index 348d901d3bfbea50891c29896300acc915a8c64e..145770a598b9b3347546fac697ab949587273861 100644 (file)
@@ -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);
+}
+
index 745a69cc2e73d2f41937935035ed1857bef2374e..447a79d98e61775b952a5fb5d7bb57605f1d5247 100644 (file)
@@ -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()