dcerpc: handle gap for TCP streams

pull/5270/head
Shivani Bhardwaj 5 years ago
parent 6cff558663
commit 4c7f55e636

@ -16,13 +16,13 @@
*/
use std::mem::transmute;
use crate::applayer::{AppLayerResult, AppLayerTxData};
use crate::core;
use crate::dcerpc::parser;
use crate::log::*;
use nom::error::ErrorKind;
use nom::number::Endianness;
use nom;
use std::cmp;
// Constant DCERPC UDP Header length
@ -149,6 +149,8 @@ pub struct DCERPCTransaction {
pub stub_data_buffer_reset_tc: bool,
pub req_done: bool,
pub resp_done: bool,
pub req_lost: bool,
pub resp_lost: bool,
pub req_cmd: u8,
pub resp_cmd: u8,
pub tx_data: AppLayerTxData,
@ -174,6 +176,8 @@ impl DCERPCTransaction {
stub_data_buffer_reset_tc: false,
req_done: false,
resp_done: false,
req_lost: false,
resp_lost: false,
req_cmd: DCERPC_TYPE_REQUEST,
resp_cmd: DCERPC_TYPE_RESPONSE,
tx_data: AppLayerTxData::new(),
@ -299,6 +303,10 @@ pub struct DCERPCState {
pub prev_dir: u8,
pub prev_tx_call_id: u32,
pub clear_bind_cache: bool,
pub ts_gap: bool,
pub tc_gap: bool,
pub ts_ssn_gap: bool,
pub tc_ssn_gap: bool,
}
impl DCERPCState {
@ -319,6 +327,10 @@ impl DCERPCState {
prev_dir: core::STREAM_TOSERVER,
prev_tx_call_id: 0,
clear_bind_cache: false,
ts_gap: false,
tc_gap: false,
ts_ssn_gap: false,
tc_ssn_gap: false,
};
}
@ -410,9 +422,11 @@ impl DCERPCState {
match direction {
core::STREAM_TOSERVER => {
self.buffer_ts.clear();
self.ts_gap = false;
}
_ => {
self.buffer_tc.clear();
self.tc_gap = false;
}
}
self.bytes_consumed = 0;
@ -510,6 +524,62 @@ impl DCERPCState {
self.prev_tx_call_id = call_id;
}
pub fn parse_data_gap(&mut self, direction: u8) -> AppLayerResult {
match direction {
core::STREAM_TOSERVER => {
self.ts_gap = true;
self.ts_ssn_gap = true;
},
_ => {
self.tc_gap = true;
self.tc_ssn_gap = true;
},
}
AppLayerResult::ok()
}
pub fn post_gap_housekeeping(&mut self, dir: u8) {
SCLogDebug!("ts ssn gap: {:?}, tc ssn gap: {:?}, dir: {:?}", self.ts_ssn_gap, self.tc_ssn_gap, dir);
if self.ts_ssn_gap && dir == core::STREAM_TOSERVER {
for tx in &mut self.transactions {
if tx.id >= self.tx_id {
SCLogDebug!("post_gap_housekeeping: done");
break;
}
if tx.req_done == false {
tx.req_lost = true;
}
tx.req_done = true;
}
} else if self.tc_ssn_gap && dir == core::STREAM_TOCLIENT {
for tx in &mut self.transactions {
if tx.id >= self.tx_id {
SCLogDebug!("post_gap_housekeeping: done");
break;
}
if tx.req_done == false {
tx.req_lost = true;
}
if tx.resp_done == false {
tx.resp_lost = true;
}
tx.req_done = true;
tx.resp_done = true;
}
}
}
pub fn search_dcerpc_record<'a>(&mut self, i: &'a[u8]) -> nom::IResult<&'a[u8], &'a[u8]> {
let mut d = i;
while d.len() >= 2 {
if d[0] == 0x05 && d[1] == 0x00 {
return Ok((&d[2..], d));
}
d = &d[1..];
}
Err(nom::Err::Incomplete(nom::Needed::Size(2 as usize - d.len())))
}
/// Makes a call to the nom parser for parsing DCERPC Header.
///
/// Arguments:
@ -810,10 +880,49 @@ impl DCERPCState {
pub fn handle_input_data(&mut self, input: &[u8], direction: u8) -> AppLayerResult {
let mut parsed;
let retval;
let input_len = input.len();
let mut cur_i = input;
let input_len = cur_i.len();
let mut v: Vec<u8>;
// Set any query's completion status to false in the beginning
self.query_completed = false;
// Skip the record since this means that its in the middle of a known length record
if self.ts_gap || self.tc_gap {
SCLogDebug!("Trying to catch up after GAP (input {})", cur_i.len());
while cur_i.len() > 0 { // min record size
match self.search_dcerpc_record(cur_i) {
Ok((_, pg)) => {
SCLogDebug!("DCERPC record found");
let offset = cur_i.len() - pg.len();
if offset == 1 {
cur_i = &cur_i[offset + 2..];
continue; // see if we have another record in our data
}
match direction {
core::STREAM_TOSERVER => {
self.ts_gap = false;
break;
},
_ => {
self.tc_gap = false;
break;
}
}
},
_ => {
let mut consumed = cur_i.len();
if consumed < 2 {
consumed = 0;
} else {
consumed = consumed - 1;
}
SCLogDebug!("DCERPC record NOT found");
return AppLayerResult::incomplete(consumed as u32, 2);
},
}
}
}
// Overwrite the dcerpc_state data in case of multiple complete queries in the
// same direction
if self.prev_dir == direction {
@ -827,7 +936,7 @@ impl DCERPCState {
return AppLayerResult::err();
}
v = self.buffer_ts.split_off(0);
v.extend_from_slice(input);
v.extend_from_slice(cur_i);
v.as_slice()
}
_ => {
@ -836,7 +945,7 @@ impl DCERPCState {
return AppLayerResult::err();
}
v = self.buffer_tc.split_off(0);
v.extend_from_slice(input);
v.extend_from_slice(cur_i);
v.as_slice()
}
};
@ -934,7 +1043,7 @@ impl DCERPCState {
self.handle_bind_cache(current_call_id, true);
}
_ => {
SCLogDebug!("Unrecognized packet type");
SCLogDebug!("Unrecognized packet type: {:?}", x);
self.clean_buffer(direction);
return AppLayerResult::err();
}
@ -950,6 +1059,7 @@ impl DCERPCState {
self.clean_buffer(direction);
self.reset_direction(direction);
}
self.post_gap_housekeeping(direction);
self.prev_dir = direction;
return AppLayerResult::ok();
}
@ -978,22 +1088,18 @@ fn evaluate_stub_params(
#[no_mangle]
pub extern "C" fn rs_parse_dcerpc_request_gap(
state: &mut DCERPCState, _input_len: u32,
state: &mut DCERPCState,
_input_len: u32,
) -> AppLayerResult {
if state.handle_gap_ts() == 0 {
return AppLayerResult::ok();
}
AppLayerResult::err()
state.parse_data_gap(core::STREAM_TOSERVER)
}
#[no_mangle]
pub extern "C" fn rs_parse_dcerpc_response_gap(
state: &mut DCERPCState, _input_len: u32,
state: &mut DCERPCState,
_input_len: u32,
) -> AppLayerResult {
if state.handle_gap_tc() == 0 {
return AppLayerResult::ok();
}
AppLayerResult::err()
state.parse_data_gap(core::STREAM_TOCLIENT)
}
#[no_mangle]
@ -1001,6 +1107,11 @@ pub extern "C" fn rs_dcerpc_parse_request(
_flow: *mut core::Flow, state: &mut DCERPCState, _pstate: *mut std::os::raw::c_void,
input: *const u8, input_len: u32, _data: *mut std::os::raw::c_void, flags: u8,
) -> AppLayerResult {
SCLogDebug!("Handling request");
/* START with MIDSTREAM set: record might be starting the middle. */
if flags & (core::STREAM_START|core::STREAM_MIDSTREAM) == (core::STREAM_START|core::STREAM_MIDSTREAM) {
state.ts_gap = true;
}
if input_len > 0 && input != std::ptr::null_mut() {
let buf = build_slice!(input, input_len as usize);
return state.handle_input_data(buf, flags);
@ -1013,6 +1124,10 @@ pub extern "C" fn rs_dcerpc_parse_response(
_flow: *mut core::Flow, state: &mut DCERPCState, _pstate: *mut std::os::raw::c_void,
input: *const u8, input_len: u32, _data: *mut std::os::raw::c_void, flags: u8,
) -> AppLayerResult {
/* START with MIDSTREAM set: record might be starting the middle. */
if flags & (core::STREAM_START|core::STREAM_MIDSTREAM) == (core::STREAM_START|core::STREAM_MIDSTREAM) {
state.tc_gap = true;
}
if input_len > 0 {
if input != std::ptr::null_mut() {
let buf = build_slice!(input, input_len as usize);

@ -22,7 +22,7 @@ use crate::jsonbuilder::{JsonBuilder, JsonError};
fn log_dcerpc_header(
jsb: &mut JsonBuilder, state: &DCERPCState, tx: &DCERPCTransaction,
) -> Result<(), JsonError> {
if tx.req_done == true {
if tx.req_done == true && tx.req_lost == false {
jsb.set_string("request", &dcerpc_type_string(tx.req_cmd))?;
match tx.req_cmd {
DCERPC_TYPE_REQUEST => {
@ -55,7 +55,7 @@ fn log_dcerpc_header(
jsb.set_string("request", "REQUEST_LOST")?;
}
if tx.resp_done == true {
if tx.resp_done == true && tx.resp_lost == false {
jsb.set_string("response", &dcerpc_type_string(tx.resp_cmd))?;
match tx.resp_cmd {
DCERPC_TYPE_RESPONSE => {

@ -180,6 +180,8 @@ void RegisterDCERPCParsers(void)
AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_DCERPC,
DCERPCGetAlstateProgressCompletionStatus);
/* This parser accepts gaps. */
AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_DCERPC, APP_LAYER_PARSER_OPT_ACCEPT_GAPS);
} else {
SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
"still on.", proto_name);

Loading…
Cancel
Save