From 2df840a8b87d15b8d3b398a8af964e4c9b5ee82c Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 8 Aug 2018 15:35:31 +0200 Subject: [PATCH] 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 | 69 ++++ src/app-layer-snmp.h | 33 ++ suricata.yaml.in | 2 + 13 files changed, 743 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 020c039b27..750f1179ed 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 b3eceb5c07..0dc2973898 100755 --- a/rust/gen-c-headers.py +++ b/rust/gen-c-headers.py @@ -86,6 +86,8 @@ type_map = { "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 46f6a2d357..d05b2fe0ae 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 0000000000..17a026ac4b --- /dev/null +++ b/rust/src/snmp/mod.rs @@ -0,0 +1,22 @@ +/* Copyright (C) 2017-2019 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 0000000000..a1eb598eab --- /dev/null +++ b/rust/src/snmp/snmp.rs @@ -0,0 +1,601 @@ +/* Copyright (C) 2017-2019 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 23f8c13718..9f0f489aac 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 780b386105..d8ae206713 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 73fa275d84..58af8e3e03 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" @@ -1478,6 +1479,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 4ad78b73f4..04120d3454 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 c258935b79..ffd0bd9251 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 0000000000..f2ae0add60 --- /dev/null +++ b/src/app-layer-snmp.c @@ -0,0 +1,69 @@ +/* Copyright (C) 2015-2019 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" + +static void SNMPParserRegisterTests(void); + +void RegisterSNMPParsers(void) +{ + rs_register_snmp_parser(); + +#ifdef UNITTESTS + AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_SNMP, + SNMPParserRegisterTests); +#endif +} + +#ifdef UNITTESTS +#endif + +static 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 0000000000..7c2d09680c --- /dev/null +++ b/src/app-layer-snmp.h @@ -0,0 +1,33 @@ +/* Copyright (C) 2017-2019 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); + +/** 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 1c3863575c..b4f2d91c38 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: -- 2.47.2