]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #573 in SNORT/snort3 from smb_segmentation to master
authorRuss Combs (rucombs) <rucombs@cisco.com>
Tue, 2 Aug 2016 17:42:47 +0000 (13:42 -0400)
committerRuss Combs (rucombs) <rucombs@cisco.com>
Tue, 2 Aug 2016 17:42:47 +0000 (13:42 -0400)
Squashed commit of the following:

commit 3b5cd0bd44a3a79c0c55dca9a3aee990deb1f3f6
Author: mdagon <mdagon@cisco.com>
Date:   Mon Aug 1 13:48:37 2016 -0400

    Smb segmentation port

    Code review: delete space before **, replace comment with assert

src/service_inspectors/dce_rpc/dce_co.cc
src/service_inspectors/dce_rpc/dce_smb.cc
src/service_inspectors/dce_rpc/dce_smb_utils.cc
src/service_inspectors/dce_rpc/dce_smb_utils.h
src/service_inspectors/dce_rpc/dce_utils.h

index 8e1fc3612ceca5ee4e37da9dcf3e990ceb9cb2ac..baf1591e944a91612d98f555d514af4c88131853 100644 (file)
@@ -30,6 +30,7 @@
 #include "log/messages.h"
 #include "main/snort_debug.h"
 #include "utils/util.h"
+#include  <assert.h>
 
 THREAD_LOCAL int co_reassembled = 0;
 
@@ -2246,12 +2247,14 @@ static void DCE2_CoSegDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot, DCE2_CoSeg*
 }
 
 static DCE2_Ret DCE2_HandleSegmentation(DCE2_Buffer* seg_buf, const uint8_t* data_ptr,
-    uint16_t data_len, uint32_t need_len)
+    uint16_t data_len, uint32_t need_len, uint16_t* data_used)
 {
     uint32_t copy_len;
     DCE2_Ret status;
 
-    // FIXIT-L PORT_IF_NEEDED data_used
+/* Initialize in case we return early without adding
+     * any data to the buffer */
+    *data_used = 0;
 
     if (seg_buf == nullptr)
         return DCE2_RET__ERROR;
@@ -2280,6 +2283,10 @@ static DCE2_Ret DCE2_HandleSegmentation(DCE2_Buffer* seg_buf, const uint8_t* dat
     if (status != DCE2_RET__SUCCESS)
         return DCE2_RET__ERROR;
 
+    assert (copy_len <= data_len);
+
+    *data_used = (uint16_t)copy_len;
+
     if (DCE2_BufferLength(seg_buf) == need_len)
         return DCE2_RET__SUCCESS;
 
@@ -2294,7 +2301,7 @@ static DCE2_Ret DCE2_HandleSegmentation(DCE2_Buffer* seg_buf, const uint8_t* dat
  *
  ********************************************************************/
 static DCE2_Ret DCE2_CoHandleSegmentation(DCE2_SsnData* sd, DCE2_CoSeg* seg,
-    const uint8_t* data_ptr, uint16_t data_len, uint16_t need_len)
+    const uint8_t* data_ptr, uint16_t data_len, uint16_t need_len, uint16_t* data_used)
 {
     DCE2_Ret status;
 
@@ -2322,7 +2329,7 @@ static DCE2_Ret DCE2_CoHandleSegmentation(DCE2_SsnData* sd, DCE2_CoSeg* seg,
     }
 
     status = DCE2_HandleSegmentation(seg->buf,
-        data_ptr, data_len, need_len);
+        data_ptr, data_len, need_len, data_used);
 
     return status;
 }
@@ -2359,9 +2366,20 @@ void DCE2_CoProcess(DCE2_SsnData* sd, DCE2_CoTracker* cot,
         {
             const uint8_t* frag_ptr = data_ptr;
             uint16_t frag_len;
+            uint16_t data_used;
+
+            /* Not enough data left for a header.  Buffer it and return */
+            if (data_len < sizeof(DceRpcCoHdr))
+            {
+                DebugMessage(DEBUG_DCE_COMMON,
+                    "Not enough data in packet for DCE/RPC Connection-oriented header.\n");
 
-            // FIXIT-L PORT_IF_NEEDED
-            //Not enough data left for a headerr
+                DCE2_CoHandleSegmentation(sd, seg, data_ptr, data_len, sizeof(DceRpcCoHdr),
+                    &data_used);
+
+                /* Just break out of loop in case early detect is enabled */
+                break;
+            }
 
             if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr*)data_ptr) != DCE2_RET__SUCCESS)
                 return;
@@ -2377,7 +2395,7 @@ void DCE2_CoProcess(DCE2_SsnData* sd, DCE2_CoTracker* cot,
                 /* Set frag length so we don't have to check it again in seg code */
                 seg->frag_len = frag_len;
 
-                DCE2_CoHandleSegmentation(sd, seg, data_ptr, data_len, frag_len);
+                DCE2_CoHandleSegmentation(sd, seg, data_ptr, data_len, frag_len, &data_used);
                 goto dce2_coprocess_exit;
             }
 
@@ -2398,25 +2416,58 @@ void DCE2_CoProcess(DCE2_SsnData* sd, DCE2_CoTracker* cot,
         }
         else  /* We've already buffered data */
         {
+            uint16_t data_used = 0;
+
             DebugFormat(DEBUG_DCE_COMMON, "Segmentation buffer has %u bytes\n",
                 DCE2_BufferLength(seg->buf));
 
-            // FIXIT-L PORT_IF_NEEDED
-            //Need more data to get header
+            // Need more data to get header
+            if (DCE2_BufferLength(seg->buf) < sizeof(DceRpcCoHdr))
+            {
+                DCE2_Ret status = DCE2_CoHandleSegmentation(sd, seg, data_ptr, data_len,
+                    sizeof(DceRpcCoHdr), &data_used);
+
+                /* Still not enough for header */
+                if (status != DCE2_RET__SUCCESS)
+                    break;
+
+                /* Move the length of the amount of data we used to get header */
+                DCE2_MOVE(data_ptr, data_len, data_used);
+
+                if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr*)DCE2_BufferData(seg->buf)) !=
+                    DCE2_RET__SUCCESS)
+                {
+                    int data_back;
+                    DCE2_BufferEmpty(seg->buf);
+                    /* Move back to original packet header */
+                    data_back = -data_used;
+                    DCE2_MOVE(data_ptr, data_len, data_back);
+                    /*Check the original packet*/
+                    if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr*)data_ptr) != DCE2_RET__SUCCESS)
+                        return;
+                    else
+                    {
+                        /*Only use the original packet, ignore the data in seg_buffer*/
+                        num_frags = 0;
+                        continue;
+                    }
+                }
+
+                seg->frag_len = DceRpcCoFragLen((DceRpcCoHdr*)DCE2_BufferData(seg->buf));
+            }
 
             /* Need more data for full pdu */
             if (DCE2_BufferLength(seg->buf) < seg->frag_len)
             {
                 DCE2_Ret status = DCE2_CoHandleSegmentation(sd, seg, data_ptr, data_len,
-                    seg->frag_len);
+                    seg->frag_len, &data_used);
 
                 /* Still not enough */
                 if (status != DCE2_RET__SUCCESS)
-                    goto dce2_coprocess_exit;
-            }
+                    break;
 
-            // FIXIT-L PORT_IF_NEEDED
-            // DCE_MOVE, data_used
+                DCE2_MOVE(data_ptr, data_len, data_used);
+            }
 
             /* Do this before calling DCE2_CoSegDecode since it will empty
              * seg buffer */
@@ -2425,6 +2476,9 @@ void DCE2_CoProcess(DCE2_SsnData* sd, DCE2_CoTracker* cot,
 
             /* Got the full DCE/RPC pdu. Need to create new packet before decoding */
             DCE2_CoSegDecode(sd, cot, seg);
+
+            if ( !data_used )
+                break;
         }
     }
 
index b3c73506586be8c36de4edfc7d1a4674bbaca5ed..f6878f78d86648146d2e0c67eddf92b61ad0d8b9 100644 (file)
@@ -686,13 +686,16 @@ static uint32_t DCE2_IgnoreJunkData(const uint8_t* data_ptr, uint16_t data_len,
 static DCE2_Ret DCE2_SmbHdrChecks(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr)
 {
     Packet* p = ssd->sd.wire_pkt;
+    bool is_seg_buf = DCE2_SmbIsSegBuffer(ssd, (uint8_t*)smb_hdr);
 
     if ((DCE2_SsnFromServer(p) && (SmbType(smb_hdr) == SMB_TYPE__REQUEST)) ||
         (DCE2_SsnFromClient(p) && (SmbType(smb_hdr) == SMB_TYPE__RESPONSE)))
     {
-        // FIXIT-M port segment check
-        // Same for all cases below
-        dce_alert(GID_DCE2, DCE2_SMB_BAD_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+        if (is_seg_buf)
+            DCE2_SmbSegAlert(ssd, DCE2_SMB_BAD_TYPE);
+        else
+            dce_alert(GID_DCE2, DCE2_SMB_BAD_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+
         // Continue looking at traffic.  Neither Windows nor Samba seem
         // to care, or even look at this flag
     }
@@ -700,7 +703,11 @@ static DCE2_Ret DCE2_SmbHdrChecks(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr)
     if ((SmbId(smb_hdr) != DCE2_SMB_ID)
         && (SmbId(smb_hdr) != DCE2_SMB2_ID))
     {
-        dce_alert(GID_DCE2, DCE2_SMB_BAD_ID, (dce2CommonStats*)&dce2_smb_stats);
+        if (is_seg_buf)
+            DCE2_SmbSegAlert(ssd, DCE2_SMB_BAD_ID);
+        else
+            dce_alert(GID_DCE2, DCE2_SMB_BAD_ID, (dce2CommonStats*)&dce2_smb_stats);
+
         return DCE2_RET__IGNORE;
     }
 
@@ -1192,7 +1199,7 @@ static void DCE2_SmbProcessCommand(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr
 
         DCE2_MOVE(nb_ptr, nb_len, (off2_ptr - nb_ptr));
 
-        // FIXIT Need to test more.
+        // FIXIT-L Need to test more.
         switch (smb_com)
         {
         case SMB_COM_SESSION_SETUP_ANDX:
@@ -1773,6 +1780,7 @@ static DCE2_SmbSsnData* dce2_handle_smb_session(Packet* p, dce2SmbProtoConf* con
 static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData* ssd, const NbssHdr* nb_hdr)
 {
     Packet* p = ssd->sd.wire_pkt;
+    bool is_seg_buf = DCE2_SmbIsSegBuffer(ssd, (uint8_t*)nb_hdr);
 
     DebugMessage(DEBUG_DCE_SMB, "NetBIOS Session Service type: ");
 
@@ -1794,9 +1802,11 @@ static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData* ssd, const NbssHdr* nb_hdr)
                 DebugFormat(DEBUG_DCE_SMB, "NetBIOS SS len(%zu) < SMB header len(%zu).\n",
                     sizeof(SmbNtHdr), sizeof(NbssHdr) + nb_len);
 
-                // FIXIT-M port segment check
-                // Same for all cases below
-                dce_alert(GID_DCE2, DCE2_SMB_NB_LT_SMBHDR, (dce2CommonStats*)&dce2_smb_stats);
+                if (is_seg_buf)
+                    DCE2_SmbSegAlert(ssd, DCE2_SMB_NB_LT_SMBHDR);
+                else
+                    dce_alert(GID_DCE2, DCE2_SMB_NB_LT_SMBHDR, (dce2CommonStats*)&dce2_smb_stats);
+
                 return DCE2_RET__IGNORE;
             }
         }
@@ -1807,7 +1817,10 @@ static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData* ssd, const NbssHdr* nb_hdr)
         DebugMessage(DEBUG_DCE_SMB, "Session Request\n");
         if (DCE2_SsnFromServer(p))
         {
-            dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+            if (is_seg_buf)
+                DCE2_SmbSegAlert(ssd, DCE2_SMB_BAD_NBSS_TYPE);
+            else
+                dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
         }
 
         break;
@@ -1817,7 +1830,10 @@ static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData* ssd, const NbssHdr* nb_hdr)
     case NBSS_SESSION_TYPE__RETARGET_RESPONSE:
         if (DCE2_SsnFromClient(p))
         {
-            dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+            if (is_seg_buf)
+                DCE2_SmbSegAlert(ssd, DCE2_SMB_BAD_NBSS_TYPE);
+            else
+                dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
         }
 
         break;
@@ -1829,7 +1845,11 @@ static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData* ssd, const NbssHdr* nb_hdr)
     default:
         DebugFormat(DEBUG_DCE_SMB,
             "Invalid Session Service type: 0x%02X\n", NbssType(nb_hdr));
-        dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+
+        if (is_seg_buf)
+            DCE2_SmbSegAlert(ssd, DCE2_SMB_BAD_NBSS_TYPE);
+        else
+            dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
 
         return DCE2_RET__ERROR;
     }
@@ -1857,6 +1877,7 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
     const Packet* p = ssd->sd.wire_pkt;
     const uint8_t* data_ptr = p->data;
     uint16_t data_len = p->dsize;
+    DCE2_Buffer** seg_buf = DCE2_SmbGetSegBuffer(ssd);
 
     /* Have to account for segmentation.  Even though stream will give
      * us larger chunks, we might end up in the middle of something */
@@ -1901,12 +1922,35 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
                 DebugFormat(DEBUG_DCE_SMB, "Data len(%hu) < NetBIOS SS header(%u). "
                     "Queueing data.\n", data_len, data_need);
 
-                // FIXIT-M port segmentation code
+                if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr,
+                    data_len, sizeof(NbssHdr)) != DCE2_RET__SUCCESS)
+                {
+                    DCE2_BufferEmpty(*seg_buf);
+                }
+
                 return;
             }
 
             // Set the NetBIOS header structure
-            NbssHdr* nb_hdr = (NbssHdr*)data_ptr;
+            NbssHdr* nb_hdr;
+            if (DCE2_BufferIsEmpty(*seg_buf))
+            {
+                nb_hdr = (NbssHdr*)data_ptr;
+            }
+            else
+            {
+                // If data already buffered add the remainder for the
+                // size of the NetBIOS header
+                if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr,
+                    data_need, sizeof(NbssHdr)) != DCE2_RET__SUCCESS)
+                {
+                    DCE2_BufferEmpty(*seg_buf);
+                    return;
+                }
+
+                nb_hdr = (NbssHdr*)DCE2_BufferData(*seg_buf);
+            }
+
             uint32_t nb_len = NbssLen(nb_hdr);
 
             DebugFormat(DEBUG_DCE_SMB, "NetBIOS PDU length: %u\n", nb_len);
@@ -1930,10 +1974,14 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
                     *ignore_bytes = DCE2_IgnoreJunkData(data_ptr, data_len, data_need + nb_len);
                 }
 
+                DCE2_BufferEmpty(*seg_buf);
                 dce2_smb_stats.smb_ignored_bytes += *ignore_bytes;
                 continue;
             }
 
+            if (!DCE2_BufferIsEmpty(*seg_buf))
+                DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need);
+
             switch (ssd->pdu_state)
             {
             case DCE2_SMB_PDU_STATE__COMMAND:
@@ -1959,9 +2007,9 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
         // there won't be any DCE/RPC traffic associated with it.
         case DCE2_SMB_DATA_STATE__SMB_HEADER:
         {
-            // FIXIT-M add segmentation code path, including seg_buf code to the entire state
+            uint32_t data_need = (sizeof(NbssHdr) + sizeof(SmbNtHdr)) - DCE2_BufferLength(
+                *seg_buf);
 
-            uint32_t data_need = (sizeof(NbssHdr) + sizeof(SmbNtHdr));
             // See if there is enough data to process the SMB header
             if (data_len < data_need)
             {
@@ -1969,12 +2017,34 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
                     "NetBIOS SS header + SMB header (%u). Queueing data.\n",
                     data_len, data_need);
 
-                // FIXIT-M add segmentation code path
+                if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_len,
+                    sizeof(NbssHdr) + sizeof(SmbNtHdr)) != DCE2_RET__SUCCESS)
+                {
+                    DCE2_BufferEmpty(*seg_buf);
+                    *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
+                }
+
                 return;
             }
 
-            // FIXIT-M add segmentation checks
-            SmbNtHdr* smb_hdr = (SmbNtHdr*)(data_ptr + sizeof(NbssHdr));
+            // Set the SMB header structure
+            SmbNtHdr* smb_hdr;
+            if (DCE2_BufferIsEmpty(*seg_buf))
+            {
+                smb_hdr = (SmbNtHdr*)(data_ptr + sizeof(NbssHdr));
+            }
+            else
+            {
+                if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_need,
+                    sizeof(NbssHdr) + sizeof(SmbNtHdr)) != DCE2_RET__SUCCESS)
+                {
+                    DCE2_BufferEmpty(*seg_buf);
+                    *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
+                    return;
+                }
+
+                smb_hdr = (SmbNtHdr*)(DCE2_BufferData(*seg_buf) + sizeof(NbssHdr));
+            }
 
             // FIXIT-L Don't support SMB2 yet
             if (SmbId(smb_hdr) == DCE2_SMB2_ID)
@@ -1989,8 +2059,16 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
             {
                 DebugMessage(DEBUG_DCE_SMB, "Not inspecting SMB packet.\n");
 
-                // FIXIT-M add segmentation
-                *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr*)data_ptr);
+                if (DCE2_BufferIsEmpty(*seg_buf))
+                {
+                    *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr*)data_ptr);
+                }
+                else
+                {
+                    *ignore_bytes = (NbssLen((NbssHdr*)DCE2_BufferData(*seg_buf))
+                        - sizeof(SmbNtHdr)) + data_need;
+                    DCE2_BufferEmpty(*seg_buf);
+                }
 
                 *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
                 dce2_smb_stats.smb_ignored_bytes += *ignore_bytes;
@@ -2002,7 +2080,16 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
             {
                 DebugMessage(DEBUG_DCE_SMB, "Bad SMB header.\n");
 
-                *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr*)data_ptr);
+                if (DCE2_BufferIsEmpty(*seg_buf))
+                {
+                    *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr*)data_ptr);
+                }
+                else
+                {
+                    *ignore_bytes = (NbssLen((NbssHdr*)DCE2_BufferData(*seg_buf))
+                        - sizeof(SmbNtHdr)) + data_need;
+                    DCE2_BufferEmpty(*seg_buf);
+                }
 
                 *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
 
@@ -2010,6 +2097,9 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
                 continue;
             }
 
+            if (!DCE2_BufferIsEmpty(*seg_buf))
+                DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need);
+
             *data_state = DCE2_SMB_DATA_STATE__NETBIOS_PDU;
         }
 
@@ -2019,8 +2109,19 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
         // to process.
         case DCE2_SMB_DATA_STATE__NETBIOS_PDU:
         {
-            uint32_t nb_len = NbssLen((NbssHdr*)data_ptr);
-            uint32_t data_need = sizeof(NbssHdr) + nb_len;
+            uint32_t nb_len;
+            uint32_t data_need;
+
+            if (DCE2_BufferIsEmpty(*seg_buf))
+            {
+                nb_len = NbssLen((NbssHdr*)data_ptr);
+                data_need = sizeof(NbssHdr) + nb_len;
+            }
+            else
+            {
+                nb_len = NbssLen((NbssHdr*)DCE2_BufferData(*seg_buf));
+                data_need = (sizeof(NbssHdr) + nb_len) - DCE2_BufferLength(*seg_buf);
+            }
 
             /* It's something we want to inspect so make sure we have the full NBSS packet */
             if (data_len < data_need)
@@ -2029,7 +2130,12 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
                     "NetBIOS SS header + NetBIOS len(%zu). "
                     "Queueing data.\n", data_len, sizeof(NbssHdr) + nb_len);
 
-                // FIXIT-M add segmentation code
+                if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_len,
+                    sizeof(NbssHdr) + nb_len) != DCE2_RET__SUCCESS)
+                {
+                    DCE2_BufferEmpty(*seg_buf);
+                    *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
+                }
 
                 return;
             }
@@ -2039,9 +2145,52 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
 
             *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
 
-            const uint8_t* nb_ptr = data_ptr;
-            nb_len = data_need;
-            DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need);
+            const uint8_t* nb_ptr;
+            if (DCE2_BufferIsEmpty(*seg_buf))
+            {
+                nb_ptr = data_ptr;
+                nb_len = data_need;
+                DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need);
+            }
+            else
+            {
+                if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_need,
+                    sizeof(NbssHdr) + nb_len) != DCE2_RET__SUCCESS)
+                {
+                    DCE2_BufferEmpty(*seg_buf);
+                    DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need);
+                    continue;
+                }
+
+                DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need);
+
+                nb_ptr = DCE2_BufferData(*seg_buf);
+                nb_len = DCE2_BufferLength(*seg_buf);
+
+                // Get reassembled packet
+                Packet* rpkt = DCE2_SmbGetRpkt(ssd, &nb_ptr, &nb_len,
+                    DCE2_RPKT_TYPE__SMB_SEG);
+                if (rpkt == nullptr)
+                {
+                    DCE2_BufferEmpty(*seg_buf);
+                    continue;
+                }
+
+                nb_ptr = DCE2_BufferData(*seg_buf);
+                nb_len = DCE2_BufferLength(*seg_buf);
+
+                DebugFormat(DEBUG_DCE_SMB,
+                    "Segmentation buffer: len: %u, size: %u\n",
+                    DCE2_BufferLength(*seg_buf), DCE2_BufferSize(*seg_buf));
+
+                if (DCE2_SsnFromClient(ssd->sd.wire_pkt))
+                    dce2_smb_stats.smb_cli_seg_reassembled++;
+                else
+                    dce2_smb_stats.smb_srv_seg_reassembled++;
+
+                DebugMessage(DEBUG_DCE_SMB, "TCP reassembled SMB PDU\n");
+                DCE2_PrintPktData(rpkt->data, rpkt->dsize);
+            }
 
             switch (ssd->pdu_state)
             {
@@ -2068,6 +2217,14 @@ static void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
                     "state: %d\n", __FILE__, __LINE__, ssd->pdu_state);
                 return;
             }
+
+            if (!DCE2_BufferIsEmpty(*seg_buf))
+            {
+                DCE2_SmbReturnRpkt(ssd);
+                DCE2_BufferDestroy(*seg_buf);
+                *seg_buf = nullptr;
+            }
+
             break;
         }
 
index 94ff9aba111f738d522a94e41c7573e502f708ad..bb57cf8d5eec6626602ddb6f2e30f1576a9d9a99 100644 (file)
@@ -1318,3 +1318,129 @@ Packet* DCE2_SmbGetRpkt(DCE2_SmbSsnData* ssd,
     return rpkt;
 }
 
+/********************************************************************
+ * Function: DCE2_SmbHandleSegmentation()
+ *
+ * Wrapper around DCE2_HandleSegmentation() to allocate a new
+ * buffer object if necessary.
+ *
+ * Arguments:
+ *  DCE2_SmbBuffer **
+ *      Pointer to pointer of buffer to add data to.  If NULL
+ *      a new buffer will be allocated.
+ *  uint8_t *
+ *      Pointer to the current data cursor in packet.
+ *  uint32_t
+ *      Length of data to add to buffer.
+ *  uint32_t
+ *      The minimum allocation size so that small allocations
+ *      aren't consistently done.
+ *
+ * Returns:
+ *  DCE2_Ret
+ *      DCE2_RET__ERROR if an error occured.  Nothing can
+ *          be trusted.
+ *      DCE2_RET__SUCCESS if data was successfully added.
+ *
+ ********************************************************************/
+DCE2_Ret DCE2_SmbHandleSegmentation(DCE2_Buffer** buf,
+    const uint8_t* data_ptr, uint32_t add_len, uint32_t alloc_size)
+{
+    Profile profile(dce2_smb_pstat_smb_seg);
+
+    if (buf == nullptr)
+    {
+        return DCE2_RET__ERROR;
+    }
+
+    if (*buf == nullptr)
+    {
+        /* No initial size or min alloc size */
+        *buf = DCE2_BufferNew(alloc_size, alloc_size);
+    }
+
+    DCE2_Ret status = DCE2_BufferAddData(*buf, data_ptr, add_len,
+        DCE2_BufferLength(*buf), DCE2_BUFFER_MIN_ADD_FLAG__IGNORE);
+
+    return status;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbIsSegBuffer()
+ *
+ * Purpose:
+ *  Determines whether the pointer passed in lies within one of the
+ *  segmentation buffers or not.
+ *
+ * Arguments:
+ *  DCE2_SmbSsnData *
+ *      Pointer to SMB session data.
+ *
+ * Returns:
+ *  bool  -  True is the pointer lies within one of the segmentation
+ *           buffers.
+ *           False if it doesn't.
+ *
+ ********************************************************************/
+bool DCE2_SmbIsSegBuffer(DCE2_SmbSsnData* ssd, const uint8_t* ptr)
+{
+    DCE2_Buffer* seg_buf;
+
+    if (DCE2_SsnFromServer(ssd->sd.wire_pkt))
+        seg_buf = ssd->srv_seg;
+    else
+        seg_buf = ssd->cli_seg;
+
+    if (DCE2_BufferIsEmpty(seg_buf))
+        return false;
+
+    /* See if we're looking at a segmentation buffer */
+    if ((ptr < DCE2_BufferData(seg_buf)) ||
+        (ptr > (DCE2_BufferData(seg_buf) + DCE2_BufferLength(seg_buf))))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbSegAlert()
+ *
+ * Purpose:
+ *  To create a reassembled packet using the data in one of the
+ *  segmentation buffers in order to generate an alert with the
+ *  correct, or more complete data.
+ *
+ * Arguments:
+ *  DCE2_SmbSsnData * - Pointer to SMB session data.
+ *  rule_id -  rule id .
+ *
+ * Returns: None
+ *
+ ********************************************************************/
+void DCE2_SmbSegAlert(DCE2_SmbSsnData* ssd, uint32_t rule_id)
+{
+    DCE2_Buffer* buf;
+
+    if (DCE2_SsnFromClient(ssd->sd.wire_pkt))
+        buf = ssd->cli_seg;
+    else
+        buf = ssd->srv_seg;
+
+    /* This should be called from the desegmentation code. */
+    if (DCE2_BufferIsEmpty(buf))
+        return;
+
+    const uint8_t* data_ptr = DCE2_BufferData(buf);
+    uint32_t data_len = DCE2_BufferLength(buf);
+
+    Packet* rpkt = DCE2_SmbGetRpkt(ssd, &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_SEG);
+    if (rpkt == nullptr)
+        return;
+
+    dce_alert(GID_DCE2, rule_id, (dce2CommonStats*)&dce2_smb_stats);
+
+    DCE2_SmbReturnRpkt(ssd);
+}
+
index 369663591e9ae2e4f802b107936e5a81fb073467..7720cd07927427344b04913cb8a784408e30ac61 100644 (file)
@@ -165,8 +165,12 @@ DCE2_Ret DCE2_SmbProcessResponseData(DCE2_SmbSsnData*,
     const uint8_t*, uint32_t);
 void DCE2_SmbInitRdata(uint8_t*, int);
 void DCE2_SmbSetRdata(DCE2_SmbSsnData*, uint8_t*, uint16_t);
-Packet* DCE2_SmbGetRpkt(DCE2_SmbSsnData*, const uint8_t **,
+Packet* DCE2_SmbGetRpkt(DCE2_SmbSsnData*, const uint8_t**,
     uint32_t*, DCE2_RpktType);
+DCE2_Ret DCE2_SmbHandleSegmentation(DCE2_Buffer**,
+    const uint8_t*, uint32_t, uint32_t);
+bool DCE2_SmbIsSegBuffer(DCE2_SmbSsnData*, const uint8_t*);
+void DCE2_SmbSegAlert(DCE2_SmbSsnData*, uint32_t rule_id);
 
 /********************************************************************
  * Inline functions
@@ -331,5 +335,12 @@ inline void DCE2_SmbReturnRpkt(DCE2_SmbSsnData* ssd)
     DCE2_PopPkt(&ssd->sd);
 }
 
+inline DCE2_Buffer** DCE2_SmbGetSegBuffer(DCE2_SmbSsnData* ssd)
+{
+    if (DCE2_SsnFromServer(ssd->sd.wire_pkt))
+        return &ssd->srv_seg;
+    return &ssd->cli_seg;
+}
+
 #endif
 
index 4ff7c6532592a6d311b3d019467647185b5176b7..e47d7513601aa0a8d1d97314cddb9c838858dcca 100644 (file)
@@ -376,6 +376,13 @@ inline uint32_t DCE2_BufferLength(DCE2_Buffer* buf)
     return buf->len;
 }
 
+inline uint32_t DCE2_BufferSize(DCE2_Buffer* buf)
+{
+    if (buf == nullptr)
+        return 0;
+    return buf->size;
+}
+
 inline uint8_t* DCE2_BufferData(DCE2_Buffer* buf)
 {
     if (buf == nullptr)