From 425bc4cf45fb2f4afdbc36c80a99db4d8b1520cc Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Thu, 23 Jul 2026 12:30:29 +0530 Subject: [PATCH 1/6] dcerpc: set PDU type from header in tx The constructor for creation of a new DCERPC Transaction unconditionally set the PDU type to REQUEST and RESPONSE in the respective direction. This means that if a transaction had only seen a request of type BIND, its response type would still be set to RESPONSE which is incorrect pairing and assumption. Fix the parser to only use PDU type reliably derived from the header. Do not return the corresponding PDU type for a request/response if it does not match the transaction's stated type for that respective direction. Bug 8759 --- rust/src/dcerpc/dcerpc.rs | 12 ++++++------ rust/src/dcerpc/dcerpc_udp.rs | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index 7ea109b69f..4efe040740 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -221,9 +221,9 @@ impl DCERPCTransaction { return Self { stub_data_buffer_ts: Vec::new(), stub_data_buffer_tc: Vec::new(), - req_cmd: DCERPC_TYPE_REQUEST, - resp_cmd: DCERPC_TYPE_RESPONSE, activityuuid: Vec::new(), + req_cmd: DCERPC_TYPE_UNKNOWN, + resp_cmd: DCERPC_TYPE_UNKNOWN, tx_data: AppLayerTxData::new(), ..Default::default() }; @@ -445,8 +445,8 @@ impl DCERPCState { if tx.req_done || tx.req_lost { continue; } - let resp_cmd = get_resp_type_for_req(cmd); - if resp_cmd != tx.resp_cmd { + /* do not match if the transaction's command does not match expected */ + if tx.req_cmd != cmd && get_req_type_for_resp(tx.resp_cmd) != cmd { continue; } } @@ -454,8 +454,8 @@ impl DCERPCState { if tx.resp_done || tx.resp_lost { continue; } - let req_cmd = get_req_type_for_resp(cmd); - if req_cmd != tx.req_cmd { + /* do not match if the transaction's command does not match expected */ + if tx.resp_cmd != cmd && get_resp_type_for_req(tx.req_cmd) != cmd { continue; } } diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs index 538ee3d3c6..0312c4187d 100644 --- a/rust/src/dcerpc/dcerpc_udp.rs +++ b/rust/src/dcerpc/dcerpc_udp.rs @@ -190,6 +190,7 @@ impl DCERPCUDPState { let max_size = cfg_max_stub_size() as usize; match hdr.pkt_type { DCERPC_TYPE_REQUEST => { + tx.req_cmd = hdr.pkt_type; tx.frag_cnt_ts = tx.frag_cnt_ts.saturating_add(1); if input.len() + tx.stub_data_buffer_ts.len() < max_size { tx.stub_data_buffer_ts.extend_from_slice(input); @@ -203,6 +204,7 @@ impl DCERPCUDPState { return true; } DCERPC_TYPE_RESPONSE => { + tx.resp_cmd = hdr.pkt_type; tx.frag_cnt_tc = tx.frag_cnt_tc.saturating_add(1); if input.len() + tx.stub_data_buffer_tc.len() < max_size { tx.stub_data_buffer_tc.extend_from_slice(input); From cbd4e6eb086bd2f6c3cf6fba100e6d1489ab80aa Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Thu, 23 Jul 2026 13:27:39 +0530 Subject: [PATCH 2/6] dcerpc: move drep alloc on stack DCERPC's drep field is always 4 bytes for connection oriented PDU types. Make it a byte array of exactly 4 bytes to store the entire header on stack. --- rust/src/dcerpc/dcerpc.rs | 4 ++-- rust/src/dcerpc/parser.rs | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index 4efe040740..81e7e8002f 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -278,13 +278,13 @@ pub struct Uuid { pub node: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct DCERPCHdr { pub rpc_vers: u8, pub rpc_vers_minor: u8, pub hdrtype: u8, pub pfc_flags: u8, - pub packed_drep: Vec, + pub packed_drep: [u8; 4], pub frag_length: u16, pub auth_length: u16, pub call_id: u32, diff --git a/rust/src/dcerpc/parser.rs b/rust/src/dcerpc/parser.rs index a80126b1c7..3fe2adea48 100644 --- a/rust/src/dcerpc/parser.rs +++ b/rust/src/dcerpc/parser.rs @@ -233,7 +233,12 @@ pub(super) fn parse_dcerpc_header(i: &[u8]) -> IResult<&[u8], DCERPCHdr> { rpc_vers_minor, hdrtype, pfc_flags, - packed_drep: packed_drep.to_vec(), + packed_drep: [ + packed_drep[0], + packed_drep[1], + packed_drep[2], + packed_drep[3], + ], frag_length, auth_length, call_id, From 29eebd5954aa1179a771047c4af05d7f1043a291 Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Thu, 23 Jul 2026 18:05:18 +0530 Subject: [PATCH 3/6] dcerpc: mark request/response seen In order to avoid a blanket match on unknown PDUs with dcerpc.ptype keyword. --- rust/src/dcerpc/dcerpc.rs | 9 +++++++++ rust/src/dcerpc/dcerpc_udp.rs | 2 ++ 2 files changed, 11 insertions(+) diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index 81e7e8002f..2cd6cbb85c 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -205,6 +205,8 @@ pub struct DCERPCTransaction { pub resp_lost: bool, pub req_cmd: u8, pub resp_cmd: u8, + pub req_seen: bool, + pub resp_seen: bool, pub activityuuid: Vec, pub seqnum: u32, pub tx_data: AppLayerTxData, @@ -585,6 +587,7 @@ impl DCERPCState { } let mut tx = self.create_tx(hdr); tx.req_cmd = hdr.hdrtype; + tx.req_seen = true; tx.req_done = true; if let Some(flow) = self.flow { sc_app_layer_parser_trigger_raw_stream_inspection( @@ -762,6 +765,7 @@ impl DCERPCState { match transaction { Some(ref mut tx) => { tx.req_cmd = hdr_type; + tx.req_seen = true; tx.ctxid = request.ctxid; tx.opnum = request.opnum; tx.first_request_seen = request.first_request_seen; @@ -769,6 +773,7 @@ impl DCERPCState { None => { let mut tx = self.create_tx(hdr); tx.req_cmd = hdr_type; + tx.req_seen = true; tx.ctxid = request.ctxid; tx.opnum = request.opnum; tx.first_request_seen = request.first_request_seen; @@ -949,10 +954,12 @@ impl DCERPCState { self.get_tx_by_call_id(current_call_id, Direction::ToClient, hdrtype) { tx.resp_cmd = hdrtype; + tx.resp_seen = true; tx } else { let mut tx = self.create_tx(&hdr); tx.resp_cmd = hdrtype; + tx.resp_seen = true; self.transactions.push_back(tx); self.transactions.back_mut().unwrap() }; @@ -980,10 +987,12 @@ impl DCERPCState { match transaction { Some(tx) => { tx.resp_cmd = hdrtype; + tx.resp_seen = true; } None => { let mut tx = self.create_tx(&hdr); tx.resp_cmd = hdrtype; + tx.resp_seen = true; self.transactions.push_back(tx); } }; diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs index 0312c4187d..66b91b23d6 100644 --- a/rust/src/dcerpc/dcerpc_udp.rs +++ b/rust/src/dcerpc/dcerpc_udp.rs @@ -191,6 +191,7 @@ impl DCERPCUDPState { match hdr.pkt_type { DCERPC_TYPE_REQUEST => { tx.req_cmd = hdr.pkt_type; + tx.req_seen = true; tx.frag_cnt_ts = tx.frag_cnt_ts.saturating_add(1); if input.len() + tx.stub_data_buffer_ts.len() < max_size { tx.stub_data_buffer_ts.extend_from_slice(input); @@ -205,6 +206,7 @@ impl DCERPCUDPState { } DCERPC_TYPE_RESPONSE => { tx.resp_cmd = hdr.pkt_type; + tx.resp_seen = true; tx.frag_cnt_tc = tx.frag_cnt_tc.saturating_add(1); if input.len() + tx.stub_data_buffer_tc.len() < max_size { tx.stub_data_buffer_tc.extend_from_slice(input); From d29d84690cd5bebc65011882f2841184ed0b71c6 Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Thu, 23 Jul 2026 16:38:35 +0530 Subject: [PATCH 4/6] dcerpc: store unhandled packet types This allows to at least log and even match on unhandled packet types even if the parser does not do anything extra with them. All this information was already in the header but since transactions were only created for the supported PDU types, there was no way to match on these. Store the packet type information for all unhandled types in a new transaction. Note that one transaction is created for each unhandled type and there is no request-response pair matching done. Feature 8762 --- rust/src/dcerpc/dcerpc.rs | 44 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index 2cd6cbb85c..7a6f206b49 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -801,6 +801,24 @@ impl DCERPCState { } } + fn save_unhandled_ptypes(&mut self, hdr: &DCERPCHdr, direction: Direction) { + /* Create tx even on unhandled PDU types to be able to + * at least issue a match on header only fields */ + let mut tx = self.create_tx(hdr); + if direction == Direction::ToServer { + tx.req_cmd = hdr.hdrtype; + tx.req_seen = true; + } else { + tx.resp_cmd = hdr.hdrtype; + tx.resp_seen = true; + } + tx.req_done = true; + tx.resp_done = true; + self.transactions.push_back(tx); + // recognized DCERPC PDU types that we do not process + SCLogDebug!("Unhandled packet type: {:?}", hdr.hdrtype); + } + pub fn handle_input_data( &mut self, stream_slice: StreamSlice, direction: Direction, ) -> AppLayerResult { @@ -893,6 +911,15 @@ impl DCERPCState { // input only consists of the header and that was consumed, so, return early cur_i = &cur_i[fraglen as usize..]; consumed += fraglen as u32; + // save header-only PDU types too + if matches!( + hdr.hdrtype, + DCERPC_TYPE_SHUTDOWN | DCERPC_TYPE_CO_CANCEL | DCERPC_TYPE_ORPHANED + ) { + self.save_unhandled_ptypes(&hdr, direction); + /* DCERPC_TYPE_CO_CANCEL and ORPHANED can optionally be more than header + * too so they're also handled later on the in the unrecognized branch */ + } continue; } cmp::Ordering::Greater => {} @@ -1006,9 +1033,24 @@ impl DCERPCState { return AppLayerResult::err(); } } + DCERPC_TYPE_PING + | DCERPC_TYPE_FAULT + | DCERPC_TYPE_WORKING + | DCERPC_TYPE_NOCALL + | DCERPC_TYPE_REJECT + | DCERPC_TYPE_ACK + | DCERPC_TYPE_CL_CANCEL + | DCERPC_TYPE_FACK + | DCERPC_TYPE_CANCEL_ACK + | DCERPC_TYPE_BINDNAK + | DCERPC_TYPE_AUTH3 + | DCERPC_TYPE_CO_CANCEL + | DCERPC_TYPE_ORPHANED + | DCERPC_TYPE_RTS => { + self.save_unhandled_ptypes(&hdr, direction); + } _ => { SCLogDebug!("Unrecognized packet type: {:?}", hdrtype); - // skip unrecognized packet types such as AUTH3 } } consumed += fraglen as u32; From 71c8f2349d31cd2c9b343096fd64a192e31563d3 Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Fri, 24 Jul 2026 12:27:16 +0530 Subject: [PATCH 5/6] dcerpc/udp: store unhandled packet types DCERPC over UDP supports more PDU types than the current acceptable REQUEST and RESPONSE. All states until cancel_ack are valid PDU types. While Suricata only supports processing the REQUEST and RESPONSE types, it is prudent to expose all other supported PDU types as well to allow matches on upcoming keywords like dcerpc.ptype. Feature 8762 --- rust/src/dcerpc/dcerpc_udp.rs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs index 66b91b23d6..898b7df19b 100644 --- a/rust/src/dcerpc/dcerpc_udp.rs +++ b/rust/src/dcerpc/dcerpc_udp.rs @@ -19,7 +19,10 @@ use crate::applayer::{self, *}; use crate::core; use crate::dcerpc::dcerpc::{ cfg_max_stub_size, get_alstate_progress, DCERPCTransaction, ALPROTO_DCERPC, DCERPC_MAX_TX, - DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, PARSER_NAME, PFCL1_FRAG, PFCL1_LASTFRAG, + DCERPC_TYPE_ACK, DCERPC_TYPE_CANCEL_ACK, DCERPC_TYPE_CL_CANCEL, DCERPC_TYPE_FACK, + DCERPC_TYPE_FAULT, DCERPC_TYPE_NOCALL, DCERPC_TYPE_PING, DCERPC_TYPE_REJECT, + DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, DCERPC_TYPE_WORKING, PARSER_NAME, PFCL1_FRAG, + PFCL1_LASTFRAG, }; use crate::dcerpc::parser; use crate::direction::{Direction, DIR_BOTH}; @@ -163,8 +166,8 @@ impl DCERPCUDPState { } pub fn handle_fragment_data(&mut self, hdr: &DCERPCHdrUdp, input: &[u8]) -> bool { - if hdr.pkt_type != DCERPC_TYPE_REQUEST && hdr.pkt_type != DCERPC_TYPE_RESPONSE { - SCLogDebug!("Unrecognized packet type"); + if hdr.pkt_type > DCERPC_TYPE_CANCEL_ACK { + SCLogDebug!("Unrecognized packet type: {}", hdr.pkt_type); return false; } @@ -219,8 +222,32 @@ impl DCERPCUDPState { } return true; } + /* Unhandled connectionless PDU types are not otherwise + * processed; store the PDU type so dcerpc.ptype can match */ + DCERPC_TYPE_PING | DCERPC_TYPE_ACK | DCERPC_TYPE_CL_CANCEL => { + // client-to-server PDU types + tx.req_cmd = hdr.pkt_type; + tx.req_seen = true; + tx.req_done = true; + tx.resp_done = true; + return true; + } + // server-to-client PDU types (fack is bidirectional in CL and + // is recorded here as a response) + DCERPC_TYPE_FAULT + | DCERPC_TYPE_WORKING + | DCERPC_TYPE_NOCALL + | DCERPC_TYPE_REJECT + | DCERPC_TYPE_FACK + | DCERPC_TYPE_CANCEL_ACK => { + tx.resp_cmd = hdr.pkt_type; + tx.resp_seen = true; + tx.req_done = true; + tx.resp_done = true; + return true; + } _ => { - // unreachable + // unreachable: pkt_type is guarded to 0..=10 above } } } From 81a80a70644067cca12b8892b0a00e5af5d9c7b0 Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Thu, 23 Jul 2026 11:07:01 +0530 Subject: [PATCH 6/6] detect: add dcerpc.ptype keyword Add keyword to match on the DCERPC PDU type. This information is found in the DCERPC Header as a u8 so, the DetectUint* API is leveraged. Feature 8733 --- doc/userguide/rules/dcerpc-keywords.rst | 47 +++++++++++++ etc/schema.json | 14 +++- rust/src/dcerpc/detect.rs | 93 ++++++++++++++++++++++++- rust/src/smb/detect.rs | 21 +++++- 4 files changed, 170 insertions(+), 5 deletions(-) diff --git a/doc/userguide/rules/dcerpc-keywords.rst b/doc/userguide/rules/dcerpc-keywords.rst index a84d099847..697f356efa 100644 --- a/doc/userguide/rules/dcerpc-keywords.rst +++ b/doc/userguide/rules/dcerpc-keywords.rst @@ -59,6 +59,53 @@ Examples:: dcerpc.opnum can since suricata 9 use an :ref:`unsigned 16-bits integer `. +dcerpc.ptype +------------ + +Match on the PDU type of a DCERPC header. On a to_server match the request PDU +type is used, on a to_client match the response PDU type is used. + +The format of the keyword:: + + dcerpc.ptype:; + dcerpc.ptype:[>,<,!,=]; + +The PDU type values are: + +===== ===================== +Value PDU type +===== ===================== +0 request +1 ping +2 response +3 fault +4 working +5 nocall +6 reject +7 ack +8 cl_cancel +9 fack +10 cancel_ack +11 bind +12 bind_ack +13 bind_nak +14 alter_context +15 alter_context_resp +16 auth3 +17 shutdown +18 co_cancel +19 orphaned +20 rts +===== ===================== + +Examples:: + + dcerpc.ptype:11; + dcerpc.ptype:!0; + dcerpc.ptype:>10; + +dcerpc.ptype uses an :ref:`unsigned 8-bits integer `. + dcerpc.stub_data ---------------- diff --git a/etc/schema.json b/etc/schema.json index ed0f01107b..5d1391e5af 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -450,7 +450,12 @@ } }, "request": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dcerpc.ptype" + ] + } }, "res": { "type": "object", @@ -465,7 +470,12 @@ } }, "response": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dcerpc.ptype" + ] + } }, "rpc_version": { "type": "string" diff --git a/rust/src/dcerpc/detect.rs b/rust/src/dcerpc/detect.rs index 03241f7a87..9d097cb68d 100644 --- a/rust/src/dcerpc/detect.rs +++ b/rust/src/dcerpc/detect.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Open Information Security Foundation +/* Copyright (C) 2020-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -22,7 +22,9 @@ use super::dcerpc::{ use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER}; use crate::detect::uint::{detect_match_uint, detect_parse_uint, DetectUintData}; use crate::detect::{helper_keyword_register_sticky_buffer, SigTableElmtStickyBuffer}; -use crate::smb::detect::{smb_tx_get_stub_data, smb_tx_match_dce_iface, smb_tx_match_dce_opnum}; +use crate::smb::detect::{ + smb_tx_get_stub_data, smb_tx_match_dce_iface, smb_tx_match_dce_opnum, smb_tx_match_dce_ptype, +}; use crate::smb::smb::ALPROTO_SMB; use std::ffi::CStr; use std::os::raw::{c_char, c_int, c_void}; @@ -439,9 +441,85 @@ unsafe extern "C" fn dcerpc_tx_get_stub_data( return buffer; } +fn parse_dcerpc_ptype(arg: &str) -> Option> { + if let Ok((_, ctx)) = detect_parse_uint::(arg) { + return Some(ctx); + } + None +} + +unsafe extern "C" fn dcerpc_ptype_parse(carg: *const c_char) -> *mut c_void { + if let Ok(arg) = CStr::from_ptr(carg).to_str() { + if let Some(detect) = parse_dcerpc_ptype(arg) { + return Box::into_raw(Box::new(detect)) as *mut _; + } + } + return std::ptr::null_mut(); +} + +unsafe extern "C" fn dcerpc_ptype_setup( + de: *mut DetectEngineCtx, s: *mut Signature, raw: *const libc::c_char, +) -> c_int { + if SCDetectSignatureSetAppProto(s, ALPROTO_DCERPC) != 0 { + return -1; + } + let ctx = dcerpc_ptype_parse(raw); + if ctx.is_null() { + return -1; + } + if SCSigMatchAppendSMToList( + de, + s, + G_DCERPC_PTYPE_KW_ID, + ctx as *mut SigMatchCtx, + G_DCERPC_GENERIC_BUFFER_ID, + ) + .is_null() + { + dcerpc_ptype_free(std::ptr::null_mut(), ctx); + return -1; + } + return 0; +} + +unsafe fn dcerpc_tx_match_dce_ptype(tx: *mut c_void, ctx: *const SigMatchCtx, flags: u8) -> c_int { + let tx = cast_pointer!(tx, DCERPCTransaction); + let du8 = cast_pointer!(ctx, DetectUintData); + + if flags & STREAM_TOSERVER != 0 { + if tx.req_seen && detect_match_uint(du8, tx.req_cmd) { + return 1; + } + } else if tx.resp_seen && detect_match_uint(du8, tx.resp_cmd) { + return 1; + } + return 0; +} + +unsafe extern "C" fn dcerpc_ptype_match( + _de: *mut DetectEngineThreadCtx, f: *mut crate::flow::Flow, flags: u8, _state: *mut c_void, + tx: *mut c_void, _sig: *const Signature, ctx: *const SigMatchCtx, +) -> c_int { + if SCFlowGetAppProtocol(f) == ALPROTO_DCERPC { + return dcerpc_tx_match_dce_ptype(tx, ctx, flags); + } + if smb_tx_match_dce_ptype(tx, ctx, flags) != 1 { + return 0; + } + + return 1; +} + +unsafe extern "C" fn dcerpc_ptype_free(_de: *mut DetectEngineCtx, ptr: *mut c_void) { + if !ptr.is_null() { + std::mem::drop(Box::from_raw(ptr as *mut DetectUintData)); + } +} + static mut G_DCERPC_OPNUM_KW_ID: u16 = 0; static mut G_DCERPC_GENERIC_BUFFER_ID: c_int = 0; static mut G_DCERPC_IFACE_KW_ID: u16 = 0; +static mut G_DCERPC_PTYPE_KW_ID: u16 = 0; static mut G_DCERPC_STUB_BUFFER_ID: c_int = 0; #[no_mangle] @@ -490,6 +568,17 @@ pub unsafe extern "C" fn SCDetectDcerpcRegister() { b"dce_iface\0".as_ptr() as *const libc::c_char, ); + let kw = SCSigTableAppLiteElmt { + name: b"dcerpc.ptype\0".as_ptr() as *const libc::c_char, + desc: b"match on the PDU type in a DCERPC header\0".as_ptr() as *const libc::c_char, + url: b"/rules/dcerpc-keywords.html#dcerpc-ptype\0".as_ptr() as *const libc::c_char, + AppLayerTxMatch: Some(dcerpc_ptype_match), + Setup: Some(dcerpc_ptype_setup), + Free: Some(dcerpc_ptype_free), + flags: 0, + }; + G_DCERPC_PTYPE_KW_ID = SCDetectHelperKeywordRegister(&kw); + let kw_stub = SigTableElmtStickyBuffer { name: String::from("dcerpc.stub_data"), desc: String::from("match on the stub data in a DCERPC packet"), diff --git a/rust/src/smb/detect.rs b/rust/src/smb/detect.rs index dcb16bc4d5..0411a5626c 100644 --- a/rust/src/smb/detect.rs +++ b/rust/src/smb/detect.rs @@ -19,7 +19,7 @@ use super::smb::ALPROTO_SMB; use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER}; use crate::dcerpc::dcerpc::DCERPC_TYPE_REQUEST; use crate::dcerpc::detect::{DCEIfaceData, DCEOpnumData, DETECT_DCE_OPNUM_RANGE_UNINITIALIZED}; -use crate::detect::uint::detect_match_uint; +use crate::detect::uint::{detect_match_uint, DetectUintData}; use crate::detect::{helper_keyword_register_sticky_buffer, SigTableElmtStickyBuffer}; use crate::direction::Direction; use crate::smb::smb::*; @@ -125,6 +125,25 @@ pub(crate) unsafe extern "C" fn smb_tx_match_dce_opnum( return 0; } +pub(crate) unsafe extern "C" fn smb_tx_match_dce_ptype( + tx: *mut c_void, ctx: *const SigMatchCtx, flags: u8, +) -> u8 { + let tx = cast_pointer!(tx, SMBTransaction); + let du8 = cast_pointer!(ctx, DetectUintData); + + if let Some(SMBTransactionTypeData::DCERPC(ref x)) = tx.type_data { + if flags & STREAM_TOSERVER != 0 { + if x.req_set && detect_match_uint(du8, x.req_cmd) { + return 1; + } + } else if x.res_set && detect_match_uint(du8, x.res_cmd) { + return 1; + } + } + + return 0; +} + /* mimic logic that is/was in the C code: * - match on REQUEST (so not on BIND/BINDACK (probably for mixing with * dce_opnum and dce_stub_data)