]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
nfs: break out partial record handling
authorVictor Julien <vjulien@oisf.net>
Sun, 20 Feb 2022 07:59:05 +0000 (08:59 +0100)
committerVictor Julien <vjulien@oisf.net>
Mon, 21 Feb 2022 19:25:23 +0000 (20:25 +0100)
rust/src/nfs/nfs.rs

index 3706a9d0727a4d3210cb71aa07bf6f15d0f02845..f200cdd227200016555b23651ab140e338e024ec 100644 (file)
@@ -983,6 +983,57 @@ impl NFSState {
         return AppLayerResult::ok();
     }
 
+    /// Handle partial records
+    fn parse_tcp_partial_data_ts<'b>(&mut self, base_input: &'b[u8], start_input: &'b[u8],
+            phdr: &RpcRequestPacketPartial, rec_size: usize) -> AppLayerResult {
+        let mut cur_i = start_input;
+        // special case: avoid buffering file write blobs
+        // as these can be large.
+        if rec_size >= 512 && cur_i.len() >= 44 {
+            // large record, likely file xfer
+            SCLogDebug!("large record {}, likely file xfer", rec_size);
+
+            // quick peek, are we in WRITE mode?
+            if phdr.procedure == NFSPROC3_WRITE {
+                SCLogDebug!("CONFIRMED WRITE: large record {}, file chunk xfer", rec_size);
+
+                // lets try to parse the RPC record. Might fail with Incomplete.
+                match parse_rpc(cur_i) {
+                    Ok((remaining, ref hdr)) => {
+                        match parse_nfs3_request_write(hdr.prog_data) {
+                            Ok((_, ref w)) => {
+                                // deal with the partial nfs write data
+                                self.process_partial_write_request_record(hdr, w);
+                                cur_i = remaining; // progress input past parsed record
+                            }
+                            _ => {
+                                self.set_event(NFSEvent::MalformedData);
+                            }
+                        }
+                    }
+                    Err(Err::Incomplete(_)) => {
+                        // we just size checked for the minimal record size above,
+                        // so if options are used (creds/verifier), we can still
+                        // have Incomplete data. Fall through to the buffer code
+                        // and try again on our next iteration.
+                        SCLogDebug!("TS data incomplete");
+                        // fall through to the incomplete below
+                    }
+                    Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
+                        self.set_event(NFSEvent::MalformedData);
+                        SCLogDebug!("Parsing failed: {:?}", _e);
+                        return AppLayerResult::err();
+                    }
+                }
+            }
+        }
+        // make sure we pass a value higher than current input
+        // but lower than the record size
+        let n1 = cmp::max(cur_i.len(), 1024);
+        let n2 = cmp::min(n1, rec_size);
+        return AppLayerResult::incomplete((base_input.len() - cur_i.len()) as u32, n2 as u32);
+    }
+
     /// Parsing function, handling TCP chunks fragmentation
     pub fn parse_tcp_data_ts<'b>(&mut self, i: &'b[u8]) -> AppLayerResult {
         let mut cur_i = i;
@@ -1033,56 +1084,10 @@ impl NFSState {
             match parse_rpc_request_partial(cur_i) {
                 Ok((_, ref rpc_phdr)) => {
                     let rec_size = (rpc_phdr.hdr.frag_len + 4) as usize;
-                    //SCLogDebug!("rec_size {}/{}", rec_size, cur_i.len());
-                    //SCLogDebug!("cur_i {:?}", cur_i);
 
+                    // Handle partial records
                     if rec_size > cur_i.len() {
-                        // special case: avoid buffering file write blobs
-                        // as these can be large.
-                        if rec_size >= 512 && cur_i.len() >= 44 {
-                            // large record, likely file xfer
-                            SCLogDebug!("large record {}, likely file xfer", rec_size);
-
-                            // quick peek, are in WRITE mode?
-                            if rpc_phdr.procedure == NFSPROC3_WRITE {
-                                SCLogDebug!("CONFIRMED WRITE: large record {}, file chunk xfer", rec_size);
-
-                                // lets try to parse the RPC record. Might fail with Incomplete.
-                                match parse_rpc(cur_i) {
-                                    Ok((remaining, ref rpc_record)) => {
-                                        match parse_nfs3_request_write(rpc_record.prog_data) {
-                                            Ok((_, ref nfs_request_write)) => {
-                                                // deal with the partial nfs write data
-                                                self.process_partial_write_request_record(rpc_record, nfs_request_write);
-                                                cur_i = remaining; // progress input past parsed record
-                                            },
-                                            _ => {
-                                                self.set_event(NFSEvent::MalformedData);
-                                            },
-                                        }
-                                    },
-                                    Err(Err::Incomplete(_)) => {
-                                        // we just size checked for the minimal record size above,
-                                        // so if options are used (creds/verifier), we can still
-                                        // have Incomplete data. Fall through to the buffer code
-                                        // and try again on our next iteration.
-                                        SCLogDebug!("TS data incomplete");
-                                        // fall through to the incomplete below
-                                    },
-                                    Err(Err::Error(_e)) |
-                                    Err(Err::Failure(_e)) => {
-                                        self.set_event(NFSEvent::MalformedData);
-                                        SCLogDebug!("Parsing failed: {:?}", _e);
-                                        return AppLayerResult::err();
-                                    },
-                                }
-                            }
-                        }
-                        // make sure we pass a value higher than current input
-                        // but lower than the record size
-                        let n1 = cmp::max(cur_i.len(), 1024);
-                        let n2 = cmp::min(n1, rec_size);
-                        return AppLayerResult::incomplete((i.len() - cur_i.len()) as u32, n2 as u32);
+                        return self.parse_tcp_partial_data_ts(i, cur_i, rpc_phdr, rec_size);
                     }
 
                     // we have the full records size worth of data,
@@ -1137,6 +1142,58 @@ impl NFSState {
         AppLayerResult::ok()
     }
 
+    /// Handle partial records
+    fn parse_tcp_partial_data_tc<'b>(&mut self, base_input: &'b[u8], start_input: &'b[u8],
+            phdr: &RpcPacketHeader, rec_size: usize) -> AppLayerResult {
+        let mut cur_i = start_input;
+        // special case: avoid buffering file read blobs
+        // as these can be large.
+        if rec_size >= 512 && cur_i.len() >= 128 {//36 {
+            // large record, likely file xfer
+            SCLogDebug!("large record {}, likely file xfer", rec_size);
+
+            // quick peek, are in READ mode?
+            if self.peek_reply_record(phdr) == NFSPROC3_READ {
+                SCLogDebug!("CONFIRMED large READ record {}, likely file chunk xfer", rec_size);
+
+                // we should have enough data to parse the RPC record
+                match parse_rpc_reply(cur_i) {
+                    Ok((remaining, ref hdr)) => {
+                        match parse_nfs3_reply_read(hdr.prog_data) {
+                            Ok((_, ref r)) => {
+                                // deal with the partial nfs read data
+                                self.process_partial_read_reply_record(hdr, r);
+                                cur_i = remaining; // progress input past parsed record
+                            }
+                            Err(Err::Incomplete(_)) => {
+                            }
+                            Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
+                                self.set_event(NFSEvent::MalformedData);
+                                SCLogDebug!("Parsing failed: {:?}", _e);
+                                return AppLayerResult::err();
+                            }
+                        }
+                    }
+                    Err(Err::Incomplete(_)) => {
+                        // size check was done for MINIMAL record size,
+                        // so Incomplete is normal.
+                        SCLogDebug!("TC data incomplete");
+                    }
+                    Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
+                        self.set_event(NFSEvent::MalformedData);
+                        SCLogDebug!("Parsing failed: {:?}", _e);
+                        return AppLayerResult::err();
+                    }
+                }
+            }
+        }
+        // make sure we pass a value higher than current input
+        // but lower than the record size
+        let n1 = cmp::max(cur_i.len(), 1024);
+        let n2 = cmp::min(n1, rec_size);
+        return AppLayerResult::incomplete((base_input.len() - cur_i.len()) as u32, n2 as u32);
+    }
+
     /// Parsing function, handling TCP chunks fragmentation
     pub fn parse_tcp_data_tc<'b>(&mut self, i: &'b[u8]) -> AppLayerResult {
         let mut cur_i = i;
@@ -1185,58 +1242,11 @@ impl NFSState {
 
         while cur_i.len() > 0 {
             match parse_rpc_packet_header(cur_i) {
-                Ok((_, ref rpc_hdr)) => {
-                    let rec_size = (rpc_hdr.frag_len + 4) as usize;
+                Ok((_, ref rpc_phdr)) => {
+                    let rec_size = (rpc_phdr.frag_len + 4) as usize;
                     // see if we have all data available
                     if rec_size > cur_i.len() {
-                        // special case: avoid buffering file read blobs
-                        // as these can be large.
-                        if rec_size >= 512 && cur_i.len() >= 128 {//36 {
-                            // large record, likely file xfer
-                            SCLogDebug!("large record {}, likely file xfer", rec_size);
-
-                            // quick peek, are in READ mode?
-                            if self.peek_reply_record(rpc_hdr) == NFSPROC3_READ {
-                                SCLogDebug!("CONFIRMED large READ record {}, likely file chunk xfer", rec_size);
-
-                                // we should have enough data to parse the RPC record
-                                match parse_rpc_reply(cur_i) {
-                                    Ok((remaining, ref rpc_record)) => {
-                                        match parse_nfs3_reply_read(rpc_record.prog_data) {
-                                            Ok((_, ref nfs_reply_read)) => {
-                                                // deal with the partial nfs read data
-                                                self.process_partial_read_reply_record(rpc_record, nfs_reply_read);
-                                                cur_i = remaining; // progress input past parsed record
-                                            },
-                                            Err(Err::Incomplete(_)) => {
-                                            },
-                                            Err(Err::Error(_e)) |
-                                            Err(Err::Failure(_e)) => {
-                                                self.set_event(NFSEvent::MalformedData);
-                                                SCLogDebug!("Parsing failed: {:?}", _e);
-                                                return AppLayerResult::err();
-                                            }
-                                        }
-                                    },
-                                    Err(Err::Incomplete(_)) => {
-                                        // size check was done for MINIMAL record size,
-                                        // so Incomplete is normal.
-                                        SCLogDebug!("TC data incomplete");
-                                    },
-                                    Err(Err::Error(_e)) |
-                                    Err(Err::Failure(_e)) => {
-                                        self.set_event(NFSEvent::MalformedData);
-                                        SCLogDebug!("Parsing failed: {:?}", _e);
-                                        return AppLayerResult::err();
-                                    }
-                                }
-                            }
-                        }
-                        // make sure we pass a value higher than current input
-                        // but lower than the record size
-                        let n1 = cmp::max(cur_i.len(), 1024);
-                        let n2 = cmp::min(n1, rec_size);
-                        return AppLayerResult::incomplete((i.len() - cur_i.len()) as u32, n2 as u32);
+                        return self.parse_tcp_partial_data_tc(i, cur_i, rpc_phdr, rec_size);
                     }
 
                     // we have the full data of the record, lets parse