From a306ccfd34e7e2ccb12c41ae13e9dbffcfdcb3f1 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Wed, 12 Jul 2017 10:56:56 +0200 Subject: [PATCH] rust/nfs: implement events Remove lots of panic statements in favor of setting non-fatal events. Bug #2175. --- rules/Makefile.am | 3 +- rules/nfs-events.rules | 8 ++ rust/gen-c-headers.py | 2 + rust/src/nfs/nfs.rs | 185 +++++++++++++++++++++++++++++++++------- src/app-layer-nfs-tcp.c | 44 +++------- src/app-layer-nfs-tcp.h | 2 + src/app-layer-nfs-udp.c | 44 +++------- 7 files changed, 189 insertions(+), 99 deletions(-) create mode 100644 rules/nfs-events.rules diff --git a/rules/Makefile.am b/rules/Makefile.am index daa8d67e5235..435950c05a05 100644 --- a/rules/Makefile.am +++ b/rules/Makefile.am @@ -9,4 +9,5 @@ modbus-events.rules \ app-layer-events.rules \ files.rules \ dnp3-events.rules \ -ntp-events.rules +ntp-events.rules \ +nfs-events.rules diff --git a/rules/nfs-events.rules b/rules/nfs-events.rules new file mode 100644 index 000000000000..33adc3b3436c --- /dev/null +++ b/rules/nfs-events.rules @@ -0,0 +1,8 @@ +# NFS app layer event rules +# +# SID's fall in the 2223000+ range. See https://redmine.openinfosecfoundation.org/projects/suricata/wiki/AppLayer +# +# These sigs fire at most once per connection. +# +alert nfs any any -> any any (msg:"SURICATA NFS malformed request data"; flow:to_server; app-layer-event:nfs.malformed_data; classtype:protocol-command-decode; sid:2223000; rev:1;) +alert nfs any any -> any any (msg:"SURICATA NFS malformed response data"; flow:to_client; app-layer-event:nfs.malformed_data; classtype:protocol-command-decode; sid:2223001; rev:1;) diff --git a/rust/gen-c-headers.py b/rust/gen-c-headers.py index cb46f783dfa9..ba7ce7defd96 100755 --- a/rust/gen-c-headers.py +++ b/rust/gen-c-headers.py @@ -84,7 +84,9 @@ "DetectEngineState": "DetectEngineState", "core::DetectEngineState": "DetectEngineState", "core::AppLayerDecoderEvents": "AppLayerDecoderEvents", + "AppLayerDecoderEvents": "AppLayerDecoderEvents", "core::AppLayerEventType": "AppLayerEventType", + "AppLayerEventType": "AppLayerEventType", "CLuaState": "lua_State", "Store": "Store", } diff --git a/rust/src/nfs/nfs.rs b/rust/src/nfs/nfs.rs index 2489ce8cd573..71956b650ea1 100644 --- a/rust/src/nfs/nfs.rs +++ b/rust/src/nfs/nfs.rs @@ -22,6 +22,7 @@ extern crate libc; use std; use std::mem::transmute; use std::collections::{HashMap}; +use std::ffi::CStr; use nom; use nom::IResult; @@ -91,6 +92,14 @@ pub static mut SURICATA_NFS3_FILE_CONFIG: Option<&'static SuricataFileContext> = * Transaction lookup. */ +#[repr(u32)] +pub enum NFSEvent { + MalformedData = 0, + /* remove 'Padding' when more events are added. Rustc 1.7 won't + * accept a single field enum with repr(u32) */ + Padding, +} + #[derive(Debug)] pub enum NFSTransactionTypeData { RENAME(Vec), @@ -305,6 +314,8 @@ pub struct NFSState { pub nfs_version: u16, + pub events: u16, + /// tx counter for assigning incrementing id's to tx's tx_id: u64, @@ -336,6 +347,7 @@ impl NFSState { tc_gap:false, is_udp:false, nfs_version:0, + events:0, tx_id:0, de_state_count:0, //ts_txs_updated:false, @@ -407,6 +419,18 @@ impl NFSState { return None; } + /// Set an event. The event is set on the most recent transaction. + pub fn set_event(&mut self, event: NFSEvent) { + let len = self.transactions.len(); + if len == 0 { + return; + } + + let mut tx = &mut self.transactions[len - 1]; + sc_app_layer_decoder_events_set_event_raw(&mut tx.events, event as u8); + self.events += 1; + } + // TODO maybe not enough users to justify a func fn mark_response_tx_done(&mut self, xid: u32, rpc_status: u32, nfs_status: u32, resp_handle: &Vec) { @@ -436,7 +460,9 @@ impl NFSState { SCLogDebug!("LOOKUP {:?}", lookup); xidmap.file_name = lookup.name_vec; }, - IResult::Incomplete(_) => { panic!("WEIRD: parse_nfs3_request_lookup said: incomplete"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } @@ -475,7 +501,9 @@ impl NFSState { xidmap.file_handle = ar.handle.value.to_vec(); self.xidmap_handle2name(&mut xidmap); }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if r.procedure == NFSPROC3_GETATTR { @@ -484,7 +512,9 @@ impl NFSState { xidmap.file_handle = gar.handle.value.to_vec(); self.xidmap_handle2name(&mut xidmap); }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if r.procedure == NFSPROC3_READDIRPLUS { @@ -493,7 +523,9 @@ impl NFSState { xidmap.file_handle = rdp.handle.value.to_vec(); self.xidmap_handle2name(&mut xidmap); }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if r.procedure == NFSPROC3_READ { @@ -503,7 +535,9 @@ impl NFSState { xidmap.file_handle = nfs3_read_record.handle.value.to_vec(); self.xidmap_handle2name(&mut xidmap); }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if r.procedure == NFSPROC3_WRITE { @@ -511,7 +545,9 @@ impl NFSState { IResult::Done(_, w) => { self.process_write_record(r, &w); }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, } } else if r.procedure == NFSPROC3_CREATE { @@ -520,7 +556,9 @@ impl NFSState { xidmap.file_handle = nfs3_create_record.handle.value.to_vec(); xidmap.file_name = nfs3_create_record.name_vec; }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; @@ -530,7 +568,9 @@ impl NFSState { xidmap.file_handle = rr.handle.value.to_vec(); xidmap.file_name = rr.name_vec; }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; @@ -541,7 +581,9 @@ impl NFSState { xidmap.file_name = rr.from_name_vec; aux_file_name = rr.to_name_vec; }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if r.procedure == NFSPROC3_MKDIR { @@ -550,7 +592,9 @@ impl NFSState { xidmap.file_handle = mr.handle.value.to_vec(); xidmap.file_name = mr.name_vec; }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if r.procedure == NFSPROC3_RMDIR { @@ -559,7 +603,9 @@ impl NFSState { xidmap.file_handle = rr.handle.value.to_vec(); xidmap.file_name = rr.name_vec; }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if r.procedure == NFSPROC3_COMMIT { @@ -585,7 +631,9 @@ impl NFSState { } //self.ts_txs_updated = true; }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } @@ -663,7 +711,9 @@ impl NFSState { xidmap.file_handle = ar.handle.value.to_vec(); self.xidmap_handle2name(&mut xidmap); }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if r.procedure == NFSPROC3_READ { @@ -673,7 +723,9 @@ impl NFSState { xidmap.file_handle = read_record.handle.value.to_vec(); self.xidmap_handle2name(&mut xidmap); }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } @@ -839,7 +891,7 @@ impl NFSState { } fn process_reply_record_v3<'b>(&mut self, r: &RpcReplyPacket<'b>, xidmap: &mut NFSRequestXidMap) -> u32 { - let nfs_status; + let mut nfs_status = 0; let mut resp_handle = Vec::new(); if xidmap.procedure == NFSPROC3_LOOKUP { @@ -854,7 +906,9 @@ impl NFSState { self.namemap.insert(lookup.handle.value.to_vec(), xidmap.file_name.to_vec()); resp_handle = lookup.handle.value.to_vec(); }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if xidmap.procedure == NFSPROC3_CREATE { @@ -875,7 +929,9 @@ impl NFSState { } }, - IResult::Incomplete(_) => { panic!("WEIRD"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, }; } else if xidmap.procedure == NFSPROC3_READ { @@ -884,7 +940,9 @@ impl NFSState { self.process_read_record(r, reply, Some(&xidmap)); nfs_status = reply.status; }, - IResult::Incomplete(_) => { panic!("Incomplete!"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, } } else if xidmap.procedure == NFSPROC3_READDIRPLUS { @@ -919,11 +977,15 @@ impl NFSState { SCLogDebug!("READDIRPLUS ENTRIES reply {:?}", entries); }, - IResult::Incomplete(_) => { panic!("Incomplete!"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, } }, - IResult::Incomplete(_) => { panic!("Incomplete!"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, } } @@ -948,7 +1010,7 @@ impl NFSState { } fn process_reply_record_v2<'b>(&mut self, r: &RpcReplyPacket<'b>, xidmap: &NFSRequestXidMap) -> u32 { - let nfs_status; + let mut nfs_status = 0; let resp_handle = Vec::new(); if xidmap.procedure == NFSPROC3_READ { @@ -958,7 +1020,9 @@ impl NFSState { self.process_read_record(r, reply, Some(&xidmap)); nfs_status = reply.status; }, - IResult::Incomplete(_) => { panic!("Incomplete!"); }, + IResult::Incomplete(_) => { + self.set_event(NFSEvent::MalformedData); + }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, } } else { @@ -1348,7 +1412,7 @@ impl NFSState { cur_i = remaining; // progress input past parsed record }, IResult::Incomplete(_) => { - SCLogDebug!("TS WRITE record incomplete"); + self.set_event(NFSEvent::MalformedData); }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, } @@ -1375,11 +1439,15 @@ impl NFSState { cur_i = &cur_i[rec_size..]; status |= self.process_request_record(rpc_record); }, - IResult::Incomplete(x) => { - // should be unreachable unless our rec_size calc is off - panic!("TS data incomplete while we checked for rec_size? BUG {:?}", x); - //self.tcp_buffer_ts.extend_from_slice(cur_i); - //break; + IResult::Incomplete(_) => { + cur_i = &cur_i[rec_size..]; // progress input past parsed record + + // we shouldn't get incomplete as we have the full data + // so if we got incomplete anyway it's the data that is + // bad. + self.set_event(NFSEvent::MalformedData); + + status = 1; }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); //break }, @@ -1487,7 +1555,7 @@ impl NFSState { cur_i = remaining; // progress input past parsed record }, IResult::Incomplete(_) => { - SCLogDebug!("TS WRITE record incomplete"); + self.set_event(NFSEvent::MalformedData); }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); }, } @@ -1512,10 +1580,14 @@ impl NFSState { status |= self.process_reply_record(rpc_record); }, IResult::Incomplete(_) => { + cur_i = &cur_i[rec_size..]; // progress input past parsed record + // we shouldn't get incomplete as we have the full data - panic!("TC data incomplete, BUG!"); - //self.tcp_buffer_tc.extend_from_slice(cur_i); - //break; + // so if we got incomplete anyway it's the data that is + // bad. + self.set_event(NFSEvent::MalformedData); + + status = 1; }, IResult::Error(e) => { panic!("Parsing failed: {:?}",e); //break }, @@ -1838,6 +1910,55 @@ pub extern "C" fn rs_nfs3_state_get_tx_detect_state( } } +#[no_mangle] +pub extern "C" fn rs_nfs_state_has_events(state: &mut NFSState) -> u8 { + if state.events > 0 { + return 1; + } + return 0; +} + +#[no_mangle] +pub extern "C" fn rs_nfs_state_get_events(state: &mut NFSState, + tx_id: libc::uint64_t) + -> *mut AppLayerDecoderEvents +{ + match state.get_tx_by_id(tx_id) { + Some(tx) => { + return tx.events; + } + _ => { + return std::ptr::null_mut(); + } + } +} + +#[no_mangle] +pub extern "C" fn rs_nfs_state_get_event_info(event_name: *const libc::c_char, + event_id: *mut libc::c_int, + event_type: *mut AppLayerEventType) + -> i8 +{ + if event_name == std::ptr::null() { + return -1; + } + let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) }; + let event = match c_event_name.to_str() { + Ok(s) => { + match s { + "malformed_data" => NFSEvent::MalformedData as i32, + _ => -1, // unknown event + } + }, + Err(_) => -1, // UTF-8 conversion failed + }; + unsafe{ + *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; + *event_id = event as libc::c_int; + }; + 0 +} + /// return procedure(s) in the tx. At 0 return the main proc, /// otherwise get procs from the 'file_additional_procs'. /// Keep calling until 0 is returned. diff --git a/src/app-layer-nfs-tcp.c b/src/app-layer-nfs-tcp.c index 38623f1a6dff..bdb6545cd762 100644 --- a/src/app-layer-nfs-tcp.c +++ b/src/app-layer-nfs-tcp.c @@ -95,43 +95,21 @@ static void NFSTCPStateTxFree(void *state, uint64_t tx_id) rs_nfs3_state_tx_free(state, tx_id); } -#if 0 static int NFSTCPStateGetEventInfo(const char *event_name, int *event_id, AppLayerEventType *event_type) { - *event_id = SCMapEnumNameToValue(event_name, nfs_decoder_event_table); - if (*event_id == -1) { - SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in " - "nfs enum map table.", event_name); - /* This should be treated as fatal. */ - return -1; - } - - *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; - - return 0; + return rs_nfs_state_get_event_info(event_name, event_id, event_type); } -static AppLayerDecoderEvents *NFSTCPGetEvents(void *state, uint64_t tx_id) +static int NFSTCPHasEvents(void *state) { - NFSTCPState *nfs_state = state; - NFSTCPTransaction *tx; - - TAILQ_FOREACH(tx, &nfs_state->tx_list, next) { - if (tx->tx_id == tx_id) { - return tx->decoder_events; - } - } - - return NULL; + return rs_nfs_state_has_events(state); } -static int NFSTCPHasEvents(void *state) +static AppLayerDecoderEvents *NFSTCPGetEvents(void *state, uint64_t id) { - NFSTCPState *echo = state; - return echo->events; + return rs_nfs_state_get_events(state, id); } -#endif /** * \brief Probe the input to see if it looks like echo. @@ -352,17 +330,17 @@ void RegisterNFSTCPParsers(void) AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_NFS, NFSTCPGetFiles); /* Application layer event handling. */ -// AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_NFS, -// NFSTCPHasEvents); + AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_NFS, + NFSTCPHasEvents); /* What is this being registered for? */ AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_NFS, NULL, NFSTCPGetTxDetectState, NFSTCPSetTxDetectState); -// AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_NFS, -// NFSTCPStateGetEventInfo); -// AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_NFS, -// NFSTCPGetEvents); + AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_NFS, + NFSTCPStateGetEventInfo); + AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_NFS, + NFSTCPGetEvents); /* This parser accepts gaps. */ AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_NFS, diff --git a/src/app-layer-nfs-tcp.h b/src/app-layer-nfs-tcp.h index ad9671f1ef6d..2ffb831b78a7 100644 --- a/src/app-layer-nfs-tcp.h +++ b/src/app-layer-nfs-tcp.h @@ -24,6 +24,8 @@ #ifndef __APP_LAYER_NFS_TCP_H__ #define __APP_LAYER_NFS_TCP_H__ +#include "app-layer-events.h" + void RegisterNFSTCPParsers(void); void NFSTCPParserRegisterTests(void); diff --git a/src/app-layer-nfs-udp.c b/src/app-layer-nfs-udp.c index 65bd41a77c9f..6016610c43c3 100644 --- a/src/app-layer-nfs-udp.c +++ b/src/app-layer-nfs-udp.c @@ -92,43 +92,21 @@ static void NFSStateTxFree(void *state, uint64_t tx_id) rs_nfs3_state_tx_free(state, tx_id); } -#if 0 static int NFSStateGetEventInfo(const char *event_name, int *event_id, AppLayerEventType *event_type) { - *event_id = SCMapEnumNameToValue(event_name, nfs_decoder_event_table); - if (*event_id == -1) { - SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in " - "nfs enum map table.", event_name); - /* This should be treated as fatal. */ - return -1; - } - - *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; - - return 0; + return rs_nfs_state_get_event_info(event_name, event_id, event_type); } -static AppLayerDecoderEvents *NFSGetEvents(void *state, uint64_t tx_id) +static int NFSHasEvents(void *state) { - NFSState *nfs_state = state; - NFSTransaction *tx; - - TAILQ_FOREACH(tx, &nfs3_state->tx_list, next) { - if (tx->tx_id == tx_id) { - return tx->decoder_events; - } - } - - return NULL; + return rs_nfs_state_has_events(state); } -static int NFSHasEvents(void *state) +static AppLayerDecoderEvents *NFSGetEvents(void *state, uint64_t id) { - NFSState *echo = state; - return echo->events; + return rs_nfs_state_get_events(state, id); } -#endif /** * \brief Probe the input to see if it looks like echo. @@ -357,17 +335,17 @@ void RegisterNFSUDPParsers(void) AppLayerParserRegisterGetFilesFunc(IPPROTO_UDP, ALPROTO_NFS, NFSGetFiles); /* Application layer event handling. */ -// AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_NFS, -// NFSHasEvents); + AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_NFS, + NFSHasEvents); /* What is this being registered for? */ AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_NFS, NULL, NFSGetTxDetectState, NFSSetTxDetectState); -// AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_NFS, -// NFSStateGetEventInfo); -// AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_NFS, -// NFSGetEvents); + AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_NFS, + NFSStateGetEventInfo); + AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_NFS, + NFSGetEvents); } else { SCLogNotice("NFS protocol parsing disabled.");