SO_PUBLIC int SnortEventqAdd(uint32_t gid, uint32_t sid, RuleType = RULE_TYPE__NONE);
SO_PUBLIC bool event_is_enabled(uint32_t gid, uint32_t sid);
-void SnortEventqPush(void);
-void SnortEventqPop(void);
+SO_PUBLIC void SnortEventqPush(void);
+SO_PUBLIC void SnortEventqPop(void);
#endif
// based on work by Todd Wease
#include "dce_co.h"
+#include "dce_tcp.h"
+#include "dce_smb.h"
+#include "dce_list.h"
+#include "dce_utils.h"
+#include "profiler/profiler.h"
+#include "main/snort_debug.h"
+#include "log/messages.h"
+
+THREAD_LOCAL int co_reassembled = 0;
/********************************************************************
* Function: DCE2_CoInitTracker()
* these fields have been set.
*
********************************************************************/
-void DCE2_CoInitTracker(DCE2_CoTracker *cot)
+void DCE2_CoInitTracker(DCE2_CoTracker* cot)
{
- if (cot == NULL)
+ if (cot == nullptr)
return;
cot->max_xmit_frag = DCE2_SENTINEL;
cot->ctx_id = DCE2_SENTINEL;
cot->opnum = DCE2_SENTINEL;
cot->call_id = DCE2_SENTINEL;
- cot->stub_data = NULL;
+ cot->stub_data = nullptr;
cot->got_bind = 0;
cot->frag_tracker.opnum = DCE2_SENTINEL;
cot->frag_tracker.expected_ctx_id = DCE2_SENTINEL;
}
+/********************************************************************
+ * Function: DCE2_CoResetFragTracker()
+ *
+ * Resets frag tracker fields after having reassembled.
+ *
+ ********************************************************************/
+static inline void DCE2_CoResetFragTracker(DCE2_CoFragTracker* ft)
+{
+ if (ft == nullptr)
+ return;
+
+ ft->opnum = DCE2_SENTINEL;
+ ft->ctx_id = DCE2_SENTINEL;
+ ft->expected_call_id = DCE2_SENTINEL;
+ ft->expected_ctx_id = DCE2_SENTINEL;
+ ft->expected_opnum = DCE2_SENTINEL;
+}
+
+/********************************************************************
+ * Function: DCE2_CoResetTracker()
+ *
+ * Resets fields that are transient for requests after the bind or
+ * alter context. The context id and opnum are dependent on the
+ * request and in the case of fragmented requests are set until all
+ * fragments are received. If we got a full request or all of the
+ * fragments, these should be reset.
+ *
+ ********************************************************************/
+static inline void DCE2_CoResetTracker(DCE2_CoTracker* cot)
+{
+ if (cot == nullptr)
+ return;
+
+ cot->ctx_id = DCE2_SENTINEL;
+ cot->opnum = DCE2_SENTINEL;
+ cot->call_id = DCE2_SENTINEL;
+ cot->stub_data = nullptr;
+
+ DCE2_CoResetFragTracker(&cot->frag_tracker);
+}
+
+static inline bool DCE2_CoIsSegBuf(DCE2_SsnData* sd, DCE2_CoTracker* cot, const uint8_t* ptr)
+{
+ DCE2_Buffer* seg_buf;
+
+ if (DCE2_SsnFromServer(sd->wire_pkt))
+ seg_buf = cot->srv_seg.buf;
+ else
+ seg_buf = cot->cli_seg.buf;
+
+ if (DCE2_BufferIsEmpty(seg_buf))
+ return 0;
+
+ /* 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 0;
+ }
+
+ return 1;
+}
+
+static inline DCE2_CoSeg* DCE2_CoGetSegPtr(DCE2_SsnData* sd, DCE2_CoTracker* cot)
+{
+ if (DCE2_SsnFromServer(sd->wire_pkt))
+ return &cot->srv_seg;
+
+ return &cot->cli_seg;
+}
+
+/********************************************************************
+ * Function: DCE2_CoSetIface()
+ *
+ * Sets the interface UUID for the rules options. Looks in the
+ * context id list. If nothing found there, it looks in the pending
+ * list (in case we never saw the server response because of
+ * missed packets) to see if something is there.
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_CoSetIface(DCE2_SsnData* sd, DCE2_CoTracker* cot, uint16_t ctx_id)
+{
+ DCE2_CoCtxIdNode* ctx_id_node;
+
+ /* This should be set if we've gotten a Bind */
+ if (cot->ctx_ids == nullptr)
+ return DCE2_RET__ERROR;
+ if (sd->trans == DCE2_TRANS_TYPE__TCP)
+ {
+ Profile profile(dce2_tcp_pstat_co_ctx);
+ }
+ else
+ {
+ Profile profile(dce2_smb_pstat_co_ctx);
+ }
+
+ ctx_id_node = (DCE2_CoCtxIdNode*)DCE2_ListFind(cot->ctx_ids, (void*)(uintptr_t)ctx_id);
+ if (ctx_id_node == nullptr) /* context id not found in list */
+ {
+ /* See if it's in the queue. An easy evasion would be to stagger the writes
+ * and reads such that we see a request before seeing the server bind ack */
+ if (cot->pending_ctx_ids != nullptr)
+ {
+ for (ctx_id_node = (DCE2_CoCtxIdNode*)DCE2_QueueFirst(cot->pending_ctx_ids);
+ ctx_id_node != nullptr;
+ ctx_id_node = (DCE2_CoCtxIdNode*)DCE2_QueueNext(cot->pending_ctx_ids))
+ {
+ if (ctx_id_node->ctx_id == ctx_id)
+ break;
+ }
+ }
+
+ if (ctx_id_node == nullptr)
+ {
+ return DCE2_RET__ERROR;
+ }
+ }
+
+ if (ctx_id_node->state == DCE2_CO_CTX_STATE__REJECTED)
+ {
+ return DCE2_RET__ERROR;
+ }
+
+ DCE2_CopyUuid(&sd->ropts.iface, &ctx_id_node->iface, DCERPC_BO_FLAG__NONE);
+ sd->ropts.iface_vers_maj = ctx_id_node->iface_vers_maj;
+ sd->ropts.iface_vers_min = ctx_id_node->iface_vers_min;
+
+ return DCE2_RET__SUCCESS;
+}
+
+/********************************************************************
+ * Function: DCE2_CoSetRopts()
+ *
+ * Sets values necessary for the rule options.
+ *
+ ********************************************************************/
+static inline void DCE2_CoSetRopts(DCE2_SsnData* sd, DCE2_CoTracker* cot, const
+ DceRpcCoHdr* co_hdr)
+{
+ DCE2_CoFragTracker* ft = &cot->frag_tracker;
+ int opnum = (ft->opnum != DCE2_SENTINEL) ? ft->opnum : cot->opnum;
+ int ctx_id = (ft->ctx_id != DCE2_SENTINEL) ? ft->ctx_id : cot->ctx_id;
+
+ int data_byte_order =
+ (cot->data_byte_order != DCE2_SENTINEL) ?
+ cot->data_byte_order : (int)DceRpcCoByteOrder(co_hdr);
+
+ if (DCE2_CoSetIface(sd, cot, (uint16_t)ctx_id) != DCE2_RET__SUCCESS)
+ sd->ropts.first_frag = DCE2_SENTINEL;
+ else
+ sd->ropts.first_frag = DceRpcCoFirstFrag(co_hdr);
+
+ sd->ropts.hdr_byte_order = DceRpcCoByteOrder(co_hdr);
+ sd->ropts.data_byte_order = data_byte_order;
+ sd->ropts.opnum = opnum;
+ sd->ropts.stub_data = cot->stub_data;
+}
+
+static inline dce2CommonStats* dce_get_proto_stats_ptr(DCE2_SsnData* sd)
+{
+ if (sd->trans == DCE2_TRANS_TYPE__TCP)
+ {
+ return((dce2CommonStats*)&dce2_tcp_stats);
+ }
+ else
+ {
+ return((dce2CommonStats*)&dce2_smb_stats);
+ }
+}
+
+/********************************************************************
+ * Function: DCE2_CoHdrChecks()
+ *
+ * Checks some relevant fields in the header to make sure they're
+ * sane.
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_CoHdrChecks(DCE2_SsnData* sd, DCE2_CoTracker* cot, const DceRpcCoHdr* co_hdr)
+{
+ uint16_t frag_len = DceRpcCoFragLen(co_hdr);
+ DceRpcPduType pdu_type = DceRpcCoPduType(co_hdr);
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ if (frag_len < sizeof(DceRpcCoHdr))
+ {
+ /* Assume we autodetected incorrectly or that DCE/RPC is not running
+ * over the SMB named pipe */
+ if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB))
+ {
+ //FIXIT-M add segment check
+ dce_alert(GID_DCE2, DCE2_CO_FRAG_LEN_LT_HDR,dce_common_stats);
+ }
+
+ return DCE2_RET__ERROR;
+ }
+
+ if (DceRpcCoVersMaj(co_hdr) != DCERPC_PROTO_MAJOR_VERS__5)
+ {
+ if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB))
+ {
+ //FIXIT-M add segment check
+ dce_alert(GID_DCE2, DCE2_CO_BAD_MAJOR_VERSION,dce_common_stats);
+ }
+
+ return DCE2_RET__ERROR;
+ }
+
+ if (DceRpcCoVersMin(co_hdr) != DCERPC_PROTO_MINOR_VERS__0)
+ {
+ if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB))
+ {
+ //FIXIT-M add segment check
+ dce_alert(GID_DCE2, DCE2_CO_BAD_MINOR_VERSION,dce_common_stats);
+ }
+
+ return DCE2_RET__ERROR;
+ }
+ if (pdu_type >= DCERPC_PDU_TYPE__MAX)
+ {
+ if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB))
+ {
+ //FIXIT-M add segment check
+
+ dce_alert(GID_DCE2, DCE2_CO_BAD_PDU_TYPE,dce_common_stats);
+ }
+
+ return DCE2_RET__ERROR;
+ }
+
+ if (DCE2_SsnFromClient(sd->wire_pkt) && (cot->max_xmit_frag != DCE2_SENTINEL))
+ {
+ if (frag_len > cot->max_xmit_frag)
+ {
+ //FIXIT-M add segment check
+ dce_alert(GID_DCE2, DCE2_CO_FRAG_GT_MAX_XMIT_FRAG,dce_common_stats);
+ }
+ else if (!DceRpcCoLastFrag(co_hdr) && (pdu_type == DCERPC_PDU_TYPE__REQUEST)
+ && ((((int)cot->max_xmit_frag - DCE2_MAX_XMIT_SIZE_FUZZ) < 0)
+ || ((int)frag_len < ((int)cot->max_xmit_frag - DCE2_MAX_XMIT_SIZE_FUZZ))))
+ {
+ /* If client needs to fragment the DCE/RPC request, it shouldn't be less than the
+ * maximum xmit size negotiated. Only if it's not a last fragment. Make this alert
+ * only if it is considerably less - have seen legitimate fragments that are just
+ * slightly less the negotiated fragment size. */
+
+ //FIXIT-M add segment check
+ dce_alert(GID_DCE2, DCE2_CO_FRAG_LT_MAX_XMIT_FRAG,dce_common_stats);
+ }
+
+ /* Continue processing */
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+/********************************************************************
+ * Function: DCE2_CoCtxCompare()
+ *
+ * Callback to context id list for finding the right interface
+ * UUID node. Values passed in are context ids which are used as
+ * the keys for the list.
+ *
+ ********************************************************************/
+static int DCE2_CoCtxCompare(const void* a, const void* b)
+{
+ int x = (int)(uintptr_t)a;
+ int y = (int)(uintptr_t)b;
+
+ if (x == y)
+ return 0;
+
+ /* Only care about equality for finding */
+ return -1;
+}
+
+/********************************************************************
+ * Function: DCE2_CoCtxFree()
+ *
+ * Callback to context id list for freeing context id nodes in
+ * the list.
+ *
+ ********************************************************************/
+static void DCE2_CoCtxFree(void* data)
+{
+ if (data == nullptr)
+ return;
+
+ free(data);
+}
+
+/********************************************************************
+ * Function: DCE2_CoInitCtxStorage()
+ *
+ * Allocates, if necessary, and initializes the context id list
+ * and the context id pending queue.
+ *
+ *
+ ********************************************************************/
+static inline DCE2_Ret DCE2_CoInitCtxStorage(DCE2_CoTracker* cot)
+{
+ if (cot == nullptr)
+ return DCE2_RET__ERROR;
+
+ if (cot->ctx_ids == nullptr)
+ {
+ cot->ctx_ids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_CoCtxCompare, DCE2_CoCtxFree,
+ nullptr, DCE2_LIST_FLAG__NO_DUPS);
+ if (cot->ctx_ids == nullptr)
+ return DCE2_RET__ERROR;
+ }
+
+ if (cot->pending_ctx_ids == nullptr)
+ {
+ cot->pending_ctx_ids = DCE2_QueueNew(DCE2_CoCtxFree);
+ if (cot->pending_ctx_ids == nullptr)
+ {
+ DCE2_ListDestroy(cot->ctx_ids);
+ cot->ctx_ids = nullptr;
+ return DCE2_RET__ERROR;
+ }
+ }
+ else if (!DCE2_QueueIsEmpty(cot->pending_ctx_ids))
+ {
+ DCE2_QueueEmpty(cot->pending_ctx_ids);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+/********************************************************************
+ * Function: DCE2_CoEraseCtxIds()
+ *
+ * Empties out the context id list and the pending context id
+ * queue. Does not free the list and queue - might need to still
+ * use them.
+ *
+ ********************************************************************/
+static inline void DCE2_CoEraseCtxIds(DCE2_CoTracker* cot)
+{
+ if (cot == nullptr)
+ return;
+
+ DCE2_QueueEmpty(cot->pending_ctx_ids);
+ DCE2_ListEmpty(cot->ctx_ids);
+}
+
+DCE2_CoCtxIdNode* dce_process_ctx_id(DCE2_SsnData* sd,DCE2_CoTracker* cot,
+ const DceRpcCoHdr* co_hdr,DCE2_Policy policy,
+ const uint8_t* frag_ptr, uint16_t frag_len)
+{
+ DCE2_CoCtxIdNode* ctx_node;
+ DCE2_Ret status;
+ uint16_t ctx_id;
+ uint8_t num_tsyns;
+ const Uuid* iface;
+ uint16_t if_vers_maj;
+ uint16_t if_vers_min;
+ DceRpcCoContElem* ctx_elem = (DceRpcCoContElem*)frag_ptr;
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ int j;
+
+ if (frag_len < sizeof(DceRpcCoContElem))
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE, dce_common_stats);
+ return nullptr;
+ }
+
+ ctx_id = DceRpcCoContElemCtxId(co_hdr, ctx_elem);
+ num_tsyns = DceRpcCoContElemNumTransSyntaxes(ctx_elem);
+ iface = DceRpcCoContElemIface(ctx_elem);
+ if_vers_maj = DceRpcCoContElemIfaceVersMaj(co_hdr, ctx_elem);
+ if_vers_min = DceRpcCoContElemIfaceVersMin(co_hdr, ctx_elem);
+
+ /* No transfer syntaxes */
+ if (num_tsyns == 0)
+ {
+ dce_alert(GID_DCE2, DCE2_CO_NO_TFER_SYNTAX_SPECFD,dce_common_stats);
+ return nullptr;
+ }
+
+ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoContElem));
+
+ /* Don't really care about the transfer syntaxes */
+ for (j = 0; j < num_tsyns; j++)
+ {
+ if (frag_len < sizeof(DceRpcCoSynId))
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE, dce_common_stats);
+ return nullptr;
+ }
+
+ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoSynId));
+ }
+ if (sd->trans == DCE2_TRANS_TYPE__TCP)
+ {
+ Profile profile(dce2_tcp_pstat_co_ctx);
+ }
+ else
+ {
+ Profile profile(dce2_smb_pstat_co_ctx);
+ }
+
+ /* If there is already an accepted node with in the list
+ * with this ctx, just return */
+ if (policy == DCE2_POLICY__SAMBA_3_0_20)
+ {
+ ctx_node = (DCE2_CoCtxIdNode*)DCE2_ListFind(cot->ctx_ids, (void*)(uintptr_t)ctx_id);
+ if ((ctx_node != nullptr) && (ctx_node->state != DCE2_CO_CTX_STATE__REJECTED))
+ {
+ return nullptr;
+ }
+ }
+
+ ctx_node = (DCE2_CoCtxIdNode*)calloc(sizeof(DCE2_CoCtxIdNode),1);
+ if (ctx_node == nullptr)
+ {
+ return nullptr;
+ }
+
+ /* Add context id to pending queue */
+ status = DCE2_QueueEnqueue(cot->pending_ctx_ids, ctx_node);
+ if (status != DCE2_RET__SUCCESS)
+ {
+ free(ctx_node);
+ return nullptr;
+ }
+
+ /* This node will get moved to the context id list upon server response */
+ ctx_node->ctx_id = ctx_id;
+ DCE2_CopyUuid(&ctx_node->iface, iface, DceRpcCoByteOrder(co_hdr));
+ ctx_node->iface_vers_maj = if_vers_maj;
+ ctx_node->iface_vers_min = if_vers_min;
+ ctx_node->state = DCE2_CO_CTX_STATE__PENDING;
+ return ctx_node;
+}
+
+/********************************************************************
+ * Function: DCE2_CoCtxReq()
+ *
+ * Handles parsing the context id list out of the packet.
+ * Context ids and associated uuids are stored in a queue and
+ * dequeued upon server response. Server response doesn't
+ * indicate by context id which bindings were accepted or
+ * rejected, but the index or order they were in in the client
+ * bind or alter context, hence the queue.
+ *
+ ********************************************************************/
+static void DCE2_CoCtxReq(DCE2_SsnData* sd, DCE2_CoTracker* cot, const DceRpcCoHdr* co_hdr,
+ const uint8_t num_ctx_items, const uint8_t* frag_ptr, uint16_t frag_len)
+{
+ DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
+ unsigned int i;
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ if (num_ctx_items == 0)
+ {
+ dce_alert(GID_DCE2, DCE2_CO_NO_CTX_ITEMS_SPECFD, dce_common_stats);
+ return;
+ }
+
+ for (i = 0; i < num_ctx_items; i++)
+ {
+ DCE2_CoCtxIdNode* ctx_node;
+
+ ctx_node = dce_process_ctx_id(sd,cot,co_hdr,policy,frag_ptr,frag_len);
+ if ((ctx_node == nullptr))
+ {
+ return;
+ }
+
+ DebugFormat(DEBUG_DCE_COMMON, "Added Context item to queue.\n"
+ " Context id: %u\n"
+ " Interface: %s\n"
+ " Interface major version: %u\n"
+ " Interface minor version: %u\n",
+ ctx_node->ctx_id,
+ DCE2_UuidToStr(&ctx_node->iface, DCERPC_BO_FLAG__NONE),
+ ctx_node->iface_vers_maj, ctx_node->iface_vers_min);
+
+ switch (policy)
+ {
+ 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:
+ /* Samba only ever looks at one context item. Not sure
+ * if this is an alertable offense */
+ return;
+
+ default:
+ break;
+ }
+ }
+}
+
+void dce_process_ctx_result(DCE2_SsnData* sd,DCE2_CoTracker* cot,
+ const DceRpcCoHdr* co_hdr,DCE2_Policy policy,
+ uint16_t result)
+{
+ DCE2_CoCtxIdNode* ctx_node, * existing_ctx_node;
+ DCE2_Ret status;
+
+ if (sd->trans == DCE2_TRANS_TYPE__TCP)
+ {
+ Profile profile(dce2_tcp_pstat_co_ctx);
+ }
+ else
+ {
+ Profile profile(dce2_smb_pstat_co_ctx);
+ }
+
+ /* Dequeue context item in pending queue - this will get put in the permanent
+ * context id list or free'd */
+ 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__);
+ return;
+ }
+
+ DebugFormat(DEBUG_DCE_COMMON, "Adding Context item to context item list.\n"
+ " Context id: %u\n"
+ " Interface: %s\n"
+ " Interface major version: %u\n"
+ " Interface minor version: %u\n",
+ ctx_node->ctx_id,
+ DCE2_UuidToStr(&ctx_node->iface, DCERPC_BO_FLAG__NONE),
+ ctx_node->iface_vers_maj, ctx_node->iface_vers_min);
+
+ if (result == DCERPC_CO_CONT_DEF_RESULT__ACCEPTANCE)
+ {
+ DebugMessage(DEBUG_DCE_COMMON, "Server accepted context item.\n");
+ ctx_node->state = DCE2_CO_CTX_STATE__ACCEPTED;
+ if (DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND_ACK)
+ cot->got_bind = 1;
+ }
+ else
+ {
+ DebugMessage(DEBUG_DCE_COMMON, "Server rejected context item.\n");
+ ctx_node->state = DCE2_CO_CTX_STATE__REJECTED;
+ cot->got_bind = 0;
+ }
+
+ existing_ctx_node =
+ (DCE2_CoCtxIdNode*)DCE2_ListFind(cot->ctx_ids, (void*)(uintptr_t)ctx_node->ctx_id);
+
+ if (existing_ctx_node != nullptr)
+ {
+ 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 (ctx_node->state == DCE2_CO_CTX_STATE__REJECTED)
+ break;
+
+ if (existing_ctx_node->state == DCE2_CO_CTX_STATE__REJECTED)
+ {
+ existing_ctx_node->ctx_id = ctx_node->ctx_id;
+ DCE2_CopyUuid(&existing_ctx_node->iface, &ctx_node->iface, DCERPC_BO_FLAG__NONE);
+ existing_ctx_node->iface_vers_maj = ctx_node->iface_vers_maj;
+ existing_ctx_node->iface_vers_min = ctx_node->iface_vers_min;
+ existing_ctx_node->state = ctx_node->state;
+ }
+
+ 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:
+ /* Samba actually alters the context. Windows keeps the old */
+ if (ctx_node->state != DCE2_CO_CTX_STATE__REJECTED)
+ {
+ existing_ctx_node->ctx_id = ctx_node->ctx_id;
+ DCE2_CopyUuid(&existing_ctx_node->iface, &ctx_node->iface, DCERPC_BO_FLAG__NONE);
+ existing_ctx_node->iface_vers_maj = ctx_node->iface_vers_maj;
+ existing_ctx_node->iface_vers_min = ctx_node->iface_vers_min;
+ existing_ctx_node->state = ctx_node->state;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ free((void*)ctx_node);
+ }
+ else
+ {
+ status = DCE2_ListInsert(cot->ctx_ids, (void*)(uintptr_t)ctx_node->ctx_id,
+ (void*)ctx_node);
+ if (status != DCE2_RET__SUCCESS)
+ {
+ free((void*)ctx_node);
+ DebugMessage(DEBUG_DCE_COMMON,
+ "Failed to add context id node to list.\n");
+ return;
+ }
+ }
+}
+
+/********************************************************************
+ * Function: DCE2_CoBindAck()
+ *
+ * Handles the processing of a server bind ack or a server alter
+ * context response since they share the same header.
+ * Moves context id items from the pending queue into a list
+ * ultimately used by the rule options and sets each context item
+ * as accepted or rejected based on the server response.
+ *
+ ********************************************************************/
+static void DCE2_CoBindAck(DCE2_SsnData* sd, DCE2_CoTracker* cot,
+ const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len)
+{
+ DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
+ DceRpcCoBindAck* bind_ack = (DceRpcCoBindAck*)frag_ptr;
+ uint16_t sec_addr_len;
+ const uint8_t* ctx_data;
+ uint16_t ctx_len;
+ uint16_t pad = 0;
+ DceRpcCoContResultList* ctx_list;
+ uint8_t num_ctx_results;
+ unsigned int i;
+ uint16_t max_recv_frag;
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ if (frag_len < sizeof(DceRpcCoBindAck))
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return;
+ }
+
+ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoBindAck));
+
+ /* Set what should be the maximum amount of data a client can send in a fragment */
+ max_recv_frag = DceRpcCoBindAckMaxRecvFrag(co_hdr, bind_ack);
+ if ((cot->max_xmit_frag == DCE2_SENTINEL) || (max_recv_frag < cot->max_xmit_frag))
+ cot->max_xmit_frag = (int)max_recv_frag;
+
+ sec_addr_len = DceRpcCoSecAddrLen(co_hdr, bind_ack);
+
+ ctx_data = frag_ptr;
+ ctx_len = frag_len;
+
+ /* First move past secondary address */
+ if (ctx_len < sec_addr_len)
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return;
+ }
+
+ DCE2_MOVE(ctx_data, ctx_len, sec_addr_len);
+
+ /* padded to 4 octet */
+ if ((sizeof(DceRpcCoBindAck) + sec_addr_len) & 3)
+ pad = (4 - ((sizeof(DceRpcCoBindAck) + sec_addr_len) & 3));
+
+ if (ctx_len < pad)
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return;
+ }
+
+ DCE2_MOVE(ctx_data, ctx_len, pad);
+
+ /* Now we're at the start of the context item results */
+ if (ctx_len < sizeof(DceRpcCoContResultList))
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return;
+ }
+
+ ctx_list = (DceRpcCoContResultList*)ctx_data;
+ num_ctx_results = DceRpcCoContNumResults(ctx_list);
+
+ DCE2_MOVE(ctx_data, ctx_len, sizeof(DceRpcCoContResultList));
+
+ for (i = 0; i < num_ctx_results; i++)
+ {
+ DceRpcCoContResult* ctx_result;
+ uint16_t result;
+
+ if (ctx_len < sizeof(DceRpcCoContResult))
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return;
+ }
+ ctx_result = (DceRpcCoContResult*)ctx_data;
+ result = DceRpcCoContRes(co_hdr, ctx_result);
+
+ DCE2_MOVE(ctx_data, ctx_len, sizeof(DceRpcCoContResult));
+
+ if (DCE2_QueueIsEmpty(cot->pending_ctx_ids))
+ return;
+
+ dce_process_ctx_result(sd,cot,co_hdr,policy,result);
+ }
+}
+
+/********************************************************************
+ * Function: DCE2_CoBind()
+ *
+ * Handles the processing of a client bind request. There are
+ * differences between Windows and Samba and even early Samba in
+ * how multiple binds on the session are handled. Processing of
+ * the context id bindings is handed off.
+ *
+ ********************************************************************/
+static void DCE2_CoBind(DCE2_SsnData* sd, DCE2_CoTracker* cot,
+ const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len)
+{
+ DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
+ DceRpcCoBind* bind = (DceRpcCoBind*)frag_ptr;
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ if (frag_len < sizeof(DceRpcCoBind))
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return;
+ }
+
+ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoBind));
+
+ 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:
+ /* Windows will not accept more than one bind */
+ if (!DCE2_ListIsEmpty(cot->ctx_ids))
+ {
+ /* Delete context id list if anything there */
+ DCE2_CoEraseCtxIds(cot);
+ return;
+ }
+
+ /* Byte order of stub data will be that of the bind */
+ cot->data_byte_order = DceRpcCoByteOrder(co_hdr);
+
+ break;
+
+ case DCE2_POLICY__SAMBA:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ if (cot->got_bind)
+ return;
+
+ break;
+
+ case DCE2_POLICY__SAMBA_3_0_20:
+ /* Accepts multiple binds */
+ break;
+
+ default:
+ LogMessage("%s(%d) Invalid policy: %d\n",
+ __FILE__, __LINE__, policy);
+ return;
+ }
+
+ cot->max_xmit_frag = (int)DceRpcCoBindMaxXmitFrag(co_hdr, bind);
+ DCE2_CoCtxReq(sd, cot, co_hdr, DceRpcCoNumCtxItems(bind), frag_ptr, frag_len);
+}
+
+/********************************************************************
+ * Function: DCE2_CoAlterCtx()
+ *
+ * Handles the processing of a client alter context request.
+ * Again, differences in how this is handled - whether we've seen
+ * a bind yet or not, altering the data byte order. Processing
+ * of the context id bindings is handed off.
+ *
+ ********************************************************************/
+static void DCE2_CoAlterCtx(DCE2_SsnData* sd, DCE2_CoTracker* cot,
+ const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len)
+{
+ DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
+ DceRpcCoAltCtx* alt_ctx = (DceRpcCoAltCtx*)frag_ptr;
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ if (frag_len < sizeof(DceRpcCoAltCtx))
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return;
+ }
+
+ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoAltCtx));
+
+ 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:
+ /* Windows will not accept an alter context before
+ * bind and will bind_nak it */
+ if (DCE2_ListIsEmpty(cot->ctx_ids))
+ return;
+
+ if (cot->data_byte_order != (int)DceRpcCoByteOrder(co_hdr))
+ {
+ /* This is anomalous behavior. Alert, but continue processing */
+ if (cot->data_byte_order != DCE2_SENTINEL)
+ dce_alert(GID_DCE2, DCE2_CO_ALTER_CHANGE_BYTE_ORDER,dce_common_stats);
+ }
+
+ 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:
+ /* Nothing for Samba */
+ break;
+
+ default:
+ LogMessage("%s(%d) Invalid policy: %d\n",
+ __FILE__, __LINE__, policy);
+ break;
+ }
+
+ /* Alter context is typedef'ed as a bind */
+ DCE2_CoCtxReq(sd, cot, co_hdr, DceRpcCoNumCtxItems((DceRpcCoBind*)alt_ctx), frag_ptr,
+ frag_len);
+}
+
+static int DCE2_CoGetAuthLen(DCE2_SsnData* sd, const DceRpcCoHdr* co_hdr,
+ const uint8_t* frag_ptr, uint16_t frag_len)
+{
+ DceRpcCoAuthVerifier* auth_hdr;
+ uint16_t auth_len = DceRpcCoAuthLen(co_hdr);
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ if (auth_len == 0)
+ return 0;
+
+ auth_len += sizeof(DceRpcCoAuthVerifier);
+
+ /* This means the auth len was bogus */
+ if (auth_len > frag_len)
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return -1;
+ }
+
+ auth_hdr = (DceRpcCoAuthVerifier*)(frag_ptr + (frag_len - auth_len));
+ if (DceRpcCoAuthLevel(auth_hdr) == DCERPC_CO_AUTH_LEVEL__PKT_PRIVACY)
+ {
+ /* Data is encrypted - don't inspect */
+ return -1;
+ }
+
+ auth_len += DceRpcCoAuthPad(auth_hdr);
+
+ /* This means the auth pad len was bogus */
+ if (auth_len > frag_len)
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return -1;
+ }
+
+ return (int)auth_len;
+}
+
+/********************************************************************
+ * Function: DCE2_CoRequest()
+ *
+ * Handles a DCE/RPC request from the client. This is were the
+ * client actually asks the server to do stuff on it's behalf.
+ * If it's a first/last fragment, set relevant rule option
+ * data and return. If it's a true fragment, do some target
+ * based futzing to set the right opnum and context id for
+ * the to be reassembled packet.
+ *
+ *
+ ********************************************************************/
+static void DCE2_CoRequest(DCE2_SsnData* sd, DCE2_CoTracker* cot,
+ const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len)
+{
+ DceRpcCoRequest* rhdr = (DceRpcCoRequest*)frag_ptr;
+ uint16_t req_size = sizeof(DceRpcCoRequest);
+ DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ /* Account for possible object uuid */
+ if (DceRpcCoObjectFlag(co_hdr))
+ req_size += sizeof(Uuid);
+
+ if (frag_len < req_size)
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+ return;
+ }
+
+ switch (policy)
+ {
+ /* After 3.0.37 up to 3.5.2 byte order of stub data is always
+ * interpreted as little endian */
+ case DCE2_POLICY__SAMBA:
+ cot->data_byte_order = DCERPC_BO_FLAG__LITTLE_ENDIAN;
+ break;
+
+ case DCE2_POLICY__SAMBA_3_0_37:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ case DCE2_POLICY__SAMBA_3_0_20:
+ cot->data_byte_order = DceRpcCoByteOrder(co_hdr);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Move past header */
+ DCE2_MOVE(frag_ptr, frag_len, req_size);
+
+ //FIXIT-M frag stuff
+
+ cot->stub_data = frag_ptr;
+ cot->opnum = DceRpcCoOpnum(co_hdr, rhdr);
+ cot->ctx_id = DceRpcCoCtxId(co_hdr, rhdr);
+ cot->call_id = DceRpcCoCallId(co_hdr);
+
+ if (DceRpcCoFirstFrag(co_hdr) && DceRpcCoLastFrag(co_hdr))
+ {
+ int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len);
+ DebugMessage(DEBUG_DCE_COMMON, "First and last fragment.\n");
+ if (auth_len == -1)
+ return;
+ DCE2_CoSetRopts(sd, cot, co_hdr);
+ }
+ else
+ {
+ //FIXIT-M frag stuff
+ }
+}
+
+/********************************************************************
+ * Function: DCE2_CoResponse()
+ *
+ * Handles a DCE/RPC response from the server.
+ * Samba responds to SMB bind write, request write before read with
+ * a response to the request and doesn't send a bind ack. Get the
+ * context id from the pending context id list and put in stable
+ * list.
+ *
+
+ ********************************************************************/
+static void DCE2_CoResponse(DCE2_SsnData* sd, DCE2_CoTracker* cot,
+ const DceRpcCoHdr* co_hdr, const uint8_t* frag_ptr, uint16_t frag_len)
+{
+ DceRpcCoResponse* rhdr = (DceRpcCoResponse*)frag_ptr;
+ uint16_t ctx_id;
+ DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ if (frag_len < sizeof(DceRpcCoResponse))
+ {
+ dce_alert(GID_DCE2, DCE2_CO_REM_FRAG_LEN_LT_SIZE,dce_common_stats);
+
+ return;
+ }
+
+ switch (policy)
+ {
+ case DCE2_POLICY__SAMBA:
+ cot->data_byte_order = DCERPC_BO_FLAG__LITTLE_ENDIAN;
+ break;
+
+ case DCE2_POLICY__SAMBA_3_0_37:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ case DCE2_POLICY__SAMBA_3_0_20:
+ cot->data_byte_order = DceRpcCoByteOrder(co_hdr);
+ break;
+
+ default:
+ break;
+ }
+
+ ctx_id = DceRpcCoCtxIdResp(co_hdr, rhdr);
+
+ /* If pending queue is not empty, add this context id as accepted and all
+ * others as pending */
+ while (!DCE2_QueueIsEmpty(cot->pending_ctx_ids))
+ {
+ DCE2_Ret status;
+ DCE2_CoCtxIdNode* 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__);
+ return;
+ }
+
+ if (ctx_node->ctx_id == ctx_id)
+ ctx_node->state = DCE2_CO_CTX_STATE__ACCEPTED;
+
+ status = DCE2_ListInsert(cot->ctx_ids, (void*)(uintptr_t)ctx_node->ctx_id,
+ (void*)ctx_node);
+ if (status != DCE2_RET__SUCCESS)
+ {
+ /* Might be a duplicate in there already. If there is we would have used it
+ * anyway before looking at the pending queue. Just get rid of it */
+ free((void*)ctx_node);
+ return;
+ }
+ }
+
+ /* Move past header */
+ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoResponse));
+
+ //FIXIT-M frag stuff
+
+ cot->stub_data = frag_ptr;
+ /* Opnum not in response header - have to use previous client's */
+ cot->ctx_id = ctx_id;
+ cot->call_id = DceRpcCoCallId(co_hdr);
+
+ if (DceRpcCoFirstFrag(co_hdr) && DceRpcCoLastFrag(co_hdr))
+ {
+ int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len);
+ DebugMessage(DEBUG_DCE_COMMON, "First and last fragment.\n");
+ if (auth_len == -1)
+ return;
+ DCE2_CoSetRopts(sd, cot, co_hdr);
+ }
+ else
+ {
+ /* FIXIT-M frag stuff */
+ }
+}
+
+/********************************************************************
+ * Function: DCE2_CoDecode()
+ *
+ * Main processing for the DCE/RPC pdu types. Most are not
+ * implemented as, currently, they are not necessary and only
+ * stats are kept for them. Important are the bind, alter context
+ * and request.
+ *
+ ********************************************************************/
+static void DCE2_CoDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot,
+ 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;
+ int pdu_type = DceRpcCoPduType(co_hdr);
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ /* We've got the main header. Move past it to the
+ * start of the pdu */
+ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoHdr));
+
+ DebugMessage(DEBUG_DCE_COMMON, "PDU type: ");
+
+ /* Client specific pdu types - some overlap with server */
+ if (DCE2_SsnFromClient(sd->wire_pkt))
+ {
+ switch (pdu_type)
+ {
+ case DCERPC_PDU_TYPE__BIND:
+ DebugMessage(DEBUG_DCE_COMMON, "Bind\n");
+ dce_common_stats->co_bind++;
+
+ /* Make sure context id list and queue are initialized */
+ if (DCE2_CoInitCtxStorage(cot) != DCE2_RET__SUCCESS)
+ return;
+
+ DCE2_CoBind(sd, cot, co_hdr, frag_ptr, frag_len);
+
+ break;
+
+ case DCERPC_PDU_TYPE__ALTER_CONTEXT:
+ DebugMessage(DEBUG_DCE_COMMON, "Alter Context\n");
+ dce_common_stats->co_alter_ctx++;
+
+ if (DCE2_CoInitCtxStorage(cot) != DCE2_RET__SUCCESS)
+ return;
+
+ DCE2_CoAlterCtx(sd, cot, co_hdr, frag_ptr, frag_len);
+
+ break;
+
+ case DCERPC_PDU_TYPE__REQUEST:
+ DebugMessage(DEBUG_DCE_COMMON, "Request\n");
+ dce_common_stats->co_request++;
+
+ if (DCE2_ListIsEmpty(cot->ctx_ids) &&
+ DCE2_QueueIsEmpty(cot->pending_ctx_ids))
+ {
+ return;
+ }
+
+ DCE2_CoRequest(sd, cot, co_hdr, frag_ptr, frag_len);
+
+ break;
+
+ case DCERPC_PDU_TYPE__AUTH3:
+ DebugMessage(DEBUG_DCE_COMMON, "Auth3\n");
+ dce_common_stats->co_auth3++;
+ break;
+
+ case DCERPC_PDU_TYPE__CO_CANCEL:
+ DebugMessage(DEBUG_DCE_COMMON, "Cancel\n");
+ dce_common_stats->co_cancel++;
+ break;
+
+ case DCERPC_PDU_TYPE__ORPHANED:
+ DebugMessage(DEBUG_DCE_COMMON, "Orphaned\n");
+ dce_common_stats->co_orphaned++;
+ break;
+
+ case DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP:
+ DebugMessage(DEBUG_DCE_COMMON, "Microsoft Request To Send RPC over HTTP\n");
+ dce_common_stats->co_ms_pdu++;
+ break;
+
+ default:
+ DebugFormat(DEBUG_DCE_COMMON, "Unknown (0x%02x)\n", pdu_type);
+ dce_common_stats->co_other_req++;
+ break;
+ }
+ }
+ else
+ {
+ switch (pdu_type)
+ {
+ case DCERPC_PDU_TYPE__BIND_ACK:
+ case DCERPC_PDU_TYPE__ALTER_CONTEXT_RESP:
+ if (pdu_type == DCERPC_PDU_TYPE__BIND_ACK)
+ {
+ DebugMessage(DEBUG_DCE_COMMON, "Bind Ack\n");
+ dce_common_stats->co_bind_ack++;
+ }
+ else
+ {
+ DebugMessage(DEBUG_DCE_COMMON, "Alter Context Response\n");
+ dce_common_stats->co_alter_ctx_resp++;
+ }
+
+ if (DCE2_QueueIsEmpty(cot->pending_ctx_ids))
+ return;
+
+ /* Bind ack and alter context response have the same
+ * header structure, just different pdu type */
+ DCE2_CoBindAck(sd, cot, co_hdr, frag_ptr, frag_len);
+
+ /* Got the bind/alter response - clear out the pending queue */
+ DCE2_QueueEmpty(cot->pending_ctx_ids);
+
+ break;
+
+ case DCERPC_PDU_TYPE__BIND_NACK:
+ DebugMessage(DEBUG_DCE_COMMON, "Bind Nack\n");
+ dce_common_stats->co_bind_nack++;
+
+ /* Bind nack in Windows seems to blow any previous context away */
+ switch (DCE2_SsnGetServerPolicy(sd))
+ {
+ case DCE2_POLICY__WIN2000:
+ case DCE2_POLICY__WIN2003:
+ case DCE2_POLICY__WINXP:
+ case DCE2_POLICY__WINVISTA:
+ case DCE2_POLICY__WIN2008:
+ case DCE2_POLICY__WIN7:
+ DCE2_CoEraseCtxIds(cot);
+ break;
+
+ default:
+ break;
+ }
+
+ cot->got_bind = 0;
+
+ break;
+
+ case DCERPC_PDU_TYPE__RESPONSE:
+ DebugMessage(DEBUG_DCE_COMMON, "Response\n");
+ dce_common_stats->co_response++;
+ DCE2_CoResponse(sd, cot, co_hdr, frag_ptr, frag_len);
+ break;
+
+ case DCERPC_PDU_TYPE__FAULT:
+ DebugMessage(DEBUG_DCE_COMMON, "Fault\n");
+ dce_common_stats->co_fault++;
+
+ /* Clear out the client side */
+ DCE2_QueueEmpty(cot->pending_ctx_ids);
+ DCE2_BufferEmpty(cot->cli_seg.buf);
+ DCE2_BufferEmpty(cot->frag_tracker.cli_stub_buf);
+
+ DCE2_CoResetTracker(cot);
+
+ break;
+
+ case DCERPC_PDU_TYPE__SHUTDOWN:
+ DebugMessage(DEBUG_DCE_COMMON, "Shutdown\n");
+ dce_common_stats->co_shutdown++;
+ break;
+
+ case DCERPC_PDU_TYPE__REJECT:
+ DebugMessage(DEBUG_DCE_COMMON, "Reject\n");
+ dce_common_stats->co_reject++;
+
+ DCE2_QueueEmpty(cot->pending_ctx_ids);
+
+ break;
+
+ case DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP:
+ DebugMessage(DEBUG_DCE_COMMON, "Microsoft Request To Send RPC over HTTP\n");
+ dce_common_stats->co_ms_pdu++;
+ break;
+
+ default:
+ DebugFormat(DEBUG_DCE_COMMON, "Unknown (0x%02x)\n", pdu_type);
+ dce_common_stats->co_other_resp++;
+ break;
+ }
+ }
+}
+
+/********************************************************************
+ * Function: DCE2_CoProcess()
+ *
+ * Main entry point for connection-oriented DCE/RPC processing.
+ * Since there can be more than one DCE/RPC pdu in the packet, it
+ * loops through the packet data until none is left. It handles
+ * transport layer segmentation and buffers data until it gets the
+ * full pdu, then hands off to pdu processing.
+ *
+ *
+ ********************************************************************/
+void DCE2_CoProcess(DCE2_SsnData* sd, DCE2_CoTracker* cot,
+ const uint8_t* data_ptr, uint16_t data_len)
+{
+ DCE2_CoSeg* seg = DCE2_CoGetSegPtr(sd, cot);
+ uint32_t num_frags = 0;
+ dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd);
+
+ dce_common_stats->co_pdus++;
+
+ co_reassembled = 0;
+
+ while (data_len > 0)
+ {
+ num_frags++;
+
+ DebugFormat(DEBUG_DCE_COMMON, "DCE/RPC message number: %u\n", num_frags);
+
+ /* Fast track full fragments */
+ if (DCE2_BufferIsEmpty(seg->buf))
+ {
+ const uint8_t* frag_ptr = data_ptr;
+ uint16_t frag_len;
+
+ /* Not enough data left for a header. Buffer it and return */
+ if (data_len < sizeof(DceRpcCoHdr))
+ {
+ // FIXIT-M add logic for this case
+ break;
+ }
+
+ if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr*)data_ptr) != DCE2_RET__SUCCESS)
+ return;
+
+ frag_len = DceRpcCoFragLen((DceRpcCoHdr*)data_ptr);
+
+ /* Not enough data left for the pdu. */
+ if (data_len < frag_len)
+ {
+ // FIXIT-M add logic for this case
+ break;
+ }
+
+ DCE2_MOVE(data_ptr, data_len, frag_len);
+
+ /* Got a full DCE/RPC pdu */
+ 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 */
+ if (!DCE2_GcDceDefrag((dce2CommonProtoConf*)sd->config) ||
+ ((num_frags == 1) && !co_reassembled))
+ DCE2_Detect(sd);
+
+ /* Reset if this is a last frag */
+ if (DceRpcCoLastFrag((DceRpcCoHdr*)frag_ptr))
+ num_frags = 0;
+ }
+ else /* We've already buffered data */
+ {
+ // FIXIT-M add logic for this case
+ }
+ }
+
+ // FIXIT-M add reassemble logic
+}
+
#include "dce_common.h"
#include "dce_list.h"
+#include "dce_utils.h"
#define DCE2_CO_BAD_MAJOR_VERSION 27
#define DCE2_CO_BAD_MINOR_VERSION 28
"Connection-oriented DCE/RPC - Context id of non first/last fragment different \
from context id established for fragmented request."
+#define DCE2_MAX_XMIT_SIZE_FUZZ 500
+
#pragma pack(1)
struct DceRpcCoVersion
uint32_t call_id;
};
+/* Bind */
+struct DceRpcCoBind
+{
+ uint16_t max_xmit_frag;
+ uint16_t max_recv_frag;
+ uint32_t assoc_group_id;
+ uint8_t n_context_elem; /* number of context elements */
+ uint8_t reserved;
+ uint16_t reserved2;
+};
+
+struct DceRpcCoSynId
+{
+ Uuid if_uuid;
+ uint32_t if_version;
+};
+
+struct DceRpcCoContElem
+{
+ uint16_t p_cont_id;
+ uint8_t n_transfer_syn; /* number of transfer syntaxes */
+ uint8_t reserved;
+ DceRpcCoSynId abstract_syntax;
+};
+
+struct DceRpcCoBindAck
+{
+ uint16_t max_xmit_frag;
+ uint16_t max_recv_frag;
+ uint32_t assoc_group_id;
+ uint16_t sec_addr_len;
+};
+
+struct DceRpcCoContResult
+{
+ uint16_t result;
+ uint16_t reason;
+ DceRpcCoSynId transfer_syntax;
+};
+
+struct DceRpcCoAuthVerifier
+{
+ uint8_t auth_type;
+ uint8_t auth_level;
+ uint8_t auth_pad_length;
+ uint8_t auth_reserved;
+ uint32_t auth_context_id;
+};
+
+struct DceRpcCoRequest
+{
+ uint32_t alloc_hint;
+ uint16_t context_id;
+ uint16_t opnum;
+};
+
+struct DceRpcCoResponse
+{
+ uint32_t alloc_hint;
+ uint16_t context_id;
+ uint8_t cancel_count;
+ uint8_t reserved;
+};
+
+struct DceRpcCoContResultList
+{
+ uint8_t n_results;
+ uint8_t reserved;
+ uint16_t reserved2;
+};
+
+typedef DceRpcCoBind DceRpcCoAltCtx;
+typedef DceRpcCoBindAck DceRpcCoAltCtxResp;
+
#pragma pack()
struct DCE2_CoFragTracker
DCE2_CoSeg srv_seg;
};
+/*
+ * Connection oriented
+ */
+enum DceRpcCoPfcFlags
+{
+ DCERPC_CO_PFC_FLAGS__FIRST_FRAG = 0x01,
+ DCERPC_CO_PFC_FLAGS__LAST_FRAG = 0x02,
+ DCERPC_CO_PFC_FLAGS__PENDING_CANCEL = 0x04,
+ DCERPC_CO_PFC_FLAGS__RESERVED_1 = 0x08,
+ DCERPC_CO_PFC_FLAGS__CONC_MPX = 0x10,
+ DCERPC_CO_PFC_FLAGS__DID_NOT_EXECUTE = 0x20,
+ DCERPC_CO_PFC_FLAGS__MAYBE = 0x40,
+ DCERPC_CO_PFC_FLAGS__OBJECT_UUID = 0x80
+};
+
+enum DCE2_CoCtxState
+{
+ DCE2_CO_CTX_STATE__ACCEPTED,
+ DCE2_CO_CTX_STATE__REJECTED,
+ DCE2_CO_CTX_STATE__PENDING
+};
+
+struct DCE2_CoCtxIdNode
+{
+ uint16_t ctx_id; /* The context id */
+ Uuid iface; /* The presentation syntax uuid for the interface */
+ uint16_t iface_vers_maj; /* The major version of the interface */
+ uint16_t iface_vers_min; /* The minor version of the interface */
+
+ /* Whether or not the server accepted or rejected the client bind/alter context
+ * request. Initially set to pending until server response */
+ DCE2_CoCtxState state;
+};
+
+enum DceRpcCoAuthLevelType
+{
+ DCERPC_CO_AUTH_LEVEL__NONE = 1,
+ DCERPC_CO_AUTH_LEVEL__CONNECT,
+ DCERPC_CO_AUTH_LEVEL__CALL,
+ DCERPC_CO_AUTH_LEVEL__PKT,
+ DCERPC_CO_AUTH_LEVEL__PKT_INTEGRITY,
+ DCERPC_CO_AUTH_LEVEL__PKT_PRIVACY
+};
+
+enum DceRpcCoContDefResult
+{
+ DCERPC_CO_CONT_DEF_RESULT__ACCEPTANCE = 0,
+ DCERPC_CO_CONT_DEF_RESULT__USER_REJECTION,
+ DCERPC_CO_CONT_DEF_RESULT__PROVIDER_REJECTION
+};
inline uint8_t DceRpcCoVersMaj(const DceRpcCoHdr* co)
{
return co->pversion.major;
return DceRpcNtohs(&co->frag_length, DceRpcCoByteOrder(co));
}
+inline uint8_t DceRpcCoNumCtxItems(const DceRpcCoBind* cob)
+{
+ return cob->n_context_elem;
+}
+
+inline uint16_t DceRpcCoContElemCtxId(const DceRpcCoHdr* co, const DceRpcCoContElem* coce)
+{
+ return DceRpcNtohs(&coce->p_cont_id, DceRpcCoByteOrder(co));
+}
+
+inline uint8_t DceRpcCoContElemNumTransSyntaxes(const DceRpcCoContElem* coce)
+{
+ return coce->n_transfer_syn;
+}
+
+inline const Uuid* DceRpcCoContElemIface(const DceRpcCoContElem* coce)
+{
+ return &coce->abstract_syntax.if_uuid;
+}
+
+inline uint16_t DceRpcCoContElemIfaceVersMaj(const DceRpcCoHdr* co, const DceRpcCoContElem* coce)
+{
+ return (uint16_t)(DceRpcNtohl(&coce->abstract_syntax.if_version, DceRpcCoByteOrder(co)) &
+ 0x0000ffff);
+}
+
+inline uint16_t DceRpcCoContElemIfaceVersMin(const DceRpcCoHdr* co, const DceRpcCoContElem* coce)
+{
+ return (uint16_t)(DceRpcNtohl(&coce->abstract_syntax.if_version, DceRpcCoByteOrder(co)) >> 16);
+}
+
+inline uint16_t DceRpcCoBindAckMaxRecvFrag(const DceRpcCoHdr* co, const DceRpcCoBindAck* coba)
+{
+ return DceRpcNtohs(&coba->max_recv_frag, DceRpcCoByteOrder(co));
+}
+
+inline uint16_t DceRpcCoSecAddrLen(const DceRpcCoHdr* co, const DceRpcCoBindAck* coba)
+{
+ return DceRpcNtohs(&coba->sec_addr_len, DceRpcCoByteOrder(co));
+}
+
+inline uint16_t DceRpcCoContRes(const DceRpcCoHdr* co, const DceRpcCoContResult* cocr)
+{
+ return DceRpcNtohs(&cocr->result, DceRpcCoByteOrder(co));
+}
+
+inline int DceRpcCoObjectFlag(const DceRpcCoHdr* co)
+{
+ return co->pfc_flags & DCERPC_CO_PFC_FLAGS__OBJECT_UUID;
+}
+
+inline int DceRpcCoFirstFrag(const DceRpcCoHdr* co)
+{
+ return co->pfc_flags & DCERPC_CO_PFC_FLAGS__FIRST_FRAG;
+}
+
+inline int DceRpcCoLastFrag(const DceRpcCoHdr* co)
+{
+ return co->pfc_flags & DCERPC_CO_PFC_FLAGS__LAST_FRAG;
+}
+
+inline uint16_t DceRpcCoAuthLen(const DceRpcCoHdr* co)
+{
+ return DceRpcNtohs(&co->auth_length, DceRpcCoByteOrder(co));
+}
+
+inline uint8_t DceRpcCoAuthLevel(const DceRpcCoAuthVerifier* coav)
+{
+ return coav->auth_level;
+}
+
+inline uint16_t DceRpcCoAuthPad(const DceRpcCoAuthVerifier* coav)
+{
+ return coav->auth_pad_length;
+}
+
+inline uint16_t DceRpcCoCtxIdResp(const DceRpcCoHdr* co, const DceRpcCoResponse* cor)
+{
+ return DceRpcNtohs(&cor->context_id, DceRpcCoByteOrder(co));
+}
+
+inline uint16_t DceRpcCoBindMaxXmitFrag(const DceRpcCoHdr* co, const DceRpcCoBind* cob)
+{
+ return DceRpcNtohs(&cob->max_xmit_frag, DceRpcCoByteOrder(co));
+}
+
+inline uint8_t DceRpcCoContNumResults(const DceRpcCoContResultList* cocrl)
+{
+ return cocrl->n_results;
+}
+
+inline uint32_t DceRpcCoCallId(const DceRpcCoHdr* co)
+{
+ return DceRpcNtohl(&co->call_id, DceRpcCoByteOrder(co));
+}
+
+inline uint16_t DceRpcCoOpnum(const DceRpcCoHdr* co, const DceRpcCoRequest* cor)
+{
+ return DceRpcNtohs(&cor->opnum, DceRpcCoByteOrder(co));
+}
+
+inline uint16_t DceRpcCoCtxId(const DceRpcCoHdr* co, const DceRpcCoRequest* cor)
+{
+ return DceRpcNtohs(&cor->context_id, DceRpcCoByteOrder(co));
+}
+
void DCE2_CoInitTracker(DCE2_CoTracker*);
+void DCE2_CoProcess(DCE2_SsnData*, DCE2_CoTracker*,
+ const uint8_t*, uint16_t);
#endif
// dce_common.cc author Rashmi Pitre <rrp@cisco.com>
#include "dce_common.h"
+#include "dce_tcp.h"
+#include "dce_smb.h"
#include "framework/base_api.h"
#include "framework/module.h"
#include "flow/flow.h"
#include "log/messages.h"
#include "main/snort_debug.h"
+#include "detection/detect.h"
+
+THREAD_LOCAL int dce2_detected = 0;
const char* dce2_get_policy_name(DCE2_Policy policy)
{
return true;
}
- if ((sd != NULL) && DCE2_SsnNoInspect(sd))
+ if ((sd != nullptr) && DCE2_SsnNoInspect(sd))
{
DebugMessage(DEBUG_DCE_COMMON, "Aborting PAF because of session data check.\n");
return true;
return false;
}
+void DCE2_PrintRoptions(DCE2_Roptions* ropts)
+{
+ DebugFormat(DEBUG_DCE_COMMON,
+ " First frag: %s\n", ropts->first_frag == 1 ? "yes" : (ropts->first_frag == 0 ? "no" :
+ "unset"));
+ if (ropts->first_frag == DCE2_SENTINEL)
+ {
+ DebugMessage(DEBUG_DCE_COMMON, " Iface: unset\n");
+ DebugMessage(DEBUG_DCE_COMMON, " Iface version: unset\n");
+ }
+ else
+ {
+ DebugFormat(DEBUG_DCE_COMMON, " Iface: %s\n", DCE2_UuidToStr(&ropts->iface,
+ DCERPC_BO_FLAG__NONE));
+ DebugFormat(DEBUG_DCE_COMMON, " Iface version: %u\n", ropts->iface_vers_maj);
+ }
+ if (ropts->opnum == DCE2_SENTINEL)
+ DebugMessage(DEBUG_DCE_COMMON, " Opnum: unset\n");
+ else
+ DebugFormat(DEBUG_DCE_COMMON, " Opnum: %u\n", ropts->opnum);
+ DebugFormat(DEBUG_DCE_COMMON, " Header byte order: %s\n",
+ ropts->hdr_byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN ? "little endian" :
+ (ropts->hdr_byte_order == DCERPC_BO_FLAG__BIG_ENDIAN ? "big endian" : "unset"));
+ DebugFormat(DEBUG_DCE_COMMON, " Data byte order: %s\n",
+ ropts->data_byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN ? "little endian" :
+ (ropts->data_byte_order == DCERPC_BO_FLAG__BIG_ENDIAN ? "big endian" : "unset"));
+ if (ropts->stub_data != nullptr)
+ DebugFormat(DEBUG_DCE_COMMON, " Stub data: %p\n", ropts->stub_data);
+ else
+ DebugMessage(DEBUG_DCE_COMMON, " Stub data: NULL\n");
+}
+
+static void dce2_protocol_detect(DCE2_SsnData* sd, Packet* pkt)
+{
+ if (sd->trans == DCE2_TRANS_TYPE__TCP)
+ {
+ Profile profile(dce2_tcp_pstat_detect);
+ }
+ else
+ {
+ Profile profile(dce2_smb_pstat_detect);
+ }
+ SnortEventqPush();
+ snort_detect(pkt);
+ SnortEventqPop();
+
+ dce2_detected = 1;
+}
+
+void DCE2_Detect(DCE2_SsnData* sd)
+{
+ Packet* top_pkt = sd->wire_pkt;
+ //FIXIT-M Get packet from stack
+
+ DebugMessage(DEBUG_DCE_COMMON, "Detecting ------------------------------------------------\n");
+ DebugMessage(DEBUG_DCE_COMMON, " Rule options:\n");
+ DCE2_PrintRoptions(&sd->ropts);
+ DebugMessage(DEBUG_DCE_COMMON, "Payload:\n");
+ DCE2_PrintPktData(top_pkt->data, top_pkt->dsize);
+ if (sd->ropts.stub_data != nullptr)
+ {
+ DebugMessage(DEBUG_DCE_COMMON,"\nStub data:\n");
+ DCE2_PrintPktData(sd->ropts.stub_data,
+ top_pkt->dsize - (sd->ropts.stub_data - top_pkt->data));
+ }
+
+ dce2_protocol_detect(sd, top_pkt);
+ /* Always reset rule option data after detecting */
+ DCE2_ResetRopts(&sd->ropts);
+ DebugMessage(DEBUG_DCE_COMMON, "----------------------------------------------------------\n");
+}
+
#ifdef BUILDING_SO
extern const BaseApi* ips_dce_iface;
#include "framework/module.h"
#include "framework/inspector.h"
#include "protocols/packet.h"
+#include "events/event_queue.h"
extern const InspectApi dce2_smb_api;
extern const InspectApi dce2_tcp_api;
+extern THREAD_LOCAL int dce2_detected;
#define GID_DCE2 145
DCE2_POLICY__SAMBA_3_0_20,
};
+struct dce2CommonStats
+{
+ PegCount events;
+ PegCount sessions_aborted;
+ PegCount bad_autodetects;
+
+ PegCount co_pdus;
+ PegCount co_bind;
+ PegCount co_bind_ack;
+ PegCount co_alter_ctx;
+ PegCount co_alter_ctx_resp;
+ PegCount co_bind_nack;
+ PegCount co_request;
+ PegCount co_response;
+ PegCount co_cancel;
+ PegCount co_orphaned;
+ PegCount co_fault;
+ PegCount co_auth3;
+ PegCount co_shutdown;
+ PegCount co_reject;
+ PegCount co_ms_pdu;
+ PegCount co_other_req;
+ PegCount co_other_resp;
+ PegCount co_req_fragments;
+ PegCount co_resp_fragments;
+ PegCount co_cli_max_frag_size;
+ PegCount co_cli_min_frag_size;
+ PegCount co_cli_seg_reassembled;
+ PegCount co_cli_frag_reassembled;
+ PegCount co_srv_max_frag_size;
+ PegCount co_srv_min_frag_size;
+ PegCount co_srv_seg_reassembled;
+ PegCount co_srv_frag_reassembled;
+};
#define DCE2_SARG__POLICY_WIN2000 "Win2000"
#define DCE2_SARG__POLICY_WINXP "WinXP"
#define DCE2_SARG__POLICY_WINVISTA "WinVista"
#define DCE2_DEBUG__PAF_END_MSG "=========================================================="
-/* DCE/RPC byte order flag */
-enum DceRpcBoFlag
-{
- DCERPC_BO_FLAG__NONE,
- DCERPC_BO_FLAG__BIG_ENDIAN,
- DCERPC_BO_FLAG__LITTLE_ENDIAN
-};
-
enum DceRpcPduType
{
DCERPC_PDU_TYPE__REQUEST = 0,
DCE2_Policy server_policy;
DCE2_Policy client_policy;
int flags;
- const Packet* wire_pkt;
+ Packet* wire_pkt;
uint64_t alert_mask;
DCE2_Roptions ropts;
int autodetect_dir;
+ void* config;
uint32_t cli_seq;
uint32_t cli_nseq;
uint32_t srv_nseq;
};
-inline DceRpcBoFlag DceRpcByteOrder(const uint8_t value)
-{
- if ((value & 0x10) >> 4)
- return DCERPC_BO_FLAG__LITTLE_ENDIAN;
-
- return DCERPC_BO_FLAG__BIG_ENDIAN;
-}
-
-inline uint16_t DceRpcNtohs(const uint16_t* ptr, const DceRpcBoFlag bo_flag)
-{
- uint16_t value;
-
- if (ptr == NULL)
- return 0;
-
-#ifdef WORDS_MUSTALIGN
- value = *((uint8_t*)ptr) << 8 | *((uint8_t*)ptr + 1);
-#else
- value = *ptr;
-#endif /* WORDS_MUSTALIGN */
-
- if (bo_flag == DCERPC_BO_FLAG__NONE)
- return value;
-
-#ifdef WORDS_BIGENDIAN
- if (bo_flag == DCERPC_BO_FLAG__BIG_ENDIAN)
-#else
- if (bo_flag == DCERPC_BO_FLAG__LITTLE_ENDIAN)
-#endif /* WORDS_BIGENDIAN */
- return value;
-
- return ((value & 0xff00) >> 8) | ((value & 0x00ff) << 8);
-}
-
inline void DCE2_ResetRopts(DCE2_Roptions* ropts)
{
ropts->first_frag = DCE2_SENTINEL;
ropts->opnum = DCE2_SENTINEL;
ropts->hdr_byte_order = DCE2_SENTINEL;
ropts->data_byte_order = DCE2_SENTINEL;
- ropts->stub_data = NULL;
+ ropts->stub_data = nullptr;
}
inline void DCE2_SsnSetAutodetected(DCE2_SsnData* sd, Packet* p)
return sd->flags & DCE2_SSN_FLAG__NO_INSPECT;
}
+inline bool DCE2_GcDceDefrag(dce2CommonProtoConf* config)
+{
+ return config->disable_defrag;
+}
+
+inline int DCE2_SsnFromServer(Packet* p)
+{
+ return p->from_server();
+}
+
+inline int DCE2_SsnFromClient(Packet* p)
+{
+ return p->from_client();
+}
+
+inline DCE2_Policy DCE2_SsnGetServerPolicy(DCE2_SsnData* sd)
+{
+ return sd->server_policy;
+}
+
+inline void dce_alert(uint32_t gid, uint32_t sid, dce2CommonStats* stats)
+{
+ SnortEventqAdd(gid,sid);
+ stats->events++;
+}
+
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*);
#endif
return list;
}
+
/********************************************************************
* Function: DCE2_ListInsert()
*
return DCE2_RET__SUCCESS;
}
-
/********************************************************************
* Function: DCE2_ListFirst()
*
return nullptr;
}
-
/********************************************************************
* Function: DCE2_ListEmpty()
*
list->num_nodes++;
}
}
+
+/********************************************************************
+ * Function: DCE2_ListFind()
+ *
+ * Trys to find a node in the list using key passed in. If list
+ * is splayed, found node is moved to front of list. The data
+ * associated with the node is returned.
+ *
+ * Arguments:
+ * DCE2_List *
+ * A pointer to the list object.
+ * void *
+ * Pointer to a key.
+ *
+ * Returns:
+ * void *
+ * If the key is found, the data associated with the node
+ * is returned.
+ * NULL is returned if the item cannot be found given the key.
+ *
+ ********************************************************************/
+void* DCE2_ListFind(DCE2_List* list, void* key)
+{
+ DCE2_ListNode* n;
+
+ if (list == nullptr)
+ return nullptr;
+
+ for (n = list->head; n != nullptr; n = n->next)
+ {
+ int comp = list->compare(key, n->key);
+ if (comp == 0)
+ {
+ /* Found it, break out */
+ break;
+ }
+ else if ((comp < 0) && (list->type == DCE2_LIST_TYPE__SORTED))
+ {
+ /* Don't look any more if the list is sorted */
+ return nullptr;
+ }
+ }
+
+ if (n != nullptr)
+ {
+ /* If list is splayed, move found node to front of list */
+ if ((list->type == DCE2_LIST_TYPE__SPLAYED) &&
+ (n != list->head))
+ {
+ n->prev->next = n->next;
+
+ if (n->next != nullptr)
+ n->next->prev = n->prev;
+ else /* it's the tail */
+ list->tail = n->prev;
+
+ n->prev = nullptr;
+ n->next = list->head;
+ list->head->prev = n;
+ list->head = n;
+ }
+
+ return n->data;
+ }
+
+ return nullptr;
+}
+
+/********************************************************************
+ * Function: DCE2_QueueNew()
+ *
+ * Creates and initializes a new queue object.
+ *
+ * Arguments:
+ * DCE2_QueueDataFree
+ * An optional free function for the data inserted into
+ * the queue. If NULL is passed in, the user will be
+ * responsible for freeing data left in the queue.
+ *
+ * Returns:
+ * DCE2_Queue *
+ * Pointer to a new queue object.
+ * NULL if unable to allocate memory for the object.
+ *
+ ********************************************************************/
+DCE2_Queue* DCE2_QueueNew(DCE2_QueueDataFree df)
+{
+ DCE2_Queue* queue;
+
+ queue = (DCE2_Queue*)SnortAlloc(sizeof(DCE2_Queue));
+ if (queue == nullptr)
+ return nullptr;
+
+ queue->data_free = df;
+
+ return queue;
+}
+
+/********************************************************************
+ * Function: DCE2_QueueEnqueue()
+ *
+ * Inserts data into the queue.
+ *
+ * Arguments:
+ * DCE2_Queue *
+ * A pointer to the queue object.
+ * void *
+ * Pointer to the data to insert into the queue.
+ *
+ * Returns:
+ * DCE2_Ret
+ * DCE2_RET__ERROR if memory cannot be allocated for a new
+ * queue node or the queue object passed in is NULL.
+ * DCE2_RET__SUCCESS if the data is successfully added to
+ * the queue.
+ *
+ ********************************************************************/
+DCE2_Ret DCE2_QueueEnqueue(DCE2_Queue* queue, void* data)
+{
+ DCE2_QueueNode* n;
+
+ if (queue == nullptr)
+ return DCE2_RET__ERROR;
+
+ n = (DCE2_QueueNode*)SnortAlloc(sizeof(DCE2_QueueNode));
+ if (n == nullptr)
+ return DCE2_RET__ERROR;
+
+ n->data = data;
+
+ if (queue->tail == nullptr)
+ {
+ queue->head = queue->tail = n;
+ n->next = nullptr;
+ }
+ else
+ {
+ queue->tail->next = n;
+ n->prev = queue->tail;
+ queue->tail = n;
+ }
+
+ queue->num_nodes++;
+
+ return DCE2_RET__SUCCESS;
+}
+
+/********************************************************************
+ * Function: DCE2_QueueDequeue()
+ *
+ * Removes and returns the data in the first node in the queue.
+ * Note that the user will have to free the data returned. The
+ * data free function only applies to data that is in the queue
+ * when it is emptied or destroyed.
+ *
+ * Arguments:
+ * DCE2_Queue *
+ * A pointer to the queue object.
+ *
+ * Returns:
+ * void *
+ * The data in the first node in the queue.
+ * NULL if there are no items in the queue or the queue object
+ * passed in is NULL.
+ *
+ ********************************************************************/
+void* DCE2_QueueDequeue(DCE2_Queue* queue)
+{
+ DCE2_QueueNode* n;
+
+ if (queue == nullptr)
+ return nullptr;
+
+ n = queue->head;
+
+ if (n != nullptr)
+ {
+ void* data = n->data;
+
+ if (queue->head == queue->tail)
+ {
+ queue->head = queue->tail = nullptr;
+ }
+ else
+ {
+ queue->head->next->prev = nullptr;
+ queue->head = queue->head->next;
+ }
+
+ free((void*)n);
+
+ queue->num_nodes--;
+
+ return data;
+ }
+
+ return nullptr;
+}
+
+/********************************************************************
+ * Function: DCE2_QueueEmpty()
+ *
+ * Removes all of the nodes in a queue. Does not delete the queue
+ * object itself. Calls data free function for data if it is
+ * not NULL.
+ *
+ * Arguments:
+ * DCE2_Queue *
+ * A pointer to the queue object.
+ *
+ * Returns: None
+ *
+ ********************************************************************/
+void DCE2_QueueEmpty(DCE2_Queue* queue)
+{
+ DCE2_QueueNode* n;
+
+ if (queue == nullptr)
+ return;
+
+ n = queue->head;
+
+ while (n != nullptr)
+ {
+ DCE2_QueueNode* tmp = n->next;
+
+ if (queue->data_free != nullptr)
+ queue->data_free(n->data);
+
+ free((void*)n);
+ n = tmp;
+ }
+
+ queue->head = queue->tail = queue->current = nullptr;
+ queue->num_nodes = 0;
+}
+
+/********************************************************************
+ * Function: DCE2_QueueFirst()
+ *
+ * Returns a pointer to the data of the first node in the queue.
+ * Sets a current pointer to the first node in the queue for
+ * iterating over the queue.
+ *
+ * Arguments:
+ * DCE2_Queue *
+ * A pointer to the queue object.
+ *
+ * Returns:
+ * void *
+ * The data in the first node in the queue.
+ * NULL if the queue object passed in is NULL, or there are
+ * no items in the queue.
+ *
+ ********************************************************************/
+void* DCE2_QueueFirst(DCE2_Queue* queue)
+{
+ if (queue == nullptr)
+ return nullptr;
+
+ queue->current = queue->head;
+ queue->next = nullptr;
+
+ if (queue->current != nullptr)
+ return queue->current->data;
+
+ return nullptr;
+}
+
+/********************************************************************
+ * Function: DCE2_QueueNext()
+ *
+ * Increments the current pointer in the queue to the next node in
+ * the queue and returns the data associated with it. This in
+ * combination with DCE2_QueueFirst is useful in a for loop to
+ * iterate over the items in a queue.
+ *
+ * Arguments:
+ * DCE2_Queue *
+ * A pointer to the queue object.
+ *
+ * Returns:
+ * void *
+ * The data in the next node in the queue.
+ * NULL if the queue object passed in is NULL, or we are at
+ * the end of the queue and there are no next nodes.
+ *
+ ********************************************************************/
+void* DCE2_QueueNext(DCE2_Queue* queue)
+{
+ if (queue == nullptr)
+ return nullptr;
+
+ if (queue->next != nullptr)
+ {
+ queue->current = queue->next;
+ queue->next = nullptr;
+ return queue->current->data;
+ }
+ else if (queue->current != nullptr)
+ {
+ queue->current = queue->current->next;
+ if (queue->current != nullptr)
+ return queue->current->data;
+ }
+
+ return nullptr;
+}
+
inline bool DCE2_ListIsEmpty(DCE2_List*);
void DCE2_ListEmpty(DCE2_List*);
void DCE2_ListDestroy(DCE2_List*);
+void* DCE2_ListFind(DCE2_List*, void*);
+
+DCE2_Queue* DCE2_QueueNew(DCE2_QueueDataFree);
+DCE2_Ret DCE2_QueueEnqueue(DCE2_Queue*, void*);
+void* DCE2_QueueDequeue(DCE2_Queue*);
+static inline bool DCE2_QueueIsEmpty(DCE2_Queue*);
+void DCE2_QueueEmpty(DCE2_Queue*);
+void* DCE2_QueueFirst(DCE2_Queue*);
+void* DCE2_QueueNext(DCE2_Queue*);
/********************************************************************
* Function: DCE2_ListIsEmpty()
return false;
}
+/********************************************************************
+ * Function: DCE2_QueueIsEmpty()
+ *
+ * Determines whether or not the queue has any items in it
+ * currently.
+ *
+ ********************************************************************/
+inline bool DCE2_QueueIsEmpty(DCE2_Queue* queue)
+{
+ if (queue == nullptr)
+ return 1;
+ if (queue->num_nodes == 0)
+ return 1;
+ return 0;
+}
+
#endif
return(&fd->dce2_smb_session);
}
-static DCE2_SmbSsnData* dce2_create_new_smb_session(Packet* p, dce2SmbProtoConf config)
+static DCE2_SmbSsnData* dce2_create_new_smb_session(Packet* p, dce2SmbProtoConf* config)
{
- DCE2_SmbSsnData* dce2_smb_sess = NULL;
+ DCE2_SmbSsnData* dce2_smb_sess = nullptr;
Profile profile(dce2_smb_pstat_new_session);
+ //FIXIT-M Re-evaluate after infrastructure/binder support if autodetect here
+ //is necessary
+
if (DCE2_SmbAutodetect(p))
{
DebugMessage(DEBUG_DCE_SMB, "DCE over SMB packet detected\n");
DebugFormat(DEBUG_DCE_SMB,"Created (%p)\n", (void*)dce2_smb_sess);
dce2_smb_sess->sd.trans = DCE2_TRANS_TYPE__SMB;
- dce2_smb_sess->sd.server_policy = config.common.policy;
+ dce2_smb_sess->sd.server_policy = config->common.policy;
dce2_smb_sess->sd.client_policy = DCE2_POLICY__WINXP;
dce2_smb_sess->sd.wire_pkt = p;
+ dce2_smb_sess->sd.config = (void*)config;
DCE2_SsnSetAutodetected(&dce2_smb_sess->sd, p);
}
return dce2_smb_sess;
}
-DCE2_SmbSsnData* dce2_handle_smb_session(Packet* p, dce2SmbProtoConf& config)
+DCE2_SmbSsnData* dce2_handle_smb_session(Packet* p, dce2SmbProtoConf* config)
{
Profile profile(dce2_smb_pstat_session);
DCE2_SsnNoInspect(sd);
dce2_smb_stats.sessions_aborted++;
dce2_smb_stats.bad_autodetects++;
- return NULL;
+ return nullptr;
}
DCE2_SsnClearAutodetected(sd);
}
return;
}
- dce2_smb_sess = dce2_handle_smb_session(p, config);
+ dce2_smb_sess = dce2_handle_smb_session(p, &config);
if (!dce2_smb_sess)
{
return;
PegCount sessions_aborted;
PegCount bad_autodetects;
- PegCount smb_sessions;
- PegCount smb_pkts;
-
PegCount co_pdus;
PegCount co_bind;
PegCount co_bind_ack;
PegCount co_auth3;
PegCount co_shutdown;
PegCount co_reject;
+ PegCount co_ms_pdu;
PegCount co_other_req;
PegCount co_other_resp;
PegCount co_req_fragments;
PegCount co_srv_seg_reassembled;
PegCount co_srv_frag_reassembled;
+ PegCount smb_sessions;
+ PegCount smb_pkts;
PegCount smb_ignored_bytes;
PegCount smb_cli_seg_reassembled;
PegCount smb_srv_seg_reassembled;
// For tracking requests / responses
DCE2_SmbRequestTracker rtracker;
- //DCE2_Queue *rtrackers;
+ DCE2_Queue* rtrackers;
uint16_t max_outstanding_requests;
uint16_t outstanding_requests;
{ "events", "total events" },
{ "aborted sessions", "total aborted sessions" },
{ "bad autodetects", "total bad autodetects" },
- { "smb sessions", "total smb sessions" },
- { "smb packets", "total smb packets" },
- { "connection-oriented PDUs", "total connection-oriented PDUs" },
- { "connection-oriented binds", "total connection-oriented binds" },
- { "connection-oriented bind acks", "total connection-oriented binds acks" },
- { "connection-oriented alter contexts", "total connection-oriented alter contexts" },
- { "connection-oriented alter context responses",
+ { "PDUs", "total connection-oriented PDUs" },
+ { "Binds", "total connection-oriented binds" },
+ { "Bind acks", "total connection-oriented binds acks" },
+ { "Alter contexts", "total connection-oriented alter contexts" },
+ { "Alter context responses",
"total connection-oriented alter context responses" },
- { "connection-oriented bind naks", "total connection-oriented bind naks" },
- { "connection-oriented requests", "total connection-oriented requests" },
- { "connection-oriented responses", "total connection-oriented responses" },
- { "connection-oriented cancels", "total connection-oriented cancels" },
- { "connection-oriented orphaned", "total connection-oriented orphaned" },
- { "connection-oriented faults", "total connection-oriented faults" },
- { "connection-oriented auth3s", "total connection-oriented auth3s" },
- { "connection-oriented shutdowns", "total connection-oriented shutdowns" },
- { "connection-oriented rejects", "total connection-oriented rejects" },
- { "connection-oriented other requests", "total connection-oriented other requests" },
- { "connection-oriented other responses", "total connection-oriented other responses" },
- { "connection-oriented request fragments", "total connection-oriented request fragments" },
- { "connection-oriented response fragments", "total connection-oriented response fragments" },
- { "connection-oriented client maximum fragment size",
+ { "Bind naks", "total connection-oriented bind naks" },
+ { "Requests", "total connection-oriented requests" },
+ { "Responses", "total connection-oriented responses" },
+ { "Cancels", "total connection-oriented cancels" },
+ { "Orphaned", "total connection-oriented orphaned" },
+ { "Faults", "total connection-oriented faults" },
+ { "Auth3s", "total connection-oriented auth3s" },
+ { "Shutdowns", "total connection-oriented shutdowns" },
+ { "Rejects", "total connection-oriented rejects" },
+ { "MS RPC/HTTP PDUs", "total connection-oriented MS requests to send RPC over HTTP" },
+ { "Other requests", "total connection-oriented other requests" },
+ { "Other responses", "total connection-oriented other responses" },
+ { "Request fragments", "total connection-oriented request fragments" },
+ { "Response fragments", "total connection-oriented response fragments" },
+ { "Client max fragment size",
"connection-oriented client maximum fragment size" },
- { "connection-oriented client minimum fragment size",
+ { "Client min fragment size",
"connection-oriented client minimum fragment size" },
- { "connection-oriented client segments reassembled",
+ { "Client segs reassembled",
"total connection-oriented client segments reassembled" },
- { "connection-oriented client fragments reassembled",
+ { "Client frags reassembled",
"total connection-oriented client fragments reassembled" },
- { "connection-oriented server maximum fragment size",
+ { "Server max fragment size",
"connection-oriented server maximum fragment size" },
- { "connection-oriented server minimum fragment size",
+ { "Server min fragment size",
"connection-oriented server minimum fragment size" },
- { "connection-oriented server segments reassembled",
+ { "Server segs reassembled",
"total connection-oriented server segments reassembled" },
- { "connection-oriented server fragments reassembled",
+ { "Server frags reassembled",
"total connection-oriented server fragments reassembled" },
- { "smb client segments reassembled", "total smb client segments reassembled" },
- { "smb server segments reassembled", "total smb server segments reassembled" },
- { "smb maximum outstanding requests", "total smb maximum outstanding requests" },
- { "smb files processed", "total smb files processed" },
+ { "Sessions", "total smb sessions" },
+ { "Packets", "total smb packets" },
+ { "Client segs reassembled", "total smb client segments reassembled" },
+ { "Server segs reassembled", "total smb server segments reassembled" },
+ { "Max outstanding requests", "total smb maximum outstanding requests" },
+ { "Files processed", "total smb files processed" },
{ nullptr, nullptr }
};
switch ( index )
{
case 0:
- name = "dce smb main";
+ name = "dce_smb_main";
parent = nullptr;
return &dce2_smb_pstat_main;
case 1:
- name = "dce smb session";
- parent = "dce smb main";
+ name = "dce_smb_session";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_session;
case 2:
- name = "dce smb new_session";
- parent = "dce smb session";
+ name = "dce_smb_new_session";
+ parent = "dce_smb_session";
return &dce2_smb_pstat_new_session;
case 3:
- name = "dce smb detect";
- parent = "dce smb main";
+ name = "dce_smb_detect";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_detect;
case 4:
- name = "dce smb log";
- parent = "dce smb main";
+ name = "dce_smb_log";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_log;
case 5:
- name = "dce smb connection-oriented segment";
- parent = "dce smb main";
+ name = "dce_smb_co_segment";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_co_seg;
case 6:
- name = "dce smb connection-oriented fragment";
- parent = "dce smb main";
+ name = "dce_smb_co_fragment";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_co_frag;
case 7:
- name = "dce smb connection-oriented reassembly";
- parent = "dce smb main";
+ name = "dce_smb_co_reassembly";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_co_reass;
case 8:
- name = "dce smb connection-oriented context";
- parent = "dce smb main";
+ name = "dce_smb_co_context";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_co_ctx;
case 9:
- name = "dce smb segment";
- parent = "dce smb main";
+ name = "dce_smb_segment";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_smb_seg;
case 10:
- name = "dce smb request";
- parent = "dce smb main";
+ name = "dce_smb_request";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_smb_req;
case 11:
- name = "dce smb uid";
- parent = "dce smb main";
+ name = "dce_smb_uid";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_smb_uid;
case 12:
- name = "dce smb tid";
- parent = "dce smb main";
+ name = "dce_smb_tid";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_smb_tid;
case 13:
- name = "dce smb fid";
- parent = "dce smb main";
+ name = "dce_smb_fid";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_smb_fid;
case 14:
- name = "dce smb file";
- parent = "dce smb main";
+ name = "dce_smb_file";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_smb_file;
case 15:
- name = "dce smb file detect";
- parent = "dce smb file";
+ name = "dce_smb_file_detect";
+ parent = "dce_smb_file";
return &dce2_smb_pstat_smb_file_detect;
case 16:
- name = "dce smb file api";
- parent = "dce smb file";
+ name = "dce_smb_file_api";
+ parent = "dce_smb_file";
return &dce2_smb_pstat_smb_file_api;
case 17:
- name = "dce smb fingerprint";
- parent = "dce smb main";
+ name = "dce_smb_fingerprint";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_smb_fingerprint;
case 18:
- name = "dce smb negotiate";
- parent = "dce smb main";
+ name = "dce_smb_negotiate";
+ parent = "dce_smb_main";
return &dce2_smb_pstat_smb_negotiate;
}
return nullptr;
dce2SmbShare* ashare = (dce2SmbShare*)a;
dce2SmbShare* bshare = (dce2SmbShare*)b;
- if ((ashare == NULL) || (bshare == NULL))
+ if ((ashare == nullptr) || (bshare == nullptr))
return -1;
/* Just check the ascii string */
{
dce2SmbShare* smb_share = (dce2SmbShare*)data;
- if (smb_share == NULL)
+ if (smb_share == nullptr)
return;
free(smb_share->unicode_str);
smb_share = (dce2SmbShare*)calloc(sizeof(dce2SmbShare),1);
smb_share_key = (dce2SmbShare*)calloc(sizeof(dce2SmbShare),1);
- if ((smb_share == NULL) || (smb_share_key == NULL))
+ if ((smb_share == nullptr) || (smb_share_key == nullptr))
{
FatalError("DCE2 - Could not allocate memory for config\n");
}
smb_share->ascii_str_len = share_len + 1;
smb_share->ascii_str = (char*)calloc(smb_share->ascii_str_len,1);
- if ((smb_share->unicode_str == NULL) || (smb_share->ascii_str == NULL))
+ if ((smb_share->unicode_str == nullptr) || (smb_share->ascii_str == nullptr))
{
FatalError("DCE2 - Could not allocate memory for config\n");
}
smb_share_key->ascii_str_len = smb_share->ascii_str_len;
smb_share_key->ascii_str = (char*)calloc(smb_share_key->ascii_str_len,1);
- if (smb_share_key->ascii_str == NULL)
+ if (smb_share_key->ascii_str == nullptr)
{
FatalError("DCE2 - Could not allocate memory for config\n");
}
if (error)
{
DCE2_ListDestroy(config.smb_invalid_shares);
- config.smb_invalid_shares = NULL;
+ config.smb_invalid_shares = nullptr;
return error;
}
{
LogMessage(" SMB valid versions : all\n");
}
- if (config.smb_invalid_shares != NULL)
+ if (config.smb_invalid_shares != nullptr)
{
dce2SmbShare* share;
LogMessage(" Invalid SMB shares:\n");
for (share = (dce2SmbShare*)DCE2_ListFirst(config.smb_invalid_shares);
- share != NULL;
+ share != nullptr;
share = (dce2SmbShare*)DCE2_ListNext(config.smb_invalid_shares))
{
LogMessage(" %s\n",share->ascii_str);
while (n < len)
{
- DebugFormat(DEBUG_DCE_SMB, " State %d : 0x%02x", ss->paf_state, data[n]);
+ Debug::print(nullptr, 0, DEBUG_DCE_SMB, " State %d : 0x%02x\n", ss->paf_state, data[n]);
switch (ss->paf_state)
{
DebugFormat(DEBUG_DCE_SMB, "%s\n", DCE2_DEBUG__PAF_END_MSG);
return StreamSplitter::FLUSH;
}
- DebugMessage(DEBUG_DCE_SMB, "Invalid NetBIOS header - "
+ Debug::print(nullptr, 0, DEBUG_DCE_SMB, "%s", "Invalid NetBIOS header - "
"entering junk data states.\n");
ss->paf_state = (DCE2_PafSmbStates)(((int)ss->paf_state) + 1);
break;
if (!DCE2_PafSmbIsValidNetbiosHdr((uint32_t)(ss->nb_hdr >> 32), true))
{
- DebugMessage(DEBUG_DCE_SMB, "Invalid NetBIOS header - "
+ Debug::print(nullptr, 0, DEBUG_DCE_SMB, "%s", "Invalid NetBIOS header - "
"staying in State 7.\n");
break;
}
if (((uint32_t)ss->nb_hdr != DCE2_SMB_ID)
&& ((uint32_t)ss->nb_hdr != DCE2_SMB2_ID))
{
- DebugMessage(DEBUG_DCE_SMB, "Invalid SMB ID - "
+ Debug::print(nullptr, 0, DEBUG_DCE_SMB, "%s", "Invalid SMB ID - "
"staying in State 7.\n");
break;
}
#include "dce_tcp.h"
#include "dce_tcp_paf.h"
#include "dce_tcp_module.h"
+#include "dce_co.h"
#include "main/snort_debug.h"
+#include "detection/detect.h"
Dce2TcpFlowData::Dce2TcpFlowData() : FlowData(flow_id)
{
return(&fd->dce2_tcp_session);
}
-static DCE2_TcpSsnData* dce2_create_new_tcp_session(Packet* p, dce2TcpProtoConf config)
+static DCE2_TcpSsnData* dce2_create_new_tcp_session(Packet* p, dce2TcpProtoConf* config)
{
- DCE2_TcpSsnData* dce2_tcp_sess = NULL;
+ DCE2_TcpSsnData* dce2_tcp_sess = nullptr;
Profile profile(dce2_tcp_pstat_new_session);
+ //FIXIT-M Re-evaluate after infrastructure/binder support if autodetect here
+ //is necessary
if (DCE2_TcpAutodetect(p))
{
DebugMessage(DEBUG_DCE_TCP, "DCE over TCP packet detected\n");
DebugFormat(DEBUG_DCE_TCP,"Created (%p)\n", (void*)dce2_tcp_sess);
dce2_tcp_sess->sd.trans = DCE2_TRANS_TYPE__TCP;
- dce2_tcp_sess->sd.server_policy = config.common.policy;
+ dce2_tcp_sess->sd.server_policy = config->common.policy;
dce2_tcp_sess->sd.client_policy = DCE2_POLICY__WINXP;
dce2_tcp_sess->sd.wire_pkt = p;
+ dce2_tcp_sess->sd.config = (void*)config;
DCE2_SsnSetAutodetected(&dce2_tcp_sess->sd, p);
}
return dce2_tcp_sess;
}
-DCE2_TcpSsnData* dce2_handle_tcp_session(Packet* p, dce2TcpProtoConf& config)
+DCE2_TcpSsnData* dce2_handle_tcp_session(Packet* p, dce2TcpProtoConf* config)
{
Profile profile(dce2_tcp_pstat_session);
DCE2_SsnNoInspect(sd);
dce2_tcp_stats.sessions_aborted++;
dce2_tcp_stats.bad_autodetects++;
- return NULL;
+ return nullptr;
}
DCE2_SsnClearAutodetected(sd);
}
DebugFormat(DEBUG_DCE_TCP, "Session pointer: %p\n", (void*)dce2_tcp_sess);
+ if (dce2_tcp_sess)
+ {
+ //FIXIT-M Stack push
- // FIXIT-M add remaining session handling logic
+ p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT;
+ dce2_detected = 0;
+ }
return dce2_tcp_sess;
}
{
DCE2_TcpSsnData* dce2_tcp_sess;
Profile profile(dce2_tcp_pstat_main);
+ if (DCE2_SsnFromServer(p))
+ {
+ DebugMessage(DEBUG_DCE_TCP, "Packet from Server.\n");
+ }
+ else
+ {
+ DebugMessage(DEBUG_DCE_TCP, "Packet from Client.\n");
+ }
assert(p->has_tcp_data());
assert(p->flow);
return;
}
- dce2_tcp_sess = dce2_handle_tcp_session(p, config);
- if (!dce2_tcp_sess)
+ dce2_tcp_sess = dce2_handle_tcp_session(p, &config);
+ if (dce2_tcp_sess)
{
- return;
+ dce2_tcp_stats.tcp_pkts++;
+ DCE2_CoProcess(&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);
+
+ if (!DCE2_SsnAutodetected(&dce2_tcp_sess->sd))
+ DisableInspection();
}
- dce2_tcp_stats.tcp_pkts++;
}
//-------------------------------------------------------------------------
PegCount autoports[65535][DCE2_TRANS_TYPE__MAX];
#endif
*/
+ /* The common stats block has to be at the beginning followed
+ by the protocol specific stats */
+
+ /*common stats -defined in common.h*/
PegCount events;
PegCount sessions_aborted;
PegCount bad_autodetects;
- PegCount tcp_sessions;
- PegCount tcp_pkts;
-
PegCount co_pdus;
PegCount co_bind;
PegCount co_bind_ack;
PegCount co_auth3;
PegCount co_shutdown;
PegCount co_reject;
+ PegCount co_ms_pdu;
PegCount co_other_req;
PegCount co_other_resp;
PegCount co_req_fragments;
PegCount co_srv_min_frag_size;
PegCount co_srv_seg_reassembled;
PegCount co_srv_frag_reassembled;
+
+ /*DCE TCP specific*/
+ PegCount tcp_sessions;
+ PegCount tcp_pkts;
};
extern THREAD_LOCAL dce2TcpStats dce2_tcp_stats;
&& (DceRpcCoVersMin(co_hdr) == DCERPC_PROTO_MINOR_VERS__0)
&& ((p->from_client()
&& DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND)
- || (p->from_server()
+ || (DCE2_SsnFromServer(p)
&& DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND_ACK))
&& (DceRpcCoFragLen(co_hdr) >= sizeof(DceRpcCoHdr)))
{
{ "events", "total events" },
{ "aborted sessions", "total aborted sessions" },
{ "bad autodetects", "total bad autodetects" },
- { "tcp sessions", "total tcp sessions" },
- { "tcp packets", "total tcp packets" },
- { "connection-oriented PDUs", "total connection-oriented PDUs" },
- { "connection-oriented binds", "total connection-oriented binds" },
- { "connection-oriented bind acks", "total connection-oriented binds acks" },
- { "connection-oriented alter contexts", "total connection-oriented alter contexts" },
- { "connection-oriented alter context responses",
+ { "PDUs", "total connection-oriented PDUs" },
+ { "Binds", "total connection-oriented binds" },
+ { "Bind acks", "total connection-oriented binds acks" },
+ { "Alter contexts", "total connection-oriented alter contexts" },
+ { "Alter context responses",
"total connection-oriented alter context responses" },
- { "connection-oriented bind naks", "total connection-oriented bind naks" },
- { "connection-oriented requests", "total connection-oriented requests" },
- { "connection-oriented responses", "total connection-oriented responses" },
- { "connection-oriented cancels", "total connection-oriented cancels" },
- { "connection-oriented orphaned", "total connection-oriented orphaned" },
- { "connection-oriented faults", "total connection-oriented faults" },
- { "connection-oriented auth3s", "total connection-oriented auth3s" },
- { "connection-oriented shutdowns", "total connection-oriented shutdowns" },
- { "connection-oriented rejects", "total connection-oriented rejects" },
- { "connection-oriented other requests", "total connection-oriented other requests" },
- { "connection-oriented other responses", "total connection-oriented other responses" },
- { "connection-oriented request fragments", "total connection-oriented request fragments" },
- { "connection-oriented response fragments", "total connection-oriented response fragments" },
- { "connection-oriented client maximum fragment size",
+ { "Bind naks", "total connection-oriented bind naks" },
+ { "Requests", "total connection-oriented requests" },
+ { "Responses", "total connection-oriented responses" },
+ { "Cancels", "total connection-oriented cancels" },
+ { "Orphaned", "total connection-oriented orphaned" },
+ { "Faults", "total connection-oriented faults" },
+ { "Auth3s", "total connection-oriented auth3s" },
+ { "Shutdowns", "total connection-oriented shutdowns" },
+ { "Rejects", "total connection-oriented rejects" },
+ { "MS RPC/HTTP PDUs", "total connection-oriented MS requests to send RPC over HTTP" },
+ { "Other requests", "total connection-oriented other requests" },
+ { "Other responses", "total connection-oriented other responses" },
+ { "Request fragments", "total connection-oriented request fragments" },
+ { "Response fragments", "total connection-oriented response fragments" },
+ { "Client max fragment size",
"connection-oriented client maximum fragment size" },
- { "connection-oriented client minimum fragment size",
+ { "Client min fragment size",
"connection-oriented client minimum fragment size" },
- { "connection-oriented client segments reassembled",
+ { "Client segs reassembled",
"total connection-oriented client segments reassembled" },
- { "connection-oriented client fragments reassembled",
+ { "Client frags reassembled",
"total connection-oriented client fragments reassembled" },
- { "connection-oriented server maximum fragment size",
+ { "Server max fragment size",
"connection-oriented server maximum fragment size" },
- { "connection-oriented server minimum fragment size",
+ { "Server min fragment size",
"connection-oriented server minimum fragment size" },
- { "connection-oriented server segments reassembled",
+ { "Server segs reassembled",
"total connection-oriented server segments reassembled" },
- { "connection-oriented server fragments reassembled",
+ { "Server frags reassembled",
"total connection-oriented server fragments reassembled" },
+ { "tcp sessions", "total tcp sessions" },
+ { "tcp packets", "total tcp packets" },
{ nullptr, nullptr }
};
switch ( index )
{
case 0:
- name = "dce tcp main";
+ name = "dce_tcp_main";
parent = nullptr;
return &dce2_tcp_pstat_main;
case 1:
- name = "dce tcp session";
- parent = "dce tcp main";
+ name = "dce_tcp_session";
+ parent = "dce_tcp_main";
return &dce2_tcp_pstat_session;
case 2:
- name = "dce tcp new session";
- parent = "dce tcp session";
+ name = "dce_tcp_new_session";
+ parent = "dce_tcp_session";
return &dce2_tcp_pstat_new_session;
case 3:
- name = "dce tcp detect";
- parent = "dce tcp main";
+ name = "dce_tcp_detect";
+ parent = "dce_tcp_main";
return &dce2_tcp_pstat_detect;
case 4:
- name = "dce tcp log";
+ name = "dce_tcp_log";
parent = "dce_tcp_main";
return &dce2_tcp_pstat_log;
case 5:
- name = "dce tcp connection-oriented segment";
- parent = "dce tcp main";
+ name = "dce_tcp_co_segment";
+ parent = "dce_tcp_main";
return &dce2_tcp_pstat_co_seg;
case 6:
- name = "dce tcp connection-oriented fragment";
- parent = "dce tcp main";
+ name = "dce_tcp_co_fragment";
+ parent = "dce_tcp_main";
return &dce2_tcp_pstat_co_frag;
case 7:
- name = "dce tcp connection-oriented reassembly";
- parent = "dce tcp main";
+ name = "dce_tcp_co_reassembly";
+ parent = "dce_tcp_main";
return &dce2_tcp_pstat_co_reass;
case 8:
- name = "dce tcp connection-oriented context";
- parent = "dce tcp main";
+ name = "dce_tcp_co_context";
+ parent = "dce_tcp_main";
return &dce2_tcp_pstat_co_ctx;
}
return nullptr;
return StreamSplitter::ABORT;
}
- if (sd == NULL)
+ if (sd == nullptr)
{
bool autodetected = false;
DebugMessage(DEBUG_DCE_TCP, "No session data - autodetecting\n");
while (n < len)
{
- DebugFormat(DEBUG_DCE_TCP, " State %d : 0x%02x", ds->paf_state, data[n]);
+ Debug::print(nullptr, 0, DEBUG_DCE_TCP, " State %d : 0x%02x\n", ds->paf_state, data[n]);
switch (ds->paf_state)
{
ds->paf_state = (DCE2_PafTcpStates)(((int)ds->paf_state) + 1);
if (ds->byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN)
{
- DebugMessage(DEBUG_DCE_TCP, "Byte order: Little endian\n");
+ Debug::print(nullptr, 0, DEBUG_DCE_TCP, "%s","Byte order: Little endian\n");
}
else
{
- DebugMessage(DEBUG_DCE_TCP, "Byte order: Big endian\n");
+ Debug::print(nullptr, 0, DEBUG_DCE_TCP, "%s","Byte order: Big endian\n");
}
break;
case DCE2_PAF_TCP_STATES__8:
- DebugMessage(DEBUG_DCE_TCP, "First byte of fragment length\n");
+ Debug::print(nullptr, 0, DEBUG_DCE_TCP, "%s", "First byte of fragment length\n");
if (ds->byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN)
ds->frag_len = data[n];
else
ds->paf_state = (DCE2_PafTcpStates)(((int)ds->paf_state) + 1);
break;
case DCE2_PAF_TCP_STATES__9:
- DebugMessage(DEBUG_DCE_TCP, "Second byte of fragment length\n");
+ Debug::print(nullptr, 0, DEBUG_DCE_TCP, "%s", "Second byte of fragment length\n");
if (ds->byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN)
ds->frag_len |= data[n] << 8;
else
/* If we get a bad frag length abort */
if (ds->frag_len < sizeof(DceRpcCoHdr))
{
- DebugFormat(DEBUG_DCE_TCP, "%s\n", DCE2_DEBUG__PAF_END_MSG);
+ Debug::print(nullptr, 0, DEBUG_DCE_TCP, "%s\n", DCE2_DEBUG__PAF_END_MSG);
return StreamSplitter::ABORT;
}
- DebugFormat(DEBUG_DCE_TCP, "Fragment length: %u\n", ds->frag_len);
+ Debug::print(nullptr, 0, DEBUG_DCE_TCP, "Fragment length: %u\n", ds->frag_len);
/* Increment n here so we can continue */
n += ds->frag_len - (uint8_t)ds->paf_state;
* flush just before it */
if ((num_requests == 1) || (n <= len))
tmp_fp += ds->frag_len;
- DebugFormat(DEBUG_DCE_TCP, "Requests: %u\n", num_requests);
+ Debug::print(nullptr, 0, DEBUG_DCE_TCP, "Requests: %u\n", num_requests);
ds->paf_state = DCE2_PAF_TCP_STATES__0;
continue; // we incremented n already
default:
//--------------------------------------------------------------------------
#include "dce_utils.h"
-
+#include "main/snort_debug.h"
/********************************************************************
* Function: DCE2_GetValue()
uint64_t place = 1;
uint64_t max_value = 0;
- if ((end == NULL) || (start == NULL) || (int_value == NULL))
+ if ((end == nullptr) || (start == nullptr) || (int_value == nullptr))
return DCE2_RET__ERROR;
if (start >= end)
return DCE2_RET__SUCCESS;
}
+const char* DCE2_UuidToStr(const Uuid* uuid, DceRpcBoFlag byte_order)
+{
+#define UUID_BUF_SIZE 50
+ static char uuid_buf1[UUID_BUF_SIZE];
+ static char uuid_buf2[UUID_BUF_SIZE];
+ static int buf_num = 0;
+ char* uuid_buf;
+
+ if (buf_num == 0)
+ {
+ uuid_buf = uuid_buf1;
+ buf_num = 1;
+ }
+ else
+ {
+ uuid_buf = uuid_buf2;
+ buf_num = 0;
+ }
+
+ snprintf(uuid_buf, UUID_BUF_SIZE,
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ DceRpcHtonl(&uuid->time_low, byte_order),
+ DceRpcHtons(&uuid->time_mid, byte_order),
+ DceRpcHtons(&uuid->time_high_and_version, byte_order),
+ uuid->clock_seq_and_reserved, uuid->clock_seq_low,
+ uuid->node[0], uuid->node[1], uuid->node[2],
+ uuid->node[3], uuid->node[4], uuid->node[5]);
+
+ uuid_buf[UUID_BUF_SIZE - 1] = '\0';
+
+ return uuid_buf;
+}
+
+void DCE2_PrintPktData(const uint8_t* data, const uint16_t len)
+{
+ unsigned int i, j = 0, line_len = 0;
+ uint8_t hex_buf[16];
+ uint8_t char_buf[16];
+
+ for (i = 0; i < len; i++)
+ {
+ hex_buf[j] = data[i];
+
+ if (isascii((int)data[i]) && isprint((int)data[i]))
+ char_buf[j] = data[i];
+ else
+ char_buf[j] = '.';
+
+ if (line_len == 15)
+ {
+ unsigned int k, sub_line_len = 0;
+ for (k = 0; k <= j; k++)
+ {
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%02x ", hex_buf[k]);
+ if (sub_line_len >= 7)
+ {
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s"," ");
+ sub_line_len = 0;
+ }
+ else
+ {
+ sub_line_len++;
+ }
+ }
+
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s"," ");
+
+ sub_line_len = 0;
+ for (k = 0; k <= j; k++)
+ {
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%c", char_buf[k]);
+ if (sub_line_len >= 7)
+ {
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s"," ");
+ sub_line_len = 0;
+ }
+ else
+ {
+ sub_line_len++;
+ }
+ }
+
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s","\n");
+
+ j = line_len = 0;
+ }
+ else
+ {
+ j++;
+ line_len++;
+ }
+ }
+
+ if (line_len > 0)
+ {
+ unsigned int k, sub_line_len = 0;
+ for (k = 0; k < j; k++)
+ {
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%02x ", hex_buf[k]);
+ if (sub_line_len >= 7)
+ {
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s"," ");
+ sub_line_len = 0;
+ }
+ else
+ {
+ sub_line_len++;
+ }
+ }
+
+ if (k < 8)
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s"," ");
+ else
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s"," ");
+
+ while (k < 16)
+ {
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s"," ");
+ k++;
+ }
+
+ sub_line_len = 0;
+ for (k = 0; k < j; k++)
+ {
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%c", char_buf[k]);
+ if (sub_line_len >= 7)
+ {
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s"," ");
+ sub_line_len = 0;
+ }
+ else
+ {
+ sub_line_len++;
+ }
+ }
+ }
+
+ Debug::print(nullptr, 0, DEBUG_DCE_COMMON,"%s","\n");
+}
+
DCE2_INT_TYPE__UINT64
};
+/* DCE/RPC byte order flag */
+enum DceRpcBoFlag
+{
+ DCERPC_BO_FLAG__NONE,
+ DCERPC_BO_FLAG__BIG_ENDIAN,
+ DCERPC_BO_FLAG__LITTLE_ENDIAN
+};
+
/********************************************************************
* Structures
********************************************************************/
inline bool DCE2_IsEmptyStr(char*);
inline int DCE2_UuidCompare(const void*, const void*);
+const char* DCE2_UuidToStr(const Uuid*, DceRpcBoFlag);
+void DCE2_PrintPktData(const uint8_t*, const uint16_t);
/********************************************************************
* Public function prototypes
return -1;
}
+inline DceRpcBoFlag DceRpcByteOrder(const uint8_t value)
+{
+ if ((value & 0x10) >> 4)
+ return DCERPC_BO_FLAG__LITTLE_ENDIAN;
+
+ return DCERPC_BO_FLAG__BIG_ENDIAN;
+}
+
+inline uint16_t DceRpcNtohs(const uint16_t* ptr, const DceRpcBoFlag bo_flag)
+{
+ uint16_t value;
+
+ if (ptr == nullptr)
+ return 0;
+
+#ifdef WORDS_MUSTALIGN
+ value = *((uint8_t*)ptr) << 8 | *((uint8_t*)ptr + 1);
+#else
+ value = *ptr;
+#endif /* WORDS_MUSTALIGN */
+
+ if (bo_flag == DCERPC_BO_FLAG__NONE)
+ return value;
+
+#ifdef WORDS_BIGENDIAN
+ if (bo_flag == DCERPC_BO_FLAG__BIG_ENDIAN)
+#else
+ if (bo_flag == DCERPC_BO_FLAG__LITTLE_ENDIAN)
+#endif /* WORDS_BIGENDIAN */
+ return value;
+
+ return ((value & 0xff00) >> 8) | ((value & 0x00ff) << 8);
+}
+
+inline uint16_t DceRpcHtons(const uint16_t* ptr, const DceRpcBoFlag bo_flag)
+{
+ return DceRpcNtohs(ptr, bo_flag);
+}
+
+inline uint32_t DceRpcNtohl(const uint32_t* ptr, const DceRpcBoFlag bo_flag)
+{
+ uint32_t value;
+
+ if (ptr == nullptr)
+ return 0;
+
+#ifdef WORDS_MUSTALIGN
+ value = *((uint8_t*)ptr) << 24 | *((uint8_t*)ptr + 1) << 16 |
+ *((uint8_t*)ptr + 2) << 8 | *((uint8_t*)ptr + 3);
+#else
+ value = *ptr;
+#endif /* WORDS_MUSTALIGN */
+
+ if (bo_flag == DCERPC_BO_FLAG__NONE)
+ return value;
+
+#ifdef WORDS_BIGENDIAN
+ if (bo_flag == DCERPC_BO_FLAG__BIG_ENDIAN)
+#else
+ if (bo_flag == DCERPC_BO_FLAG__LITTLE_ENDIAN)
+#endif /* WORDS_BIGENDIAN */
+ return value;
+
+ return ((value & 0xff000000) >> 24) | ((value & 0x00ff0000) >> 8) |
+ ((value & 0x0000ff00) << 8) | ((value & 0x000000ff) << 24);
+}
+
+inline uint32_t DceRpcHtonl(const uint32_t* ptr, const DceRpcBoFlag bo_flag)
+{
+ return DceRpcNtohl(ptr, bo_flag);
+}
+
+inline void DCE2_CopyUuid(Uuid* dst_uuid, const Uuid* pkt_uuid, const DceRpcBoFlag byte_order)
+{
+ dst_uuid->time_low = DceRpcNtohl(&pkt_uuid->time_low, byte_order);
+ dst_uuid->time_mid = DceRpcNtohs(&pkt_uuid->time_mid, byte_order);
+ dst_uuid->time_high_and_version = DceRpcNtohs(&pkt_uuid->time_high_and_version, byte_order);
+ dst_uuid->clock_seq_and_reserved = pkt_uuid->clock_seq_and_reserved;
+ dst_uuid->clock_seq_low = pkt_uuid->clock_seq_low;
+ memcpy(dst_uuid->node, pkt_uuid->node, sizeof(dst_uuid->node));
+}
+
+inline int DCE2_BufferIsEmpty(DCE2_Buffer* buf)
+{
+ if (buf == nullptr)
+ return 1;
+ if ((buf->data == nullptr) || (buf->len == 0))
+ return 1;
+ return 0;
+}
+
+inline uint32_t DCE2_BufferLength(DCE2_Buffer* buf)
+{
+ if (buf == nullptr)
+ return 0;
+ return buf->len;
+}
+
+inline uint8_t* DCE2_BufferData(DCE2_Buffer* buf)
+{
+ if (buf == nullptr)
+ return nullptr;
+ return buf->data;
+}
+
+inline void DCE2_BufferEmpty(DCE2_Buffer* buf)
+{
+ if (buf == nullptr)
+ return;
+ buf->len = 0;
+}
+
+#define DCE2_MOVE(data_ptr, data_len, amount) \
+ { data_len -= (amount); data_ptr = (uint8_t*)data_ptr + (amount); }
+
#endif /* _DCE2_UTILS_H_ */