]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
dcerpc: handle gap for TCP streams 5270/head
authorShivani Bhardwaj <shivanib134@gmail.com>
Thu, 25 Jun 2020 20:02:59 +0000 (01:32 +0530)
committerShivani Bhardwaj <shivanib134@gmail.com>
Wed, 5 Aug 2020 07:11:25 +0000 (12:41 +0530)
rust/src/dcerpc/dcerpc.rs
rust/src/dcerpc/log.rs
src/app-layer-dcerpc.c

index d271ab3dd72f05d870438b896bfebe09ab332883..edb2d7e6ae4061aae5226bc60f5d664730ca7612 100644 (file)
  */
 
 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);
index fd13b82a9ec98df62b54e79ec2c29f02ea9f79d8..349dbae1e4b37f062263c8f9c569f1fb2b3d3108 100644 (file)
@@ -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 => {
index e9cffaca44412d4e8871fc2a129a008d0be4bc1f..bb0d1baac929d334e9df9dde9a9451a3db6f03b3 100644 (file)
@@ -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);