#include "log/messages.h"
#include "main/snort_debug.h"
#include "utils/util.h"
+#include <assert.h>
THREAD_LOCAL int co_reassembled = 0;
}
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;
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;
*
********************************************************************/
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;
}
status = DCE2_HandleSegmentation(seg->buf,
- data_ptr, data_len, need_len);
+ data_ptr, data_len, need_len, data_used);
return status;
}
{
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;
/* 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;
}
}
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 */
/* Got the full DCE/RPC pdu. Need to create new packet before decoding */
DCE2_CoSegDecode(sd, cot, seg);
+
+ if ( !data_used )
+ break;
}
}
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
}
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;
}
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:
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: ");
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;
}
}
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;
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;
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;
}
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 */
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);
*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:
// 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)
{
"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)
{
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;
{
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;
continue;
}
+ if (!DCE2_BufferIsEmpty(*seg_buf))
+ DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need);
+
*data_state = DCE2_SMB_DATA_STATE__NETBIOS_PDU;
}
// 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)
"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;
}
*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)
{
"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;
}
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);
+}
+