use crate::applayer::{AppLayerResult, AppLayerTxData};
use crate::core;
use crate::dcerpc::dcerpc::{
- DCERPCTransaction, DCERPCUuidEntry, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, PFC_FIRST_FRAG,
+ DCERPCTransaction, DCERPCUuidEntry, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, PFCL1_FRAG, PFCL1_LASTFRAG,
};
use crate::dcerpc::parser;
-use std::cmp;
// Constant DCERPC UDP Header length
pub const DCERPC_UDP_HDR_LEN: i32 = 80;
};
}
- fn create_tx(&mut self, serial_no: u16) -> DCERPCTransaction {
+ fn create_tx(&mut self, hdr: &DCERPCHdrUdp) -> DCERPCTransaction {
let mut tx = DCERPCTransaction::new();
- let endianness = self.get_hdr_drep_0() & 0x10;
tx.id = self.tx_id;
- tx.call_id = serial_no as u32;
- tx.endianness = endianness;
+ tx.endianness = hdr.drep[0] & 0x10;
+ tx.activityuuid = hdr.activityuuid.to_vec();
+ tx.seqnum = hdr.seqnum;
self.tx_id += 1;
tx
}
}
}
- fn evaluate_serial_no(&mut self) -> u16 {
- let mut serial_no: u16;
- let mut serial_hi: u8 = 0;
- let mut serial_lo: u8 = 0;
- let endianness = self.get_hdr_drep_0();
- if let Some(ref hdr) = &self.header {
- serial_hi = hdr.serial_hi;
- serial_lo = hdr.serial_lo;
- }
- if endianness & 0x10 == 0 {
- serial_no = serial_lo as u16;
- serial_no = serial_no.rotate_left(8) | serial_hi as u16;
- } else {
- serial_no = serial_hi as u16;
- serial_no = serial_no.rotate_left(8) | serial_lo as u16;
- }
- serial_no
- }
-
- fn find_tx(&mut self, serial_no: u16) -> Option<&mut DCERPCTransaction> {
+ fn find_incomplete_tx(&mut self, hdr: &DCERPCHdrUdp) -> Option<&mut DCERPCTransaction> {
for tx in &mut self.transactions {
- let found = tx.call_id == (serial_no as u32);
- if found {
- return Some(tx);
+ if tx.seqnum == hdr.seqnum && tx.activityuuid == hdr.activityuuid {
+ if (hdr.pkt_type == DCERPC_TYPE_REQUEST && !tx.req_done) ||
+ (hdr.pkt_type == DCERPC_TYPE_RESPONSE && !tx.resp_done) {
+ SCLogDebug!("found tx id {}, last tx_id {}, {} {}", tx.id, self.tx_id, tx.seqnum, tx.activityuuid[0]);
+ return Some(tx);
+ }
}
}
None
}
- fn get_hdr_pkt_type(&self) -> Option<u8> {
- debug_validate_bug_on!(self.header.is_none());
- if let Some(ref hdr) = &self.header {
- return Some(hdr.pkt_type);
+ 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");
+ return false;
}
- // Shouldn't happen
- None
- }
- fn get_hdr_flags1(&self) -> Option<u8> {
- debug_validate_bug_on!(self.header.is_none());
- if let Some(ref hdr) = &self.header {
- return Some(hdr.flags1);
+ let mut otx = self.find_incomplete_tx(hdr);
+ if otx.is_none() {
+ let ntx = self.create_tx(hdr);
+ SCLogDebug!("new tx id {}, last tx_id {}, {} {}", ntx.id, self.tx_id, ntx.seqnum, ntx.activityuuid[0]);
+ self.transactions.push(ntx);
+ otx = self.transactions.last_mut();
}
- // Shouldn't happen
- None
- }
- fn get_hdr_drep_0(&self) -> u8 {
- debug_validate_bug_on!(self.header.is_none());
- if let Some(ref hdr) = &self.header {
- return hdr.drep[0];
+ if let Some(tx) = otx {
+ let done = (hdr.flags1 & PFCL1_FRAG) == 0 || (hdr.flags1 & PFCL1_LASTFRAG) != 0;
+
+ match hdr.pkt_type {
+ DCERPC_TYPE_REQUEST => {
+ tx.stub_data_buffer_ts.extend_from_slice(&input);
+ tx.frag_cnt_ts += 1;
+ if done {
+ tx.req_done = true;
+ }
+ return true;
+ }
+ DCERPC_TYPE_RESPONSE => {
+ tx.stub_data_buffer_tc.extend_from_slice(&input);
+ tx.frag_cnt_tc += 1;
+ if done {
+ tx.resp_done = true;
+ }
+ return true;
+ }
+ _ => {
+ // unreachable
+ }
+ }
}
- 0
+ return false; // unreachable
}
- pub fn get_hdr_fraglen(&self) -> Option<u16> {
- debug_validate_bug_on!(self.header.is_none());
- if let Some(ref hdr) = &self.header {
- return Some(hdr.fraglen);
- }
- // Shouldn't happen
- None
- }
-
- pub fn handle_fragment_data(&mut self, input: &[u8], input_len: u16) -> u16 {
- let retval: u16;
- let hdrflags1 = self.get_hdr_flags1().unwrap_or(0);
- let fraglenleft = self.fraglenleft;
- let hdrtype = self.get_hdr_pkt_type().unwrap_or(0);
- let serial_no = self.evaluate_serial_no();
- let tx;
- if let Some(transaction) = self.find_tx(serial_no) {
- tx = transaction;
- } else {
- SCLogDebug!(
- "No transaction found matching the serial number: {:?}",
- serial_no
- );
- return 0;
- }
-
- // Update the stub params based on the packet type
- match hdrtype {
- DCERPC_TYPE_REQUEST => {
- retval = evaluate_stub_params(
- input,
- input_len,
- hdrflags1,
- fraglenleft,
- &mut tx.stub_data_buffer_ts,
- &mut tx.stub_data_buffer_len_ts,
- );
- tx.req_done = true;
- tx.frag_cnt_ts += 1;
- }
- DCERPC_TYPE_RESPONSE => {
- retval = evaluate_stub_params(
- input,
- input_len,
- hdrflags1,
- fraglenleft,
- &mut tx.stub_data_buffer_tc,
- &mut tx.stub_data_buffer_len_tc,
- );
- tx.resp_done = true;
- tx.frag_cnt_tc += 1;
- }
- _ => {
- SCLogDebug!("Unrecognized packet type");
- return 0;
- }
+ pub fn handle_input_data(&mut self, input: &[u8]) -> AppLayerResult {
+ // Input length should at least be header length
+ if (input.len() as i32) < DCERPC_UDP_HDR_LEN {
+ return AppLayerResult::err();
}
- // Update the remaining fragment length
- self.fraglenleft -= retval;
-
- retval
- }
- pub fn process_header(&mut self, input: &[u8]) -> i32 {
+ // Call header parser first
match parser::parse_dcerpc_udp_header(input) {
Ok((leftover_bytes, header)) => {
if header.rpc_vers != 4 {
SCLogDebug!("DCERPC UDP Header did not validate.");
- return -1;
+ return AppLayerResult::err();
+ }
+ if leftover_bytes.len() < header.fraglen as usize {
+ SCLogDebug!("Insufficient data: leftover_bytes {}, fraglen {}", leftover_bytes.len(), header.fraglen);
+ return AppLayerResult::err();
+ }
+ if !self.handle_fragment_data(&header, &leftover_bytes[..header.fraglen as usize]) {
+ return AppLayerResult::err();
}
let mut uuidentry = DCERPCUuidEntry::new();
let auuid = header.activityuuid.to_vec();
uuidentry.uuid = auuid;
self.uuid_list.push(uuidentry);
- self.header = Some(header);
- (input.len() - leftover_bytes.len()) as i32
}
Err(nom::Err::Incomplete(_)) => {
// Insufficient data.
SCLogDebug!("Insufficient data while parsing DCERPC request");
- -1
+ return AppLayerResult::err();
}
Err(_) => {
// Error, probably malformed data.
SCLogDebug!("An error occurred while parsing DCERPC request");
- -1
- }
- }
- }
-
- pub fn handle_input_data(&mut self, input: &[u8]) -> AppLayerResult {
- // Input length should at least be header length
- if (input.len() as i32) < DCERPC_UDP_HDR_LEN {
- return AppLayerResult::err();
- }
- // Call header parser first
- let mut parsed = self.process_header(input);
- if parsed == -1 {
- return AppLayerResult::err();
- }
-
- let mut input_left = input.len() as i32 - parsed;
- let fraglen = self.get_hdr_fraglen().unwrap_or(0);
- self.fraglenleft = fraglen;
- let serial_no = self.evaluate_serial_no();
- let tx = self.create_tx(serial_no);
- self.transactions.push(tx);
- // Parse rest of the body
- while parsed >= DCERPC_UDP_HDR_LEN && parsed < fraglen as i32 && input_left > 0 {
- let retval = self.handle_fragment_data(&input[parsed as usize..], input_left as u16);
- if retval > 0 && retval <= input_left as u16 {
- parsed += retval as i32;
- input_left -= retval as i32;
- } else if input_left > 0 {
- SCLogDebug!("Error parsing DCERPC UDP Fragment Data");
- parsed -= input_left;
- input_left = 0;
+ return AppLayerResult::err();
}
}
return AppLayerResult::ok();
}
}
-fn evaluate_stub_params(
- input: &[u8], input_len: u16, hdrflags: u8, lenleft: u16, stub_data_buffer: &mut Vec<u8>,
- stub_data_buffer_len: &mut u32,
-) -> u16 {
- let stub_len: u16;
- stub_len = cmp::min(lenleft, input_len);
- if stub_len == 0 {
- return 0;
- }
- // If the UDP frag is the the first frag irrespective of it being a part of
- // a multi frag PDU or not, it indicates the previous PDU's stub would
- // have been buffered and processed and we can use the buffer to hold
- // frags from a fresh request/response
- if hdrflags & PFC_FIRST_FRAG > 0 {
- *stub_data_buffer_len = 0;
- }
-
- let input_slice = &input[..stub_len as usize];
- stub_data_buffer.extend_from_slice(&input_slice);
- *stub_data_buffer_len += stub_len as u32;
-
- stub_len
-}
-
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_parse(
_flow: *mut core::Flow, state: &mut DCERPCUDPState, _pstate: *mut std::os::raw::c_void,
mod tests {
use crate::applayer::AppLayerResult;
use crate::dcerpc::dcerpc_udp::DCERPCUDPState;
+ use crate::dcerpc::parser;
#[test]
fn test_process_header_udp_incomplete_hdr() {
0x1c, 0x7d, 0xcf, 0x11,
];
- let mut dcerpcudp_state = DCERPCUDPState::new();
- assert_eq!(-1, dcerpcudp_state.process_header(request));
+ match parser::parse_dcerpc_udp_header(request) {
+ Ok((_rem, _header)) => {
+ { assert!(false); }
+ }
+ _ => {}
+ }
}
#[test]
0x79, 0xbe, 0x01, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00,
];
- let mut dcerpcudp_state = DCERPCUDPState::new();
- assert_eq!(80, dcerpcudp_state.process_header(request));
+ match parser::parse_dcerpc_udp_header(request) {
+ Ok((rem, header)) => {
+ assert_eq!(4, header.rpc_vers);
+ assert_eq!(80, request.len() - rem.len());
+ }
+ _ => { assert!(false); }
+ }
}
#[test]
0x79, 0xbe, 0x01, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00,
];
- let mut dcerpcudp_state = DCERPCUDPState::new();
- assert_eq!(80, dcerpcudp_state.process_header(request));
- assert_eq!(
- 0,
- dcerpcudp_state.handle_fragment_data(request, request.len() as u16)
- );
+ match parser::parse_dcerpc_udp_header(request) {
+ Ok((rem, header)) => {
+ assert_eq!(4, header.rpc_vers);
+ assert_eq!(80, request.len() - rem.len());
+ assert_eq!(0, rem.len());
+ }
+ _ => { assert!(false); }
+ }
}
#[test]
assert_eq!(0, dcerpcudp_state.fraglenleft);
assert_eq!(
1392,
- dcerpcudp_state.transactions[0].stub_data_buffer_len_ts
+ dcerpcudp_state.transactions[0].stub_data_buffer_ts.len()
);
}
}