From: Victor Julien Date: Sun, 20 Feb 2022 07:59:05 +0000 (+0100) Subject: nfs: break out partial record handling X-Git-Tag: suricata-7.0.0-beta1~872 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bfb5ae867e083f4146b2455309387f77fac5623c;p=thirdparty%2Fsuricata.git nfs: break out partial record handling --- diff --git a/rust/src/nfs/nfs.rs b/rust/src/nfs/nfs.rs index 3706a9d072..f200cdd227 100644 --- a/rust/src/nfs/nfs.rs +++ b/rust/src/nfs/nfs.rs @@ -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