From 26c02e3f634bc4be64cf13b1d794df7733d4f297 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Sat, 21 Apr 2018 16:05:33 +0200 Subject: [PATCH 01/12] Rust: expose function AppLayerParserRegisterGetTxIterator --- rust/src/parser.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rust/src/parser.rs b/rust/src/parser.rs index 1f9102bf8d44..e304dead2352 100644 --- a/rust/src/parser.rs +++ b/rust/src/parser.rs @@ -21,6 +21,7 @@ use core::{DetectEngineState,Flow,AppLayerEventType,AppLayerDecoderEvents,AppProto}; use filecontainer::FileContainer; +use applayer; use libc::{c_void,c_char,c_int}; use applayer::{AppLayerGetTxIterTuple}; @@ -169,8 +170,16 @@ pub const APP_LAYER_PARSER_NO_REASSEMBLY : u8 = 0b10; pub const APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD : u8 = 0b100; pub const APP_LAYER_PARSER_BYPASS_READY : u8 = 0b1000; +pub type AppLayerGetTxIteratorFn = extern "C" fn (ipproto: u8, + alproto: AppProto, + alstate: *mut c_void, + min_tx_id: u64, + max_tx_id: u64, + istate: &mut u64) -> applayer::AppLayerGetTxIterTuple; + extern { pub fn AppLayerParserStateSetFlag(state: *mut c_void, flag: u8); pub fn AppLayerParserStateIssetFlag(state: *mut c_void, flag: u8) -> c_int; pub fn AppLayerParserConfParserEnabled(ipproto: *const c_char, proto: *const c_char) -> c_int; + pub fn AppLayerParserRegisterGetTxIterator(ipproto: u8, alproto: AppProto, fun: AppLayerGetTxIteratorFn); } From f1c3d2620b4f3bf54a286a64b6ee73f9647a759b Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 8 Aug 2018 15:35:31 +0200 Subject: [PATCH 02/12] Add SNMP (v1/v2c/v3) application layer --- rust/Cargo.toml.in | 1 + rust/gen-c-headers.py | 2 + rust/src/lib.rs | 1 + rust/src/snmp/mod.rs | 22 ++ rust/src/snmp/snmp.rs | 601 +++++++++++++++++++++++++++++++++++ src/Makefile.am | 1 + src/app-layer-detect-proto.c | 4 + src/app-layer-parser.c | 2 + src/app-layer-protos.c | 4 + src/app-layer-protos.h | 1 + src/app-layer-snmp.c | 67 ++++ src/app-layer-snmp.h | 34 ++ suricata.yaml.in | 2 + 13 files changed, 742 insertions(+) create mode 100644 rust/src/snmp/mod.rs create mode 100644 rust/src/snmp/snmp.rs create mode 100644 src/app-layer-snmp.c create mode 100644 src/app-layer-snmp.h diff --git a/rust/Cargo.toml.in b/rust/Cargo.toml.in index 020c039b272f..750f1179eda4 100644 --- a/rust/Cargo.toml.in +++ b/rust/Cargo.toml.in @@ -23,3 +23,4 @@ kerberos-parser = "0.2" ntp-parser = "0.3" ipsec-parser = "0.4" +snmp-parser = "0.3.0" diff --git a/rust/gen-c-headers.py b/rust/gen-c-headers.py index b3eceb5c07b0..0dc29738988f 100755 --- a/rust/gen-c-headers.py +++ b/rust/gen-c-headers.py @@ -86,6 +86,8 @@ "TFTPState": "TFTPState", "SMBState": "SMBState", "SMBTransaction": "SMBTransaction", + "SNMPState": "SNMPState", + "SNMPTransaction": "SNMPTransaction", "IKEV2State": "IKEV2State", "IKEV2Transaction": "IKEV2Transaction", "KRB5State": "KRB5State", diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 46f6a2d35734..d05b2fe0ae14 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -53,6 +53,7 @@ pub mod smb; pub mod krb; pub mod ikev2; +pub mod snmp; pub mod ntp; pub mod tftp; diff --git a/rust/src/snmp/mod.rs b/rust/src/snmp/mod.rs new file mode 100644 index 000000000000..2bdb9b824994 --- /dev/null +++ b/rust/src/snmp/mod.rs @@ -0,0 +1,22 @@ +/* Copyright (C) 2017-2018 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// written by Pierre Chifflier + +extern crate snmp_parser; + +pub mod snmp; diff --git a/rust/src/snmp/snmp.rs b/rust/src/snmp/snmp.rs new file mode 100644 index 000000000000..1c9bb916d008 --- /dev/null +++ b/rust/src/snmp/snmp.rs @@ -0,0 +1,601 @@ +/* Copyright (C) 2017-2018 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// written by Pierre Chifflier + +use snmp::snmp_parser::*; +use core; +use core::{AppProto,Flow,ALPROTO_UNKNOWN,ALPROTO_FAILED,STREAM_TOSERVER,STREAM_TOCLIENT}; +use applayer; +use parser::*; +use libc; +use std; +use std::ffi::{CStr,CString}; +use std::mem::transmute; + +use log::*; + +use der_parser::{DerObjectContent,parse_der_sequence}; +use der_parser::oid::Oid; +use nom; +use nom::{ErrorKind,IResult}; + +#[repr(u32)] +pub enum SNMPEvent { + MalformedData = 0, + UnknownSecurityModel, +} + +pub struct SNMPState { + /// SNMP protocol version + pub version: u32, + + /// List of transactions for this session + transactions: Vec, + + /// tx counter for assigning incrementing id's to tx's + tx_id: u64, +} + +pub struct SNMPPduInfo { + pub pdu_type: PduType, + + pub err: ErrorStatus, + + pub trap_type: Option<(TrapType,Oid,NetworkAddress)>, + + pub vars: Vec, +} + +pub struct SNMPTransaction { + /// PDU info, if present (and cleartext) + pub info: Option, + + /// Community, if present (SNMPv2) + pub community: Option, + + /// USM info, if present (SNMPv3) + pub usm: Option, + + /// True if transaction was encrypted + pub encrypted: bool, + + /// The internal transaction id + id: u64, + + /// The detection engine state, if present + de_state: Option<*mut core::DetectEngineState>, + + /// The events associated with this transaction + events: *mut core::AppLayerDecoderEvents, + + logged: applayer::LoggerFlags, +} + + + +impl SNMPState { + pub fn new() -> SNMPState { + SNMPState{ + version: 0, + transactions: Vec::new(), + tx_id: 0, + } + } +} + +impl Default for SNMPPduInfo { + fn default() -> SNMPPduInfo { + SNMPPduInfo{ + pdu_type: PduType(0), + err: ErrorStatus::NoError, + trap_type: None, + vars: Vec::new() + } + } +} + +impl SNMPState { + fn add_pdu_info(&mut self, pdu: &SnmpPdu, tx: &mut SNMPTransaction) { + let mut pdu_info = SNMPPduInfo::default(); + pdu_info.pdu_type = pdu.pdu_type(); + match pdu { + SnmpPdu::Generic(ref pdu) => { + pdu_info.err = pdu.err; + }, + SnmpPdu::Bulk(_) => { + }, + SnmpPdu::TrapV1(ref t) => { + pdu_info.trap_type = Some((t.generic_trap,t.enterprise.clone(),t.agent_addr.clone())); + } + } + for ref var in pdu.vars_iter() { + pdu_info.vars.push(var.oid.clone()); + } + tx.info = Some(pdu_info); + } + + fn parse_v1_2(&mut self, i: &[u8], _direction: u8) -> i32 { + match parse_snmp_v1(i) { + Ok((_rem,r)) => { + let mut tx = self.new_tx(); + self.add_pdu_info(&r.pdu, &mut tx); + tx.community = Some(r.community.clone()); + self.transactions.push(tx); + 0 + }, + _e => { + SCLogInfo!("parse_snmp_v1 failed: {:?}", _e); + self.set_event(SNMPEvent::MalformedData); + -1 + }, + } + } + + fn parse_v3(&mut self, i: &[u8], _direction: u8) -> i32 { + match parse_snmp_v3(i) { + Ok((_rem,r)) => { + let mut tx = self.new_tx(); + match r.data { + ScopedPduData::Plaintext(pdu) => { + self.add_pdu_info(&pdu.data, &mut tx); + }, + _ => { + tx.encrypted = true; + } + } + match r.security_params { + SecurityParameters::USM(usm) => { + tx.usm = Some(usm.msg_user_name.clone()); + }, + _ => { + self.set_event_tx(&mut tx, SNMPEvent::UnknownSecurityModel); + } + } + self.transactions.push(tx); + 0 + }, + _e => { + SCLogInfo!("parse_snmp_v3 failed: {:?}", _e); + self.set_event(SNMPEvent::MalformedData); + -1 + }, + } + } + + /// Parse an SNMP request message + /// + /// Returns The number of messages parsed, or -1 on error + fn parse(&mut self, i: &[u8], direction: u8) -> i32 { + if self.version == 0 { + match parse_pdu_enveloppe_version(i) { + Ok((_,x)) => self.version = x, + _ => (), + } + } + match self.version { + 1 | 2 => self.parse_v1_2(i, direction), + 3 => self.parse_v3(i, direction), + _ => -1, + } + } + + fn free(&mut self) { + // All transactions are freed when the `transactions` object is freed. + // But let's be explicit + self.transactions.clear(); + } + + fn new_tx(&mut self) -> SNMPTransaction { + self.tx_id += 1; + SNMPTransaction::new(self.tx_id) + } + + fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SNMPTransaction> { + self.transactions.iter().find(|&tx| tx.id == tx_id + 1) + } + + fn free_tx(&mut self, tx_id: u64) { + let tx = self.transactions.iter().position(|ref tx| tx.id == tx_id + 1); + debug_assert!(tx != None); + if let Some(idx) = tx { + let _ = self.transactions.remove(idx); + } + } + + /// Set an event. The event is set on the most recent transaction. + fn set_event(&mut self, event: SNMPEvent) { + if let Some(tx) = self.transactions.last_mut() { + let ev = event as u8; + core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events, ev); + } + } + + /// Set an event on a specific transaction. + fn set_event_tx(&self, tx: &mut SNMPTransaction, event: SNMPEvent) { + core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events, event as u8); + } + + // for use with the C API call StateGetTxIterator + pub fn get_tx_iterator(&mut self, min_tx_id: u64, state: &mut u64) -> + Option<(&SNMPTransaction, u64, bool)> + { + let mut index = *state as usize; + let len = self.transactions.len(); + + // find tx that is >= min_tx_id + while index < len { + let tx = &self.transactions[index]; + if tx.id < min_tx_id + 1 { + index += 1; + continue; + } + *state = index as u64 + 1; + //SCLogDebug!("returning tx_id {} has_next? {} (len {} index {}), tx {:?}", + // tx.id - 1, (len - index) > 1, len, index, tx); + return Some((tx, tx.id - 1, (len - index) > 1)); + } + return None; + } +} + +impl SNMPTransaction { + pub fn new(id: u64) -> SNMPTransaction { + SNMPTransaction { + info: None, + community: None, + usm: None, + encrypted: false, + id: id, + de_state: None, + events: std::ptr::null_mut(), + logged: applayer::LoggerFlags::new(), + } + } + + fn free(&mut self) { + if self.events != std::ptr::null_mut() { + core::sc_app_layer_decoder_events_free_events(&mut self.events); + } + } +} + +impl Drop for SNMPTransaction { + fn drop(&mut self) { + self.free(); + } +} + + + + + + +/// Returns *mut SNMPState +#[no_mangle] +pub extern "C" fn rs_snmp_state_new() -> *mut libc::c_void { + let state = SNMPState::new(); + let boxed = Box::new(state); + return unsafe{std::mem::transmute(boxed)}; +} + +/// Params: +/// - state: *mut SNMPState as void pointer +#[no_mangle] +pub extern "C" fn rs_snmp_state_free(state: *mut libc::c_void) { + // Just unbox... + let mut snmp_state: Box = unsafe{std::mem::transmute(state)}; + snmp_state.free(); +} + +#[no_mangle] +pub extern "C" fn rs_snmp_parse_request(_flow: *const core::Flow, + state: *mut libc::c_void, + _pstate: *mut libc::c_void, + input: *const libc::uint8_t, + input_len: u32, + _data: *const libc::c_void, + _flags: u8) -> i32 { + let buf = build_slice!(input,input_len as usize); + let state = cast_pointer!(state,SNMPState); + state.parse(buf, STREAM_TOSERVER) +} + +#[no_mangle] +pub extern "C" fn rs_snmp_parse_response(_flow: *const core::Flow, + state: *mut libc::c_void, + _pstate: *mut libc::c_void, + input: *const libc::uint8_t, + input_len: u32, + _data: *const libc::c_void, + _flags: u8) -> i32 { + let buf = build_slice!(input,input_len as usize); + let state = cast_pointer!(state,SNMPState); + state.parse(buf, STREAM_TOCLIENT) +} + +#[no_mangle] +pub extern "C" fn rs_snmp_state_get_tx(state: *mut libc::c_void, + tx_id: libc::uint64_t) + -> *mut libc::c_void +{ + let state = cast_pointer!(state,SNMPState); + match state.get_tx_by_id(tx_id) { + Some(tx) => unsafe{std::mem::transmute(tx)}, + None => std::ptr::null_mut(), + } +} + +#[no_mangle] +pub extern "C" fn rs_snmp_state_get_tx_count(state: *mut libc::c_void) + -> libc::uint64_t +{ + let state = cast_pointer!(state,SNMPState); + state.tx_id +} + +#[no_mangle] +pub extern "C" fn rs_snmp_state_tx_free(state: *mut libc::c_void, + tx_id: libc::uint64_t) +{ + let state = cast_pointer!(state,SNMPState); + state.free_tx(tx_id); +} + +#[no_mangle] +pub extern "C" fn rs_snmp_state_progress_completion_status( + _direction: libc::uint8_t) + -> libc::c_int +{ + return 1; +} + +#[no_mangle] +pub extern "C" fn rs_snmp_tx_get_alstate_progress(_tx: *mut libc::c_void, + _direction: libc::uint8_t) + -> libc::c_int +{ + 1 +} + + + + + +#[no_mangle] +pub extern "C" fn rs_snmp_tx_set_logged(_state: *mut libc::c_void, + tx: *mut libc::c_void, + logged: libc::uint32_t) +{ + let tx = cast_pointer!(tx,SNMPTransaction); + tx.logged.set(logged); +} + +#[no_mangle] +pub extern "C" fn rs_snmp_tx_get_logged(_state: *mut libc::c_void, + tx: *mut libc::c_void) + -> u32 +{ + let tx = cast_pointer!(tx,SNMPTransaction); + return tx.logged.get(); +} + + +#[no_mangle] +pub extern "C" fn rs_snmp_state_set_tx_detect_state( + tx: *mut libc::c_void, + de_state: &mut core::DetectEngineState) -> libc::c_int +{ + let tx = cast_pointer!(tx,SNMPTransaction); + tx.de_state = Some(de_state); + 0 +} + +#[no_mangle] +pub extern "C" fn rs_snmp_state_get_tx_detect_state( + tx: *mut libc::c_void) + -> *mut core::DetectEngineState +{ + let tx = cast_pointer!(tx,SNMPTransaction); + match tx.de_state { + Some(ds) => ds, + None => std::ptr::null_mut(), + } +} + + +#[no_mangle] +pub extern "C" fn rs_snmp_state_get_events(state: *mut libc::c_void, + tx_id: libc::uint64_t) + -> *mut core::AppLayerDecoderEvents +{ + let state = cast_pointer!(state,SNMPState); + match state.get_tx_by_id(tx_id) { + Some(tx) => tx.events, + _ => std::ptr::null_mut(), + } +} + +#[no_mangle] +pub extern "C" fn rs_snmp_state_get_event_info(event_name: *const libc::c_char, + event_id: *mut libc::c_int, + event_type: *mut core::AppLayerEventType) + -> libc::c_int +{ + 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" => SNMPEvent::MalformedData as i32, + "unknown_security_model" => SNMPEvent::UnknownSecurityModel as i32, + _ => -1, // unknown event + } + }, + Err(_) => -1, // UTF-8 conversion failed + }; + unsafe{ + *event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION; + *event_id = event as libc::c_int; + }; + 0 +} + +// for use with the C API call StateGetTxIterator +#[no_mangle] +pub extern "C" fn rs_snmp_state_get_tx_iterator( + state: &mut SNMPState, + min_tx_id: libc::uint64_t, + istate: &mut libc::uint64_t) + -> applayer::AppLayerGetTxIterTuple +{ + match state.get_tx_iterator(min_tx_id, istate) { + Some((tx, out_tx_id, has_next)) => { + let c_tx = unsafe { transmute(tx) }; + let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next); + return ires; + } + None => { + return applayer::AppLayerGetTxIterTuple::not_found(); + } + } +} + +// for use with the C API call StateGetTxIterator +#[no_mangle] +pub extern "C" fn rs_snmp_get_tx_iterator(_ipproto: u8, + _alproto: AppProto, + alstate: *mut libc::c_void, + min_tx_id: u64, + _max_tx_id: u64, + istate: &mut u64) -> applayer::AppLayerGetTxIterTuple +{ + let state = cast_pointer!(alstate,SNMPState); + match state.get_tx_iterator(min_tx_id, istate) { + Some((tx, out_tx_id, has_next)) => { + let c_tx = unsafe { transmute(tx) }; + let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next); + return ires; + } + None => { + return applayer::AppLayerGetTxIterTuple::not_found(); + } + } +} + + + +static mut ALPROTO_SNMP : AppProto = ALPROTO_UNKNOWN; + +// Read PDU sequence and extract version, if similar to SNMP definition +fn parse_pdu_enveloppe_version(i:&[u8]) -> IResult<&[u8],u32> { + match parse_der_sequence(i) { + Ok((_,x)) => { + match x.content { + DerObjectContent::Sequence(ref v) => { + if v.len() == 3 { + match v[0].as_u32() { + Ok(0) => { return Ok((i,1)); }, // possibly SNMPv1 + Ok(1) => { return Ok((i,2)); }, // possibly SNMPv2c + _ => () + } + } else if v.len() == 4 && v[0].as_u32() == Ok(3) { + return Ok((i,3)); // possibly SNMPv3 + } + }, + _ => () + }; + Err(nom::Err::Error(error_position!(i, ErrorKind::Verify))) + }, + Err(nom::Err::Incomplete(i)) => Err(nom::Err::Incomplete(i)), + Err(nom::Err::Failure(_)) | + Err(nom::Err::Error(_)) => Err(nom::Err::Error(error_position!(i,ErrorKind::Verify))) + } +} + +#[no_mangle] +pub extern "C" fn rs_snmp_probing_parser(_flow: *const Flow, + _direction: u8, + input:*const libc::uint8_t, + input_len: u32, + _rdir: *mut u8) -> AppProto { + let slice = build_slice!(input,input_len as usize); + let alproto = unsafe{ ALPROTO_SNMP }; + if slice.len() < 4 { return unsafe{ALPROTO_FAILED}; } + match parse_pdu_enveloppe_version(slice) { + Ok((_,_)) => alproto, + Err(nom::Err::Incomplete(_)) => ALPROTO_UNKNOWN, + _ => unsafe{ALPROTO_FAILED}, + } +} + +const PARSER_NAME : &'static [u8] = b"snmp\0"; + +#[no_mangle] +pub unsafe extern "C" fn rs_register_snmp_parser() { + let default_port = CString::new("161").unwrap(); + let mut parser = RustParser { + name : PARSER_NAME.as_ptr() as *const libc::c_char, + default_port : default_port.as_ptr(), + ipproto : libc::IPPROTO_UDP, + probe_ts : rs_snmp_probing_parser, + probe_tc : rs_snmp_probing_parser, + min_depth : 0, + max_depth : 16, + state_new : rs_snmp_state_new, + state_free : rs_snmp_state_free, + tx_free : rs_snmp_state_tx_free, + parse_ts : rs_snmp_parse_request, + parse_tc : rs_snmp_parse_response, + get_tx_count : rs_snmp_state_get_tx_count, + get_tx : rs_snmp_state_get_tx, + tx_get_comp_st : rs_snmp_state_progress_completion_status, + tx_get_progress : rs_snmp_tx_get_alstate_progress, + get_tx_logged : Some(rs_snmp_tx_get_logged), + set_tx_logged : Some(rs_snmp_tx_set_logged), + get_de_state : rs_snmp_state_get_tx_detect_state, + set_de_state : rs_snmp_state_set_tx_detect_state, + get_events : Some(rs_snmp_state_get_events), + get_eventinfo : Some(rs_snmp_state_get_event_info), + localstorage_new : None, + localstorage_free : None, + get_tx_mpm_id : None, + set_tx_mpm_id : None, + get_files : None, + get_tx_iterator : None, + }; + let ip_proto_str = CString::new("udp").unwrap(); + if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + // port 161 + let alproto = AppLayerRegisterProtocolDetection(&parser, 1); + // store the allocated ID for the probe function + ALPROTO_SNMP = alproto; + if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let _ = AppLayerRegisterParser(&parser, alproto); + } + AppLayerParserRegisterGetTxIterator(libc::IPPROTO_UDP as u8, alproto, rs_snmp_get_tx_iterator); + // port 162 + let default_port_traps = CString::new("162").unwrap(); + parser.default_port = default_port_traps.as_ptr(); + let _ = AppLayerRegisterProtocolDetection(&parser, 1); + if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let _ = AppLayerRegisterParser(&parser, alproto); + } + } else { + SCLogDebug!("Protocol detecter and parser disabled for SNMP."); + } +} diff --git a/src/Makefile.am b/src/Makefile.am index 23f8c1371866..9f0f489aacf7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -40,6 +40,7 @@ app-layer-parser.c app-layer-parser.h \ app-layer-protos.c app-layer-protos.h \ app-layer-smb.c app-layer-smb.h \ app-layer-smtp.c app-layer-smtp.h \ +app-layer-snmp.c app-layer-snmp.h \ app-layer-nfs-tcp.c app-layer-nfs-tcp.h \ app-layer-nfs-udp.c app-layer-nfs-udp.h \ app-layer-ntp.c app-layer-ntp.h \ diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 780b38610508..d8ae2067130f 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -865,6 +865,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar printf(" alproto: ALPROTO_KRB5\n"); else if (pp_pe->alproto == ALPROTO_DHCP) printf(" alproto: ALPROTO_DHCP\n"); + else if (pp_pe->alproto == ALPROTO_SNMP) + printf(" alproto: ALPROTO_SNMP\n"); else if (pp_pe->alproto == ALPROTO_TEMPLATE_RUST) printf(" alproto: ALPROTO_TEMPLATE_RUST\n"); else if (pp_pe->alproto == ALPROTO_TEMPLATE) @@ -936,6 +938,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar printf(" alproto: ALPROTO_KRB5\n"); else if (pp_pe->alproto == ALPROTO_DHCP) printf(" alproto: ALPROTO_DHCP\n"); + else if (pp_pe->alproto == ALPROTO_SNMP) + printf(" alproto: ALPROTO_SNMP\n"); else if (pp_pe->alproto == ALPROTO_TEMPLATE_RUST) printf(" alproto: ALPROTO_TEMPLATE_RUST\n"); else if (pp_pe->alproto == ALPROTO_TEMPLATE) diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 858b56d46a90..586a9f71e61f 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -66,6 +66,7 @@ #include "app-layer-ikev2.h" #include "app-layer-krb5.h" #include "app-layer-dhcp.h" +#include "app-layer-snmp.h" #include "app-layer-template.h" #include "app-layer-template-rust.h" @@ -1499,6 +1500,7 @@ void AppLayerParserRegisterProtocolParsers(void) RegisterIKEV2Parsers(); RegisterKRB5Parsers(); RegisterDHCPParsers(); + RegisterSNMPParsers(); RegisterTemplateRustParsers(); RegisterTemplateParsers(); diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index 4ad78b73f450..04120d34540a 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -99,6 +99,9 @@ const char *AppProtoToString(AppProto alproto) case ALPROTO_DHCP: proto_name = "dhcp"; break; + case ALPROTO_SNMP: + proto_name = "snmp"; + break; case ALPROTO_TEMPLATE: proto_name = "template"; break; @@ -143,6 +146,7 @@ AppProto StringToAppProto(const char *proto_name) if (strcmp(proto_name,"ikev2")==0) return ALPROTO_IKEV2; if (strcmp(proto_name,"krb5")==0) return ALPROTO_KRB5; if (strcmp(proto_name,"dhcp")==0) return ALPROTO_DHCP; + if (strcmp(proto_name,"snmp")==0) return ALPROTO_SNMP; if (strcmp(proto_name,"template")==0) return ALPROTO_TEMPLATE; if (strcmp(proto_name,"template-rust")==0) return ALPROTO_TEMPLATE_RUST; if (strcmp(proto_name,"failed")==0) return ALPROTO_FAILED; diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index c258935b791e..ffd0bd9251f5 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -50,6 +50,7 @@ enum AppProtoEnum { ALPROTO_IKEV2, ALPROTO_KRB5, ALPROTO_DHCP, + ALPROTO_SNMP, ALPROTO_TEMPLATE, ALPROTO_TEMPLATE_RUST, diff --git a/src/app-layer-snmp.c b/src/app-layer-snmp.c new file mode 100644 index 000000000000..e363fabb27da --- /dev/null +++ b/src/app-layer-snmp.c @@ -0,0 +1,67 @@ +/* Copyright (C) 2015 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + * Parser for SNMP v2c/v3 application layer running on UDP port 161. + * + */ + +#include "suricata-common.h" +#include "stream.h" +#include "conf.h" + +#include "util-unittest.h" + +#include "app-layer-detect-proto.h" +#include "app-layer-parser.h" + +#include "app-layer-snmp.h" + +#ifdef HAVE_RUST + +#include "rust-snmp-snmp-gen.h" + +void RegisterSNMPParsers(void) +{ + rs_register_snmp_parser(); + +#ifdef UNITTESTS + AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_SNMP, + SNMPParserRegisterTests); +#endif +} + +#ifdef UNITTESTS +#endif + +void SNMPParserRegisterTests(void) +{ +#ifdef UNITTESTS +#endif +} + +#else /* HAVE_RUST */ + +void RegisterSNMPParsers(void) +{ +} + +#endif /* HAVE_RUST */ diff --git a/src/app-layer-snmp.h b/src/app-layer-snmp.h new file mode 100644 index 000000000000..f92371fc32e8 --- /dev/null +++ b/src/app-layer-snmp.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2017-2018 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Pierre Chifflier + */ + +#ifndef __APP_LAYER_SNMP_H__ +#define __APP_LAYER_SNMP_H__ + +void RegisterSNMPParsers(void); +void SNMPParserRegisterTests(void); + +/** Opaque Rust types. */ +typedef struct SNMPState_ SNMPState; +typedef struct SNMPTransaction_ SNMPTransaction; + +#endif /* __APP_LAYER_SNMP_H__ */ diff --git a/suricata.yaml.in b/suricata.yaml.in index 1c3863575c98..b4f2d91c38c0 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -759,6 +759,8 @@ app-layer: protocols: krb5: enabled: yes + snmp: + enabled: yes ikev2: enabled: yes tls: From 75319e93ae1250110504fe967a3c75bf429b41c5 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 8 Oct 2018 10:36:39 +0200 Subject: [PATCH 03/12] SNMP: add logger --- rust/src/snmp/log.rs | 68 ++++++++++++++ rust/src/snmp/mod.rs | 1 + src/Makefile.am | 1 + src/output-json-snmp.c | 198 +++++++++++++++++++++++++++++++++++++++++ src/output-json-snmp.h | 29 ++++++ src/output.c | 3 + src/suricata-common.h | 1 + suricata.yaml.in | 1 + 8 files changed, 302 insertions(+) create mode 100644 rust/src/snmp/log.rs create mode 100644 src/output-json-snmp.c create mode 100644 src/output-json-snmp.h diff --git a/rust/src/snmp/log.rs b/rust/src/snmp/log.rs new file mode 100644 index 000000000000..3889c9d38eec --- /dev/null +++ b/rust/src/snmp/log.rs @@ -0,0 +1,68 @@ +/* Copyright (C) 2018 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// written by Pierre Chifflier + +use json::*; +use snmp::snmp::{SNMPState,SNMPTransaction}; +use snmp::snmp_parser::NetworkAddress; + +#[no_mangle] +pub extern "C" fn rs_snmp_log_json_response(state: &mut SNMPState, tx: &mut SNMPTransaction) -> *mut JsonT +{ + let js = Json::object(); + js.set_integer("version", state.version as u64); + if tx.encrypted { + js.set_string("pdu_type", "encrypted"); + } else { + match tx.info { + Some(ref info) => { + js.set_string("pdu_type", &format!("{:?}", info.pdu_type)); + if info.err.0 != 0 { + js.set_string("error", &format!("{:?}", info.err)); + } + match &info.trap_type { + Some((trap_type, oid, address)) => { + js.set_string("trap_type", &format!("{:?}", trap_type)); + js.set_string("trap_oid", &oid.to_string()); + match address { + NetworkAddress::IPv4(ip) => js.set_string("trap_address", &ip.to_string()) + } + }, + _ => () + } + if info.vars.len() > 0 { + let jsa = Json::array(); + for var in info.vars.iter() { + jsa.array_append_string(&var.to_string()); + } + js.set("vars", jsa); + } + }, + _ => () + } + match tx.community { + Some(ref c) => js.set_string("community", c), + _ => () + } + match tx.usm { + Some(ref s) => js.set_string("usm", s), + _ => () + } + } + js.unwrap() +} diff --git a/rust/src/snmp/mod.rs b/rust/src/snmp/mod.rs index 2bdb9b824994..e0bca246539c 100644 --- a/rust/src/snmp/mod.rs +++ b/rust/src/snmp/mod.rs @@ -20,3 +20,4 @@ extern crate snmp_parser; pub mod snmp; +pub mod log; diff --git a/src/Makefile.am b/src/Makefile.am index 9f0f489aacf7..c80b71f229f4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -323,6 +323,7 @@ output-json-smb.c output-json-smb.h \ output-json-ikev2.c output-json-ikev2.h \ output-json-krb5.c output-json-krb5.h \ output-json-dhcp.c output-json-dhcp.h \ +output-json-snmp.c output-json-snmp.h \ output-json-template.c output-json-template.h \ output-json-template-rust.c output-json-template-rust.h \ output-json-metadata.c output-json-metadata.h \ diff --git a/src/output-json-snmp.c b/src/output-json-snmp.c new file mode 100644 index 000000000000..20f47a1c05d5 --- /dev/null +++ b/src/output-json-snmp.c @@ -0,0 +1,198 @@ +/* Copyright (C) 2018 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + * Implement JSON/eve logging app-layer SNMP. + */ + +#include "suricata-common.h" +#include "debug.h" +#include "detect.h" +#include "pkt-var.h" +#include "conf.h" + +#include "threads.h" +#include "threadvars.h" +#include "tm-threads.h" + +#include "util-unittest.h" +#include "util-buffer.h" +#include "util-debug.h" +#include "util-byte.h" + +#include "output.h" +#include "output-json.h" + +#include "app-layer.h" +#include "app-layer-parser.h" + +#include "app-layer-snmp.h" +#include "output-json-snmp.h" + +#ifdef HAVE_RUST +#ifdef HAVE_LIBJANSSON + +#include "rust.h" +#include "rust-snmp-log-gen.h" + +typedef struct LogSNMPFileCtx_ { + LogFileCtx *file_ctx; + OutputJsonCommonSettings cfg; +} LogSNMPFileCtx; + +typedef struct LogSNMPLogThread_ { + LogSNMPFileCtx *snmplog_ctx; + MemBuffer *buffer; +} LogSNMPLogThread; + +static int JsonSNMPLogger(ThreadVars *tv, void *thread_data, + const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) +{ + SNMPTransaction *snmptx = tx; + LogSNMPLogThread *thread = thread_data; + json_t *js, *snmpjs; + + js = CreateJSONHeader(p, LOG_DIR_PACKET, "snmp"); + if (unlikely(js == NULL)) { + return TM_ECODE_FAILED; + } + + JsonAddCommonOptions(&thread->snmplog_ctx->cfg, p, f, js); + + snmpjs = rs_snmp_log_json_response(state, snmptx); + if (unlikely(snmpjs == NULL)) { + goto error; + } + json_object_set_new(js, "snmp", snmpjs); + + MemBufferReset(thread->buffer); + OutputJSONBuffer(js, thread->snmplog_ctx->file_ctx, &thread->buffer); + + json_decref(js); + return TM_ECODE_OK; + +error: + json_decref(js); + return TM_ECODE_FAILED; +} + +static void OutputSNMPLogDeInitCtxSub(OutputCtx *output_ctx) +{ + LogSNMPFileCtx *snmplog_ctx = (LogSNMPFileCtx *)output_ctx->data; + SCFree(snmplog_ctx); + SCFree(output_ctx); +} + +static OutputInitResult OutputSNMPLogInitSub(ConfNode *conf, + OutputCtx *parent_ctx) +{ + OutputInitResult result = { NULL, false }; + OutputJsonCtx *ajt = parent_ctx->data; + + LogSNMPFileCtx *snmplog_ctx = SCCalloc(1, sizeof(*snmplog_ctx)); + if (unlikely(snmplog_ctx == NULL)) { + return result; + } + snmplog_ctx->file_ctx = ajt->file_ctx; + snmplog_ctx->cfg = ajt->cfg; + + OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx)); + if (unlikely(output_ctx == NULL)) { + SCFree(snmplog_ctx); + return result; + } + output_ctx->data = snmplog_ctx; + output_ctx->DeInit = OutputSNMPLogDeInitCtxSub; + + SCLogDebug("SNMP log sub-module initialized."); + + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_SNMP); + + result.ctx = output_ctx; + result.ok = true; + return result; +} + +#define OUTPUT_BUFFER_SIZE 65535 + +static TmEcode JsonSNMPLogThreadInit(ThreadVars *t, const void *initdata, void **data) +{ + LogSNMPLogThread *thread = SCCalloc(1, sizeof(*thread)); + if (unlikely(thread == NULL)) { + return TM_ECODE_FAILED; + } + + if (initdata == NULL) { + SCLogDebug("Error getting context for EveLogSNMP. \"initdata\" is NULL."); + SCFree(thread); + return TM_ECODE_FAILED; + } + + thread->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE); + if (unlikely(thread->buffer == NULL)) { + SCFree(thread); + return TM_ECODE_FAILED; + } + + thread->snmplog_ctx = ((OutputCtx *)initdata)->data; + *data = (void *)thread; + + return TM_ECODE_OK; +} + +static TmEcode JsonSNMPLogThreadDeinit(ThreadVars *t, void *data) +{ + LogSNMPLogThread *thread = (LogSNMPLogThread *)data; + if (thread == NULL) { + return TM_ECODE_OK; + } + if (thread->buffer != NULL) { + MemBufferFree(thread->buffer); + } + SCFree(thread); + return TM_ECODE_OK; +} + +void JsonSNMPLogRegister(void) +{ + /* Register as an eve sub-module. */ + OutputRegisterTxSubModule(LOGGER_JSON_SNMP, "eve-log", "JsonSNMPLog", + "eve-log.snmp", OutputSNMPLogInitSub, ALPROTO_SNMP, + JsonSNMPLogger, JsonSNMPLogThreadInit, + JsonSNMPLogThreadDeinit, NULL); + + SCLogDebug("SNMP JSON logger registered."); +} + +#else /* No JSON support. */ + +void JsonSNMPLogRegister(void) +{ +} + +#endif /* HAVE_LIBJANSSON */ +#else /* No rust support. */ + +void JsonSNMPLogRegister(void) +{ +} + +#endif /* HAVE_RUST */ diff --git a/src/output-json-snmp.h b/src/output-json-snmp.h new file mode 100644 index 000000000000..7eb5a280021d --- /dev/null +++ b/src/output-json-snmp.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2015 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Pierre Chifflier + */ + +#ifndef __OUTPUT_JSON_SNMP_H__ +#define __OUTPUT_JSON_SNMP_H__ + +void JsonSNMPLogRegister(void); + +#endif /* __OUTPUT_JSON_SNMP_H__ */ diff --git a/src/output.c b/src/output.c index 576183b35c6b..154319ac10ef 100644 --- a/src/output.c +++ b/src/output.c @@ -73,6 +73,7 @@ #include "output-json-ikev2.h" #include "output-json-krb5.h" #include "output-json-dhcp.h" +#include "output-json-snmp.h" #include "output-json-template.h" #include "output-json-template-rust.h" #include "output-lua.h" @@ -1103,6 +1104,8 @@ void OutputRegisterLoggers(void) JsonKRB5LogRegister(); /* DHCP JSON logger. */ JsonDHCPLogRegister(); + /* SNMP JSON logger. */ + JsonSNMPLogRegister(); /* Template JSON logger. */ JsonTemplateLogRegister(); /* Template Rust JSON logger. */ diff --git a/src/suricata-common.h b/src/suricata-common.h index 788e6037c246..dc05e19d0ae7 100644 --- a/src/suricata-common.h +++ b/src/suricata-common.h @@ -438,6 +438,7 @@ typedef enum { LOGGER_JSON_IKEV2, LOGGER_JSON_KRB5, LOGGER_JSON_DHCP, + LOGGER_JSON_SNMP, LOGGER_JSON_TEMPLATE_RUST, LOGGER_JSON_TEMPLATE, diff --git a/suricata.yaml.in b/suricata.yaml.in index b4f2d91c38c0..f88d73ae9c1e 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -236,6 +236,7 @@ outputs: - tftp - ikev2 - krb5 + - snmp - dhcp: enabled: yes # When extended mode is on, all DHCP messages are logged From 1db71d55b7705b5c91a5301a2f06f3bddf9d638b Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 8 Oct 2018 10:37:01 +0200 Subject: [PATCH 04/12] SNMP: start looking for transactions from end of list --- rust/src/snmp/snmp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/snmp/snmp.rs b/rust/src/snmp/snmp.rs index 1c9bb916d008..519feb3193ae 100644 --- a/rust/src/snmp/snmp.rs +++ b/rust/src/snmp/snmp.rs @@ -206,7 +206,7 @@ impl SNMPState { } fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SNMPTransaction> { - self.transactions.iter().find(|&tx| tx.id == tx_id + 1) + self.transactions.iter().rev().find(|&tx| tx.id == tx_id + 1) } fn free_tx(&mut self, tx_id: u64) { From 86f5c57c7ac963e06316a92e61ced79f671bf413 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Tue, 4 Dec 2018 17:40:28 +0100 Subject: [PATCH 05/12] SNMP: use explicit references to support build with old rust compiler --- rust/src/snmp/log.rs | 4 ++-- rust/src/snmp/snmp.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/src/snmp/log.rs b/rust/src/snmp/log.rs index 3889c9d38eec..c3e4dd322c19 100644 --- a/rust/src/snmp/log.rs +++ b/rust/src/snmp/log.rs @@ -35,8 +35,8 @@ pub extern "C" fn rs_snmp_log_json_response(state: &mut SNMPState, tx: &mut SNMP if info.err.0 != 0 { js.set_string("error", &format!("{:?}", info.err)); } - match &info.trap_type { - Some((trap_type, oid, address)) => { + match info.trap_type { + Some((trap_type, ref oid, address)) => { js.set_string("trap_type", &format!("{:?}", trap_type)); js.set_string("trap_oid", &oid.to_string()); match address { diff --git a/rust/src/snmp/snmp.rs b/rust/src/snmp/snmp.rs index 519feb3193ae..420b1b37f70a 100644 --- a/rust/src/snmp/snmp.rs +++ b/rust/src/snmp/snmp.rs @@ -113,7 +113,7 @@ impl SNMPState { fn add_pdu_info(&mut self, pdu: &SnmpPdu, tx: &mut SNMPTransaction) { let mut pdu_info = SNMPPduInfo::default(); pdu_info.pdu_type = pdu.pdu_type(); - match pdu { + match *pdu { SnmpPdu::Generic(ref pdu) => { pdu_info.err = pdu.err; }, From cd0ffbeadee44eef536dbcb199e964307bf1cf4e Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 5 Dec 2018 09:46:16 +0100 Subject: [PATCH 06/12] SNMP: add missing case for profiling enum --- src/util-profiling.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util-profiling.c b/src/util-profiling.c index 91a6e6bd11f0..7262b3a5b145 100644 --- a/src/util-profiling.c +++ b/src/util-profiling.c @@ -1315,6 +1315,7 @@ const char * PacketProfileLoggertIdToString(LoggerId id) CASE_CODE (LOGGER_JSON_IKEV2); CASE_CODE (LOGGER_JSON_TFTP); CASE_CODE (LOGGER_JSON_SMTP); + CASE_CODE (LOGGER_JSON_SNMP); CASE_CODE (LOGGER_JSON_TLS); CASE_CODE (LOGGER_JSON_TEMPLATE_RUST); CASE_CODE (LOGGER_JSON_TEMPLATE); From f9fc84c1a9b1b288671a78d11642aa886fe03895 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 10 Dec 2018 13:48:00 +0100 Subject: [PATCH 07/12] SNMP: add the "snmp_version" detection keyword --- doc/userguide/rules/index.rst | 1 + doc/userguide/rules/snmp-keywords.rst | 22 ++ rust/src/snmp/detect.rs | 32 +++ rust/src/snmp/mod.rs | 1 + rust/src/snmp/snmp.rs | 8 +- src/Makefile.am | 1 + src/detect-engine-register.c | 2 + src/detect-engine-register.h | 1 + src/detect-snmp-version.c | 364 ++++++++++++++++++++++++++ src/detect-snmp-version.h | 31 +++ 10 files changed, 461 insertions(+), 2 deletions(-) create mode 100644 doc/userguide/rules/snmp-keywords.rst create mode 100644 rust/src/snmp/detect.rs create mode 100644 src/detect-snmp-version.c create mode 100644 src/detect-snmp-version.h diff --git a/doc/userguide/rules/index.rst b/doc/userguide/rules/index.rst index 391d410f5351..c81440ef94cd 100644 --- a/doc/userguide/rules/index.rst +++ b/doc/userguide/rules/index.rst @@ -22,6 +22,7 @@ Suricata Rules enip-keyword ftp-keywords kerberos-keywords + snmp-keywords app-layer xbits thresholding diff --git a/doc/userguide/rules/snmp-keywords.rst b/doc/userguide/rules/snmp-keywords.rst new file mode 100644 index 000000000000..fd81203f22ef --- /dev/null +++ b/doc/userguide/rules/snmp-keywords.rst @@ -0,0 +1,22 @@ +SNMP keywords +============= + +snmp_version +------------ + +SNMP protocol version (integer). Expected values are 1, 2 (for version 2c) or 3. + +Syntax:: + + snmp_version:[op] + +The version can be matched exactly, or compared using the _op_ setting:: + + snmp_version:3 # exactly 3 + snmp_version:<3 # smaller than 3 + snmp_version:>=2 # greater or equal than 2 + +Signature example:: + + alert snmp any any -> any any (msg:"old SNMP version (<3)"; snmp_version:<3; sid:1; rev:1;) + diff --git a/rust/src/snmp/detect.rs b/rust/src/snmp/detect.rs new file mode 100644 index 000000000000..d23e986b0fe4 --- /dev/null +++ b/rust/src/snmp/detect.rs @@ -0,0 +1,32 @@ +/* Copyright (C) 2017-2018 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// written by Pierre Chifflier + +use libc; +use snmp::snmp::SNMPTransaction; + +#[no_mangle] +pub extern "C" fn rs_snmp_tx_get_version(tx: &mut SNMPTransaction, + version: *mut libc::uint32_t) +{ + debug_assert!(tx.version != 0, "SNMP version is 0"); + unsafe { + *version = tx.version as libc::uint32_t; + } +} + diff --git a/rust/src/snmp/mod.rs b/rust/src/snmp/mod.rs index e0bca246539c..93158f56baaf 100644 --- a/rust/src/snmp/mod.rs +++ b/rust/src/snmp/mod.rs @@ -21,3 +21,4 @@ extern crate snmp_parser; pub mod snmp; pub mod log; +pub mod detect; diff --git a/rust/src/snmp/snmp.rs b/rust/src/snmp/snmp.rs index 420b1b37f70a..6cc1503705bc 100644 --- a/rust/src/snmp/snmp.rs +++ b/rust/src/snmp/snmp.rs @@ -62,6 +62,9 @@ pub struct SNMPPduInfo { } pub struct SNMPTransaction { + /// PDU version + pub version: u32, + /// PDU info, if present (and cleartext) pub info: Option, @@ -202,7 +205,7 @@ impl SNMPState { fn new_tx(&mut self) -> SNMPTransaction { self.tx_id += 1; - SNMPTransaction::new(self.tx_id) + SNMPTransaction::new(self.version, self.tx_id) } fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SNMPTransaction> { @@ -254,8 +257,9 @@ impl SNMPState { } impl SNMPTransaction { - pub fn new(id: u64) -> SNMPTransaction { + pub fn new(version: u32, id: u64) -> SNMPTransaction { SNMPTransaction { + version, info: None, community: None, usm: None, diff --git a/src/Makefile.am b/src/Makefile.am index c80b71f229f4..fc0322f7ac98 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -233,6 +233,7 @@ detect-rpc.c detect-rpc.h \ detect-sameip.c detect-sameip.h \ detect-seq.c detect-seq.h \ detect-sid.c detect-sid.h \ +detect-snmp-version.c detect-snmp-version.h \ detect-ssh-proto.c detect-ssh-proto.h \ detect-ssh-proto-version.c detect-ssh-proto-version.h \ detect-ssh-software.c detect-ssh-software.h \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index e104114e69b3..d64f94275c54 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -173,6 +173,7 @@ #include "detect-krb5-sname.h" #include "detect-target.h" #include "detect-template-rust-buffer.h" +#include "detect-snmp-version.h" #include "detect-template-buffer.h" #include "detect-bypass.h" #include "detect-ftpdata.h" @@ -526,6 +527,7 @@ void SigTableSetup(void) DetectKrb5SNameRegister(); DetectTargetRegister(); DetectTemplateRustBufferRegister(); + DetectSNMPVersionRegister(); DetectTemplateBufferRegister(); DetectBypassRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index bca55bfbe6a0..44a698045b63 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -228,6 +228,7 @@ enum { DETECT_FTPDATA, DETECT_TARGET, DETECT_AL_TEMPLATE_RUST_BUFFER, + DETECT_AL_SNMP_VERSION, DETECT_AL_TEMPLATE_BUFFER, DETECT_BYPASS, diff --git a/src/detect-snmp-version.c b/src/detect-snmp-version.c new file mode 100644 index 000000000000..ab35540da3e7 --- /dev/null +++ b/src/detect-snmp-version.c @@ -0,0 +1,364 @@ +/* Copyright (C) 2015-2018 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Pierre Chifflier + */ + +#include "suricata-common.h" +#include "conf.h" +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-content-inspection.h" +#include "detect-snmp-version.h" +#include "app-layer-parser.h" + +#ifndef HAVE_RUST + +void DetectSNMPVersionRegister(void) +{ +} + +#else + +#include "rust-snmp-snmp-gen.h" +#include "rust-snmp-detect-gen.h" + +/** + * [snmp_version]:[<|>|<=|>=]; + */ +#define PARSE_REGEX "^\\s*(<=|>=|<|>)?\\s*([0-9]+)\\s*$" +static pcre *parse_regex; +static pcre_extra *parse_regex_study; + +enum DetectSNMPVersionMode { + PROCEDURE_EQ = 1, /* equal */ + PROCEDURE_LT, /* less than */ + PROCEDURE_LE, /* less than */ + PROCEDURE_GT, /* greater than */ + PROCEDURE_GE, /* greater than */ +}; + +typedef struct DetectSNMPVersionData_ { + uint32_t version; + enum DetectSNMPVersionMode mode; +} DetectSNMPVersionData; + +static DetectSNMPVersionData *DetectSNMPVersionParse (const char *); +static int DetectSNMPVersionSetup (DetectEngineCtx *, Signature *s, const char *str); +static void DetectSNMPVersionFree(void *); +static void DetectSNMPVersionRegisterTests(void); +static int g_snmp_version_buffer_id = 0; + +static int DetectEngineInspectSNMPRequestGeneric(ThreadVars *tv, + DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + const Signature *s, const SigMatchData *smd, + Flow *f, uint8_t flags, void *alstate, + void *txv, uint64_t tx_id); + +static int DetectSNMPVersionMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, + uint8_t, void *, void *, const Signature *, + const SigMatchCtx *); + +/** + * \brief Registration function for snmp_procedure keyword. + */ +void DetectSNMPVersionRegister (void) +{ + sigmatch_table[DETECT_AL_SNMP_VERSION].name = "snmp_version"; + sigmatch_table[DETECT_AL_SNMP_VERSION].desc = "match SNMP version"; + sigmatch_table[DETECT_AL_SNMP_VERSION].url = DOC_URL DOC_VERSION "/rules/snmp-keywords.html#snmp_version"; + sigmatch_table[DETECT_AL_SNMP_VERSION].Match = NULL; + sigmatch_table[DETECT_AL_SNMP_VERSION].AppLayerTxMatch = DetectSNMPVersionMatch; + sigmatch_table[DETECT_AL_SNMP_VERSION].Setup = DetectSNMPVersionSetup; + sigmatch_table[DETECT_AL_SNMP_VERSION].Free = DetectSNMPVersionFree; + sigmatch_table[DETECT_AL_SNMP_VERSION].RegisterTests = DetectSNMPVersionRegisterTests; + + + DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study); + + DetectAppLayerInspectEngineRegister("snmp_version", + ALPROTO_SNMP, SIG_FLAG_TOSERVER, 0, + DetectEngineInspectSNMPRequestGeneric); + + DetectAppLayerInspectEngineRegister("snmp_version", + ALPROTO_SNMP, SIG_FLAG_TOCLIENT, 0, + DetectEngineInspectSNMPRequestGeneric); + + g_snmp_version_buffer_id = DetectBufferTypeGetByName("snmp_version"); + + SCLogDebug("g_snmp_version_buffer_id %d", g_snmp_version_buffer_id); +} + +static int DetectEngineInspectSNMPRequestGeneric(ThreadVars *tv, + DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + const Signature *s, const SigMatchData *smd, + Flow *f, uint8_t flags, void *alstate, + void *txv, uint64_t tx_id) +{ + return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd, + f, flags, alstate, txv, tx_id); +} + +static inline int +VersionMatch(const uint32_t version, + enum DetectSNMPVersionMode mode, uint32_t ref_version) +{ + switch (mode) { + case PROCEDURE_EQ: + if (version == ref_version) + SCReturnInt(1); + break; + case PROCEDURE_LT: + if (version < ref_version) + SCReturnInt(1); + break; + case PROCEDURE_LE: + if (version <= ref_version) + SCReturnInt(1); + break; + case PROCEDURE_GT: + if (version > ref_version) + SCReturnInt(1); + break; + case PROCEDURE_GE: + if (version >= ref_version) + SCReturnInt(1); + break; + } + SCReturnInt(0); +} + +/** + * \internal + * \brief Function to match version of a TX + * + * \param t Pointer to thread vars. + * \param det_ctx Pointer to the pattern matcher thread. + * \param f Pointer to the current flow. + * \param flags Flags. + * \param state App layer state. + * \param s Pointer to the Signature. + * \param m Pointer to the sigmatch that we will cast into + * DetectSNMPVersionData. + * + * \retval 0 no match. + * \retval 1 match. + */ +static int DetectSNMPVersionMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, + Flow *f, uint8_t flags, void *state, + void *txv, const Signature *s, + const SigMatchCtx *ctx) +{ + SCEnter(); + + const DetectSNMPVersionData *dd = (const DetectSNMPVersionData *)ctx; + uint32_t version; + rs_snmp_tx_get_version(txv, &version); + SCLogDebug("version %u mode %u ref_version %d", + version, dd->mode, dd->version); + if (VersionMatch(version, dd->mode, dd->version)) + SCReturnInt(1); + SCReturnInt(0); +} + +/** + * \internal + * \brief Function to parse options passed via snmp_version keywords. + * + * \param rawstr Pointer to the user provided options. + * + * \retval dd pointer to DetectSNMPVersionData on success. + * \retval NULL on failure. + */ +static DetectSNMPVersionData *DetectSNMPVersionParse (const char *rawstr) +{ + DetectSNMPVersionData *dd = NULL; +#define MAX_SUBSTRINGS 30 + int ret = 0, res = 0; + int ov[MAX_SUBSTRINGS]; + char mode[2] = ""; + char value1[20] = ""; + char *endptr = NULL; + + ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, + 0, ov, MAX_SUBSTRINGS); + if (ret < 3 || ret > 5) { + SCLogError(SC_ERR_PCRE_MATCH, "Parse error %s", rawstr); + goto error; + } + + res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, mode, + sizeof(mode)); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed"); + goto error; + } + + res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, value1, + sizeof(value1)); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed"); + goto error; + } + + dd = SCCalloc(1, sizeof(DetectSNMPVersionData)); + if (unlikely(dd == NULL)) + goto error; + + if (strlen(mode) == 1) { + if (mode[0] == '<') + dd->mode = PROCEDURE_LT; + else if (mode[0] == '>') + dd->mode = PROCEDURE_GT; + } else if (strlen(mode) == 2) { + if (strcmp(mode, "<=") == 0) + dd->mode = PROCEDURE_LE; + if (strcmp(mode, ">=") == 0) + dd->mode = PROCEDURE_GE; + } + + if (dd->mode == 0) { + dd->mode = PROCEDURE_EQ; + } + + /* set the first value */ + dd->version = strtoul(value1, &endptr, 10); + if (endptr == NULL || *endptr != '\0') { + SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid character as arg " + "to snmp_version keyword"); + goto error; + } + + return dd; + +error: + if (dd) + SCFree(dd); + return NULL; +} + + + +/** + * \brief Function to add the parsed snmp version field into the current signature. + * + * \param de_ctx Pointer to the Detection Engine Context. + * \param s Pointer to the Current Signature. + * \param rawstr Pointer to the user provided flags options. + * \param type Defines if this is notBefore or notAfter. + * + * \retval 0 on Success. + * \retval -1 on Failure. + */ +static int DetectSNMPVersionSetup (DetectEngineCtx *de_ctx, Signature *s, + const char *rawstr) +{ + DetectSNMPVersionData *dd = NULL; + SigMatch *sm = NULL; + + if (DetectSignatureSetAppProto(s, ALPROTO_SNMP) != 0) + return -1; + + dd = DetectSNMPVersionParse(rawstr); + if (dd == NULL) { + SCLogError(SC_ERR_INVALID_ARGUMENT,"Parsing \'%s\' failed", rawstr); + goto error; + } + + /* okay so far so good, lets get this into a SigMatch + * and put it in the Signature. */ + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_AL_SNMP_VERSION; + sm->ctx = (void *)dd; + + SCLogDebug("snmp_version %d", dd->version); + SigMatchAppendSMToList(s, sm, g_snmp_version_buffer_id); + return 0; + +error: + DetectSNMPVersionFree(dd); + return -1; +} + +/** + * \internal + * \brief Function to free memory associated with DetectSNMPVersionData. + * + * \param de_ptr Pointer to DetectSNMPVersionData. + */ +static void DetectSNMPVersionFree(void *ptr) +{ + SCFree(ptr); +} + + +#ifdef UNITTESTS + +#include "util-unittest.h" +#include "util-unittest-helper.h" + +/** + * \test This is a test for a valid value 2. + * + * \retval 1 on success. + * \retval 0 on failure. + */ +static int SNMPValidityTestParse01 (void) +{ + DetectSNMPVersionData *dd = NULL; + dd = DetectSNMPVersionParse("2"); + FAIL_IF_NULL(dd); + FAIL_IF_NOT(dd->version == 2 && dd->mode == PROCEDURE_EQ); + DetectSNMPVersionFree(dd); + PASS; +} + +/** + * \test This is a test for a valid value >2. + * + * \retval 1 on success. + * \retval 0 on failure. + */ +static int SNMPValidityTestParse02 (void) +{ + DetectSNMPVersionData *dd = NULL; + dd = DetectSNMPVersionParse(">2"); + FAIL_IF_NULL(dd); + FAIL_IF_NOT(dd->version == 2 && dd->mode == PROCEDURE_GT); + DetectSNMPVersionFree(dd); + PASS; +} + + +#endif + +static void DetectSNMPVersionRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("SNMPValidityTestParse01", SNMPValidityTestParse01); + UtRegisterTest("SNMPValidityTestParse02", SNMPValidityTestParse02); +#endif /* UNITTESTS */ +} + +#endif diff --git a/src/detect-snmp-version.h b/src/detect-snmp-version.h new file mode 100644 index 000000000000..b17ed8479ec6 --- /dev/null +++ b/src/detect-snmp-version.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2015-2017 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Pierre Chifflier + */ + +#ifndef __DETECT_SNMP_VERSION_H__ +#define __DETECT_SNMP_VERSION_H__ + +#include "app-layer-snmp.h" + +void DetectSNMPVersionRegister(void); + +#endif /* __DETECT_SNMP_VERSION_H__ */ From 4a1fe634d229ff347f404c882e11597e8bed70c4 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Tue, 11 Dec 2018 08:54:17 +0100 Subject: [PATCH 08/12] SNMP: add the "snmp_community" detection keyword --- doc/userguide/rules/snmp-keywords.rst | 24 +++ rust/src/snmp/detect.rs | 15 ++ src/Makefile.am | 1 + src/detect-engine-register.c | 2 + src/detect-engine-register.h | 1 + src/detect-snmp-community.c | 226 ++++++++++++++++++++++++++ src/detect-snmp-community.h | 31 ++++ 7 files changed, 300 insertions(+) create mode 100644 src/detect-snmp-community.c create mode 100644 src/detect-snmp-community.h diff --git a/doc/userguide/rules/snmp-keywords.rst b/doc/userguide/rules/snmp-keywords.rst index fd81203f22ef..3cf8c1d48368 100644 --- a/doc/userguide/rules/snmp-keywords.rst +++ b/doc/userguide/rules/snmp-keywords.rst @@ -20,3 +20,27 @@ Signature example:: alert snmp any any -> any any (msg:"old SNMP version (<3)"; snmp_version:<3; sid:1; rev:1;) +snmp_community +-------------- + +SNMP community strings are like passwords for SNMP messages in version 1 and 2c. +In version 3, the community string is likely to be encrypted. This keyword will not +match if the value is not accessible. + +The default value for the read-only community string is often "public", and +"private" for the read-write community string. + +Comparison is case-sensitive. + +Syntax:: + + snmp_community; content:"private"; + +Signature example:: + + alert snmp any any -> any any (msg:"SNMP community private"; snmp_community; content:"private"; sid:2; rev:1;) + +``snmp_community`` is a 'sticky buffer'. + +``snmp_community`` can be used as ``fast_pattern``. + diff --git a/rust/src/snmp/detect.rs b/rust/src/snmp/detect.rs index d23e986b0fe4..88837b8f1d35 100644 --- a/rust/src/snmp/detect.rs +++ b/rust/src/snmp/detect.rs @@ -30,3 +30,18 @@ pub extern "C" fn rs_snmp_tx_get_version(tx: &mut SNMPTransaction, } } +#[no_mangle] +pub extern "C" fn rs_snmp_tx_get_community(tx: &mut SNMPTransaction, + buf: *mut *const libc::uint8_t, + len: *mut libc::uint32_t) +{ + match tx.community { + Some(ref c) => { + unsafe { + *buf = (&c).as_ptr(); + *len = c.len() as libc::uint32_t; + } + }, + None => () + } +} diff --git a/src/Makefile.am b/src/Makefile.am index fc0322f7ac98..c471b2c313ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -233,6 +233,7 @@ detect-rpc.c detect-rpc.h \ detect-sameip.c detect-sameip.h \ detect-seq.c detect-seq.h \ detect-sid.c detect-sid.h \ +detect-snmp-community.c detect-snmp-community.h \ detect-snmp-version.c detect-snmp-version.h \ detect-ssh-proto.c detect-ssh-proto.h \ detect-ssh-proto-version.c detect-ssh-proto-version.h \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index d64f94275c54..ef472c93ab7b 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -174,6 +174,7 @@ #include "detect-target.h" #include "detect-template-rust-buffer.h" #include "detect-snmp-version.h" +#include "detect-snmp-community.h" #include "detect-template-buffer.h" #include "detect-bypass.h" #include "detect-ftpdata.h" @@ -528,6 +529,7 @@ void SigTableSetup(void) DetectTargetRegister(); DetectTemplateRustBufferRegister(); DetectSNMPVersionRegister(); + DetectSNMPCommunityRegister(); DetectTemplateBufferRegister(); DetectBypassRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 44a698045b63..83e2a30dc962 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -229,6 +229,7 @@ enum { DETECT_TARGET, DETECT_AL_TEMPLATE_RUST_BUFFER, DETECT_AL_SNMP_VERSION, + DETECT_AL_SNMP_COMMUNITY, DETECT_AL_TEMPLATE_BUFFER, DETECT_BYPASS, diff --git a/src/detect-snmp-community.c b/src/detect-snmp-community.c new file mode 100644 index 000000000000..e92b455ea3e8 --- /dev/null +++ b/src/detect-snmp-community.c @@ -0,0 +1,226 @@ +/* Copyright (C) 2015-2017 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + * Set up of the "snmp_community" keyword to allow content + * inspections on the decoded snmp community. + */ + +#include "suricata-common.h" +#include "conf.h" +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-content-inspection.h" +#include "detect-snmp-community.h" +#include "app-layer-parser.h" + +#ifndef HAVE_RUST + +void DetectSNMPCommunityRegister(void) +{ +} + +#else + +#include "rust-snmp-snmp-gen.h" +#include "rust-snmp-detect-gen.h" + +static int DetectSNMPCommunitySetup(DetectEngineCtx *, Signature *, + const char *); +static int DetectEngineInspectSNMPCommunity(ThreadVars *tv, + DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + const Signature *s, const SigMatchData *smd, + Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id); +static void DetectSNMPCommunityRegisterTests(void); +static int g_snmp_rust_id = 0; + +void DetectSNMPCommunityRegister(void) +{ + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].name = "snmp_community"; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].desc = + "SNMP content modififier to match on the snmp community"; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].Setup = + DetectSNMPCommunitySetup; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].RegisterTests = + DetectSNMPCommunityRegisterTests; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].url = DOC_URL DOC_VERSION "/rules/snmp-keywords.html#snmp_community"; + + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].flags |= SIGMATCH_NOOPT; + + /* register inspect engines */ + DetectAppLayerInspectEngineRegister("snmp_community", + ALPROTO_SNMP, SIG_FLAG_TOSERVER, 0, + DetectEngineInspectSNMPCommunity); + DetectAppLayerInspectEngineRegister("snmp_community", + ALPROTO_SNMP, SIG_FLAG_TOCLIENT, 0, + DetectEngineInspectSNMPCommunity); + + g_snmp_rust_id = DetectBufferTypeGetByName("snmp_community"); + + SCLogDebug("SNMP community detect registered."); +} + +static int DetectSNMPCommunitySetup(DetectEngineCtx *de_ctx, Signature *s, + const char *str) +{ + s->init_data->list = g_snmp_rust_id; + + if (DetectSignatureSetAppProto(s, ALPROTO_SNMP) != 0) + return -1; + + return 0; +} + +static int DetectEngineInspectSNMPCommunity(ThreadVars *tv, + DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + const Signature *s, const SigMatchData *smd, + Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id) +{ + int ret = 0; + const uint8_t *data = NULL; + uint32_t data_len = 0; + + if (flags & STREAM_TOSERVER) { + rs_snmp_tx_get_community(txv, (uint8_t **)&data, &data_len); + } else if (flags & STREAM_TOCLIENT) { + rs_snmp_tx_get_community(txv, (uint8_t **)&data, &data_len); + } + + if (data != NULL) { + ret = DetectEngineContentInspection(de_ctx, det_ctx, s, smd, + f, (uint8_t *)data, data_len, 0, DETECT_CI_FLAGS_SINGLE, + DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, NULL); + } + + return ret; +} + +#ifdef UNITTESTS + +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "app-layer-parser.h" +#include "detect-engine.h" +#include "detect-parse.h" +#include "flow-util.h" +#include "stream-tcp.h" + +static int DetectSNMPCommunityTest(void) +{ + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + DetectEngineThreadCtx *det_ctx = NULL; + DetectEngineCtx *de_ctx = NULL; + Flow f; + Packet *p; + TcpSession tcp; + ThreadVars tv; + Signature *s; + + /*uint8_t request[] = "\x30\x27\x02\x01\x01\x04\x0b\x5b\x52\x30\x5f\x43\x40\x63\x74\x69" \ + "\x21\x5d\xa1\x15\x02\x04\x2b\x13\x3f\x85\x02\x01\x00\x02\x01\x00" \ + "\x30\x07\x30\x05\x06\x01\x01\x05\x00";*/ + uint8_t request[] = { + 0x30, 0x27, 0x02, 0x01, 0x01, 0x04, 0x0b, 0x5b, + 0x52, 0x30, 0x5f, 0x43, 0x40, 0x63, 0x74, 0x69, + 0x21, 0x5d, 0xa1, 0x15, 0x02, 0x04, 0x2b, 0x13, + 0x3f, 0x85, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, + 0x30, 0x07, 0x30, 0x05, 0x06, 0x01, 0x01, 0x05, + 0x00 + }; + + /* Setup flow. */ + memset(&f, 0, sizeof(Flow)); + memset(&tcp, 0, sizeof(TcpSession)); + memset(&tv, 0, sizeof(ThreadVars)); + p = UTHBuildPacket(request, sizeof(request), IPPROTO_UDP); + FLOW_INITIALIZE(&f); + f.alproto = ALPROTO_SNMP; + f.protoctx = (void *)&tcp; + f.proto = IPPROTO_UDP; + f.protomap = FlowGetProtoMapping(f.proto); + f.flags |= FLOW_IPV4; + p->flow = &f; + p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; + StreamTcpInitConfig(TRUE); + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + /* This rule should match. */ + s = DetectEngineAppendSig(de_ctx, + "alert snmp any any -> any any (" + "msg:\"SNMP Test Rule\"; " + "snmp_community; content:\"[R0_C@cti!]\"; " + "sid:1; rev:1;)"); + FAIL_IF_NULL(s); + + /* This rule should not match. */ + s = DetectEngineAppendSig(de_ctx, + "alert snmp any any -> any any (" + "msg:\"SNMP Test Rule\"; " + "snmp_community; content:\"private\"; " + "sid:2; rev:1;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + FLOWLOCK_WRLOCK(&f); + AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SNMP, + STREAM_TOSERVER, request, sizeof(request)); + FLOWLOCK_UNLOCK(&f); + + /* Check that we have app-layer state. */ + FAIL_IF_NULL(f.alstate); + + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + FAIL_IF(!PacketAlertCheck(p, 1)); + FAIL_IF(PacketAlertCheck(p, 2)); + + /* Cleanup. */ + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + + PASS; +} + +#endif + +static void DetectSNMPCommunityRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("DetectSNMPCommunityTest", + DetectSNMPCommunityTest); +#endif /* UNITTESTS */ +} + +#endif diff --git a/src/detect-snmp-community.h b/src/detect-snmp-community.h new file mode 100644 index 000000000000..ac562d1b50bc --- /dev/null +++ b/src/detect-snmp-community.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2015-2017 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author FirstName LastName + */ + +#ifndef __DETECT_SNMP_COMMUNITY_H__ +#define __DETECT_SNMP_COMMUNITY_H__ + +#include "app-layer-snmp.h" + +void DetectSNMPCommunityRegister(void); + +#endif /* __DETECT_SNMP_COMMUNITY_H__ */ From 37a7c5aeeedc62c8fa64e1a7555386ddd317e3bc Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Tue, 11 Dec 2018 17:21:44 +0100 Subject: [PATCH 09/12] SNMP: add the "snmp_pdu_type" detection keyword --- doc/userguide/rules/snmp-keywords.rst | 29 +++ rust/src/snmp/detect.rs | 16 ++ src/Makefile.am | 1 + src/detect-engine-register.c | 2 + src/detect-engine-register.h | 1 + src/detect-snmp-pdu_type.c | 278 ++++++++++++++++++++++++++ src/detect-snmp-pdu_type.h | 31 +++ 7 files changed, 358 insertions(+) create mode 100644 src/detect-snmp-pdu_type.c create mode 100644 src/detect-snmp-pdu_type.h diff --git a/doc/userguide/rules/snmp-keywords.rst b/doc/userguide/rules/snmp-keywords.rst index 3cf8c1d48368..a904a6b804ee 100644 --- a/doc/userguide/rules/snmp-keywords.rst +++ b/doc/userguide/rules/snmp-keywords.rst @@ -44,3 +44,32 @@ Signature example:: ``snmp_community`` can be used as ``fast_pattern``. +snmp_pdu_type +------------- + +SNMP PDU type (integer). + +Common values are: + + - 0: GetRequest + - 1: GetNextRequest + - 2: Response + - 3: SetRequest + - 4: TrapV1 (obsolete, was the old Trap-PDU in SNMPv1) + - 5: GetBulkRequest + - 6: InformRequest + - 7: TrapV2 + - 8: Report + +This keyword will not match if the value is not accessible within (for ex, an encrypted +SNMP v3 message). + + +Syntax:: + + snmp_pdu_type: + +Signature example:: + + alert snmp any any -> any any (msg:"SNMP response"; snmp_pdu_type:2; sid:3; rev:1;) + diff --git a/rust/src/snmp/detect.rs b/rust/src/snmp/detect.rs index 88837b8f1d35..114792d94e07 100644 --- a/rust/src/snmp/detect.rs +++ b/rust/src/snmp/detect.rs @@ -45,3 +45,19 @@ pub extern "C" fn rs_snmp_tx_get_community(tx: &mut SNMPTransaction, None => () } } + +#[no_mangle] +pub extern "C" fn rs_snmp_tx_get_pdu_type(tx: &mut SNMPTransaction, + pdu_type: *mut libc::uint32_t) +{ + unsafe { + match tx.info { + Some(ref info) => { + *pdu_type = info.pdu_type.0 as libc::uint32_t; + }, + None => { + *pdu_type = 0xffffffff; + } + } + } +} diff --git a/src/Makefile.am b/src/Makefile.am index c471b2c313ad..64510343a0dc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -234,6 +234,7 @@ detect-sameip.c detect-sameip.h \ detect-seq.c detect-seq.h \ detect-sid.c detect-sid.h \ detect-snmp-community.c detect-snmp-community.h \ +detect-snmp-pdu_type.c detect-snmp-pdu_type.h \ detect-snmp-version.c detect-snmp-version.h \ detect-ssh-proto.c detect-ssh-proto.h \ detect-ssh-proto-version.c detect-ssh-proto-version.h \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index ef472c93ab7b..09f104b779a8 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -175,6 +175,7 @@ #include "detect-template-rust-buffer.h" #include "detect-snmp-version.h" #include "detect-snmp-community.h" +#include "detect-snmp-pdu_type.h" #include "detect-template-buffer.h" #include "detect-bypass.h" #include "detect-ftpdata.h" @@ -530,6 +531,7 @@ void SigTableSetup(void) DetectTemplateRustBufferRegister(); DetectSNMPVersionRegister(); DetectSNMPCommunityRegister(); + DetectSNMPPduTypeRegister(); DetectTemplateBufferRegister(); DetectBypassRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 83e2a30dc962..504ba07d9d3d 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -230,6 +230,7 @@ enum { DETECT_AL_TEMPLATE_RUST_BUFFER, DETECT_AL_SNMP_VERSION, DETECT_AL_SNMP_COMMUNITY, + DETECT_AL_SNMP_PDU_TYPE, DETECT_AL_TEMPLATE_BUFFER, DETECT_BYPASS, diff --git a/src/detect-snmp-pdu_type.c b/src/detect-snmp-pdu_type.c new file mode 100644 index 000000000000..b061228d05e3 --- /dev/null +++ b/src/detect-snmp-pdu_type.c @@ -0,0 +1,278 @@ +/* Copyright (C) 2015-2018 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Pierre Chifflier + */ + +#include "suricata-common.h" +#include "conf.h" +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-content-inspection.h" +#include "detect-snmp-pdu_type.h" +#include "app-layer-parser.h" + +#ifndef HAVE_RUST + +void DetectSNMPPduTypeRegister(void) +{ +} + +#else + +#include "rust-snmp-snmp-gen.h" +#include "rust-snmp-detect-gen.h" + +/** + * [snmp_pdu_type]:; + */ +#define PARSE_REGEX "^\\s*([0-9]+)\\s*$" +static pcre *parse_regex; +static pcre_extra *parse_regex_study; + +typedef struct DetectSNMPPduTypeData_ { + uint32_t pdu_type; +} DetectSNMPPduTypeData; + +static DetectSNMPPduTypeData *DetectSNMPPduTypeParse (const char *); +static int DetectSNMPPduTypeSetup (DetectEngineCtx *, Signature *s, const char *str); +static void DetectSNMPPduTypeFree(void *); +static void DetectSNMPPduTypeRegisterTests(void); +static int g_snmp_pdu_type_buffer_id = 0; + +static int DetectEngineInspectSNMPRequestGeneric(ThreadVars *tv, + DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + const Signature *s, const SigMatchData *smd, + Flow *f, uint8_t flags, void *alstate, + void *txv, uint64_t tx_id); + +static int DetectSNMPPduTypeMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, + uint8_t, void *, void *, const Signature *, + const SigMatchCtx *); + +void DetectSNMPPduTypeRegister(void) +{ + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].name = "snmp_pdu_type"; + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].desc = "match SNMP Pdu type"; + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].url = DOC_URL DOC_VERSION "/rules/snmp-keywords.html#snmp_pdu_type"; + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].Match = NULL; + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].AppLayerTxMatch = DetectSNMPPduTypeMatch; + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].Setup = DetectSNMPPduTypeSetup; + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].Free = DetectSNMPPduTypeFree; + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].RegisterTests = DetectSNMPPduTypeRegisterTests; + + DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study); + + DetectAppLayerInspectEngineRegister("snmp_pdu_type", + ALPROTO_SNMP, SIG_FLAG_TOSERVER, 0, + DetectEngineInspectSNMPRequestGeneric); + + DetectAppLayerInspectEngineRegister("snmp_pdu_type", + ALPROTO_SNMP, SIG_FLAG_TOCLIENT, 0, + DetectEngineInspectSNMPRequestGeneric); + + g_snmp_pdu_type_buffer_id = DetectBufferTypeGetByName("snmp_pdu_type"); + + SCLogDebug("g_snmp_pdu_type_buffer_id %d", g_snmp_pdu_type_buffer_id); +} + +static int DetectEngineInspectSNMPRequestGeneric(ThreadVars *tv, + DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + const Signature *s, const SigMatchData *smd, + Flow *f, uint8_t flags, void *alstate, + void *txv, uint64_t tx_id) +{ + return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd, + f, flags, alstate, txv, tx_id); +} + +/** + * \internal + * \brief Function to match pdu_type of a TX + * + * \param t Pointer to thread vars. + * \param det_ctx Pointer to the pattern matcher thread. + * \param f Pointer to the current flow. + * \param flags Flags. + * \param state App layer state. + * \param s Pointer to the Signature. + * \param m Pointer to the sigmatch that we will cast into + * DetectSNMPPduTypeData. + * + * \retval 0 no match. + * \retval 1 match. + */ +static int DetectSNMPPduTypeMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, + Flow *f, uint8_t flags, void *state, + void *txv, const Signature *s, + const SigMatchCtx *ctx) +{ + SCEnter(); + + const DetectSNMPPduTypeData *dd = (const DetectSNMPPduTypeData *)ctx; + uint32_t pdu_type; + rs_snmp_tx_get_pdu_type(txv, &pdu_type); + SCLogDebug("pdu_type %u ref_pdu_type %d", + pdu_type, dd->pdu_type); + if (pdu_type == dd->pdu_type) + SCReturnInt(1); + SCReturnInt(0); +} + +/** + * \internal + * \brief Function to parse options passed via snmp_pdu_type keywords. + * + * \param rawstr Pointer to the user provided options. + * + * \retval dd pointer to DetectSNMPPduTypeData on success. + * \retval NULL on failure. + */ +static DetectSNMPPduTypeData *DetectSNMPPduTypeParse (const char *rawstr) +{ + DetectSNMPPduTypeData *dd = NULL; +#define MAX_SUBSTRINGS 30 + int ret = 0, res = 0; + int ov[MAX_SUBSTRINGS]; + char value1[20] = ""; + char *endptr = NULL; + + ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, + 0, ov, MAX_SUBSTRINGS); + if (ret != 2) { + SCLogError(SC_ERR_PCRE_MATCH, "Parse error %s", rawstr); + goto error; + } + + res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, value1, + sizeof(value1)); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed"); + goto error; + } + + dd = SCCalloc(1, sizeof(DetectSNMPPduTypeData)); + if (unlikely(dd == NULL)) + goto error; + + /* set the value */ + dd->pdu_type = strtoul(value1, &endptr, 10); + if (endptr == NULL || *endptr != '\0') { + SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid character as arg " + "to snmp_pdu_type keyword"); + goto error; + } + + return dd; + +error: + if (dd) + SCFree(dd); + return NULL; +} + +/** + * \brief Function to add the parsed snmp pdu_type field into the current signature. + * + * \param de_ctx Pointer to the Detection Engine Context. + * \param s Pointer to the Current Signature. + * \param rawstr Pointer to the user provided flags options. + * \param type Defines if this is notBefore or notAfter. + * + * \retval 0 on Success. + * \retval -1 on Failure. + */ +static int DetectSNMPPduTypeSetup (DetectEngineCtx *de_ctx, Signature *s, + const char *rawstr) +{ + DetectSNMPPduTypeData *dd = NULL; + SigMatch *sm = NULL; + + if (DetectSignatureSetAppProto(s, ALPROTO_SNMP) != 0) + return -1; + + dd = DetectSNMPPduTypeParse(rawstr); + if (dd == NULL) { + SCLogError(SC_ERR_INVALID_ARGUMENT,"Parsing \'%s\' failed", rawstr); + goto error; + } + + /* okay so far so good, lets get this into a SigMatch + * and put it in the Signature. */ + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_AL_SNMP_PDU_TYPE; + sm->ctx = (void *)dd; + + SCLogDebug("snmp_pdu_type %d", dd->pdu_type); + SigMatchAppendSMToList(s, sm, g_snmp_pdu_type_buffer_id); + return 0; + +error: + DetectSNMPPduTypeFree(dd); + return -1; +} + +/** + * \internal + * \brief Function to free memory associated with DetectSNMPPduTypeData. + * + * \param de_ptr Pointer to DetectSNMPPduTypeData. + */ +static void DetectSNMPPduTypeFree(void *ptr) +{ + SCFree(ptr); +} + + +#ifdef UNITTESTS + +#include "util-unittest.h" +#include "util-unittest-helper.h" + +/** + * \test This is a test for a valid value 2. + * + * \retval 1 on success. + * \retval 0 on failure. + */ +static int SNMPValidityTestParse01 (void) +{ + DetectSNMPPduTypeData *dd = NULL; + dd = DetectSNMPPduTypeParse("2"); + FAIL_IF_NULL(dd); + FAIL_IF_NOT(dd->pdu_type == 2); + DetectSNMPPduTypeFree(dd); + PASS; +} + +#endif + +static void DetectSNMPPduTypeRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("SNMPValidityTestParse01", SNMPValidityTestParse01); +#endif /* UNITTESTS */ +} + +#endif diff --git a/src/detect-snmp-pdu_type.h b/src/detect-snmp-pdu_type.h new file mode 100644 index 000000000000..d6b0c4feea35 --- /dev/null +++ b/src/detect-snmp-pdu_type.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2015-2017 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Pierre Chifflier + */ + +#ifndef __DETECT_SNMP_PDU_TYPE_H__ +#define __DETECT_SNMP_PDU_TYPE_H__ + +#include "app-layer-snmp.h" + +void DetectSNMPPduTypeRegister(void); + +#endif /* __DETECT_SNMP_PDU_TYPE_H__ */ From 91f18055e8617213ed6a76b8e6672083b61e517b Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Thu, 14 Feb 2019 17:11:50 +0100 Subject: [PATCH 10/12] rust/snmp: fix selection of v1/v2c parser --- rust/src/snmp/snmp.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/rust/src/snmp/snmp.rs b/rust/src/snmp/snmp.rs index 6cc1503705bc..2ecfd3f0a5cd 100644 --- a/rust/src/snmp/snmp.rs +++ b/rust/src/snmp/snmp.rs @@ -133,7 +133,16 @@ impl SNMPState { } fn parse_v1_2(&mut self, i: &[u8], _direction: u8) -> i32 { - match parse_snmp_v1(i) { + let parser = match self.version { + 1 => parse_snmp_v1, + 2 => parse_snmp_v2c, + _ => { + SCLogInfo!("parse_snmp: invalid version {}", self.version); + self.set_event(SNMPEvent::MalformedData); + return -1; + } + }; + match parser(i) { Ok((_rem,r)) => { let mut tx = self.new_tx(); self.add_pdu_info(&r.pdu, &mut tx); @@ -142,7 +151,7 @@ impl SNMPState { 0 }, _e => { - SCLogInfo!("parse_snmp_v1 failed: {:?}", _e); + SCLogInfo!("parse_snmp_v{} failed: {:?}", self.version, _e); self.set_event(SNMPEvent::MalformedData); -1 }, From 7a3ac5a1503d2510b046a6f9e7cebc8524e3ddde Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 13 May 2019 14:14:48 +0200 Subject: [PATCH 11/12] detect/snmp: rename keywords to snmp., and make rust mandatory --- src/detect-snmp-community.c | 22 +++++++--------------- src/detect-snmp-pdu_type.c | 12 ++---------- src/detect-snmp-version.c | 15 +++------------ 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/src/detect-snmp-community.c b/src/detect-snmp-community.c index e92b455ea3e8..ce0cb3c1548c 100644 --- a/src/detect-snmp-community.c +++ b/src/detect-snmp-community.c @@ -33,14 +33,6 @@ #include "detect-snmp-community.h" #include "app-layer-parser.h" -#ifndef HAVE_RUST - -void DetectSNMPCommunityRegister(void) -{ -} - -#else - #include "rust-snmp-snmp-gen.h" #include "rust-snmp-detect-gen.h" @@ -55,7 +47,8 @@ static int g_snmp_rust_id = 0; void DetectSNMPCommunityRegister(void) { - sigmatch_table[DETECT_AL_SNMP_COMMUNITY].name = "snmp_community"; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].name = "snmp.community"; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].alias = "snmp_community"; sigmatch_table[DETECT_AL_SNMP_COMMUNITY].desc = "SNMP content modififier to match on the snmp community"; sigmatch_table[DETECT_AL_SNMP_COMMUNITY].Setup = @@ -64,7 +57,7 @@ void DetectSNMPCommunityRegister(void) DetectSNMPCommunityRegisterTests; sigmatch_table[DETECT_AL_SNMP_COMMUNITY].url = DOC_URL DOC_VERSION "/rules/snmp-keywords.html#snmp_community"; - sigmatch_table[DETECT_AL_SNMP_COMMUNITY].flags |= SIGMATCH_NOOPT; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER; /* register inspect engines */ DetectAppLayerInspectEngineRegister("snmp_community", @@ -82,7 +75,8 @@ void DetectSNMPCommunityRegister(void) static int DetectSNMPCommunitySetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) { - s->init_data->list = g_snmp_rust_id; + if (DetectBufferSetActiveList(s, g_snmp_rust_id) < 0) + return -1; if (DetectSignatureSetAppProto(s, ALPROTO_SNMP) != 0) return -1; @@ -107,8 +101,8 @@ static int DetectEngineInspectSNMPCommunity(ThreadVars *tv, if (data != NULL) { ret = DetectEngineContentInspection(de_ctx, det_ctx, s, smd, - f, (uint8_t *)data, data_len, 0, DETECT_CI_FLAGS_SINGLE, - DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, NULL); + NULL, f, (uint8_t *)data, data_len, 0, DETECT_CI_FLAGS_SINGLE, + DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); } return ret; @@ -222,5 +216,3 @@ static void DetectSNMPCommunityRegisterTests(void) DetectSNMPCommunityTest); #endif /* UNITTESTS */ } - -#endif diff --git a/src/detect-snmp-pdu_type.c b/src/detect-snmp-pdu_type.c index b061228d05e3..89ad380a0b4a 100644 --- a/src/detect-snmp-pdu_type.c +++ b/src/detect-snmp-pdu_type.c @@ -30,14 +30,6 @@ #include "detect-snmp-pdu_type.h" #include "app-layer-parser.h" -#ifndef HAVE_RUST - -void DetectSNMPPduTypeRegister(void) -{ -} - -#else - #include "rust-snmp-snmp-gen.h" #include "rust-snmp-detect-gen.h" @@ -71,6 +63,7 @@ static int DetectSNMPPduTypeMatch (ThreadVars *, DetectEngineThreadCtx *, Flow * void DetectSNMPPduTypeRegister(void) { sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].name = "snmp_pdu_type"; + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].alias = "snmp.pdu_type"; sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].desc = "match SNMP Pdu type"; sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].url = DOC_URL DOC_VERSION "/rules/snmp-keywords.html#snmp_pdu_type"; sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].Match = NULL; @@ -78,6 +71,7 @@ void DetectSNMPPduTypeRegister(void) sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].Setup = DetectSNMPPduTypeSetup; sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].Free = DetectSNMPPduTypeFree; sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].RegisterTests = DetectSNMPPduTypeRegisterTests; + sigmatch_table[DETECT_AL_SNMP_PDU_TYPE].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER; DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study); @@ -274,5 +268,3 @@ static void DetectSNMPPduTypeRegisterTests(void) UtRegisterTest("SNMPValidityTestParse01", SNMPValidityTestParse01); #endif /* UNITTESTS */ } - -#endif diff --git a/src/detect-snmp-version.c b/src/detect-snmp-version.c index ab35540da3e7..2d0621bc7e71 100644 --- a/src/detect-snmp-version.c +++ b/src/detect-snmp-version.c @@ -30,14 +30,6 @@ #include "detect-snmp-version.h" #include "app-layer-parser.h" -#ifndef HAVE_RUST - -void DetectSNMPVersionRegister(void) -{ -} - -#else - #include "rust-snmp-snmp-gen.h" #include "rust-snmp-detect-gen.h" @@ -82,7 +74,8 @@ static int DetectSNMPVersionMatch (ThreadVars *, DetectEngineThreadCtx *, Flow * */ void DetectSNMPVersionRegister (void) { - sigmatch_table[DETECT_AL_SNMP_VERSION].name = "snmp_version"; + sigmatch_table[DETECT_AL_SNMP_VERSION].name = "snmp.version"; + sigmatch_table[DETECT_AL_SNMP_VERSION].alias = "snmp_version"; sigmatch_table[DETECT_AL_SNMP_VERSION].desc = "match SNMP version"; sigmatch_table[DETECT_AL_SNMP_VERSION].url = DOC_URL DOC_VERSION "/rules/snmp-keywords.html#snmp_version"; sigmatch_table[DETECT_AL_SNMP_VERSION].Match = NULL; @@ -90,7 +83,7 @@ void DetectSNMPVersionRegister (void) sigmatch_table[DETECT_AL_SNMP_VERSION].Setup = DetectSNMPVersionSetup; sigmatch_table[DETECT_AL_SNMP_VERSION].Free = DetectSNMPVersionFree; sigmatch_table[DETECT_AL_SNMP_VERSION].RegisterTests = DetectSNMPVersionRegisterTests; - + sigmatch_table[DETECT_AL_SNMP_VERSION].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER; DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study); @@ -360,5 +353,3 @@ static void DetectSNMPVersionRegisterTests(void) UtRegisterTest("SNMPValidityTestParse02", SNMPValidityTestParse02); #endif /* UNITTESTS */ } - -#endif From 0c62485026382007d7566e38f0fb714d36df737f Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Tue, 21 May 2019 21:10:02 +0200 Subject: [PATCH 12/12] rust/snmp: fix missing IPPROTO_* declarations (use core) --- rust/src/snmp/snmp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/src/snmp/snmp.rs b/rust/src/snmp/snmp.rs index 2ecfd3f0a5cd..f336faaebee3 100644 --- a/rust/src/snmp/snmp.rs +++ b/rust/src/snmp/snmp.rs @@ -564,7 +564,7 @@ pub unsafe extern "C" fn rs_register_snmp_parser() { let mut parser = RustParser { name : PARSER_NAME.as_ptr() as *const libc::c_char, default_port : default_port.as_ptr(), - ipproto : libc::IPPROTO_UDP, + ipproto : core::IPPROTO_UDP, probe_ts : rs_snmp_probing_parser, probe_tc : rs_snmp_probing_parser, min_depth : 0, @@ -600,7 +600,7 @@ pub unsafe extern "C" fn rs_register_snmp_parser() { if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { let _ = AppLayerRegisterParser(&parser, alproto); } - AppLayerParserRegisterGetTxIterator(libc::IPPROTO_UDP as u8, alproto, rs_snmp_get_tx_iterator); + AppLayerParserRegisterGetTxIterator(core::IPPROTO_UDP as u8, alproto, rs_snmp_get_tx_iterator); // port 162 let default_port_traps = CString::new("162").unwrap(); parser.default_port = default_port_traps.as_ptr();