]>
Commit | Line | Data |
---|---|---|
2f5834cd | 1 | /* Copyright (C) 2017-2020 Open Information Security Foundation |
2df840a8 PC |
2 | * |
3 | * You can copy, redistribute or modify this Program under the terms of | |
4 | * the GNU General Public License version 2 as published by the Free | |
5 | * Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * version 2 along with this program; if not, write to the Free Software | |
14 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
15 | * 02110-1301, USA. | |
16 | */ | |
17 | ||
18 | // written by Pierre Chifflier <chifflier@wzdftpd.net> | |
19 | ||
42e5065a | 20 | use crate::snmp::snmp_parser::*; |
baf30cfc | 21 | use crate::core::{self, *}; |
2f5834cd | 22 | use crate::applayer::{self, *}; |
2df840a8 | 23 | use std; |
18448f6e | 24 | use std::ffi::CString; |
2df840a8 | 25 | |
5b809f77 PC |
26 | use der_parser::ber::BerObjectContent; |
27 | use der_parser::der::parse_der_sequence; | |
2df840a8 PC |
28 | use der_parser::oid::Oid; |
29 | use nom; | |
5b809f77 PC |
30 | use nom::IResult; |
31 | use nom::error::ErrorKind; | |
2df840a8 | 32 | |
18448f6e | 33 | #[derive(AppLayerEvent)] |
2df840a8 | 34 | pub enum SNMPEvent { |
18448f6e | 35 | MalformedData, |
2df840a8 | 36 | UnknownSecurityModel, |
af7d245a | 37 | VersionMismatch, |
2df840a8 PC |
38 | } |
39 | ||
6b8517dc | 40 | pub struct SNMPState<'a> { |
2df840a8 PC |
41 | /// SNMP protocol version |
42 | pub version: u32, | |
43 | ||
44 | /// List of transactions for this session | |
6b8517dc | 45 | transactions: Vec<SNMPTransaction<'a>>, |
2df840a8 PC |
46 | |
47 | /// tx counter for assigning incrementing id's to tx's | |
48 | tx_id: u64, | |
49 | } | |
50 | ||
6b8517dc | 51 | pub struct SNMPPduInfo<'a> { |
2df840a8 PC |
52 | pub pdu_type: PduType, |
53 | ||
54 | pub err: ErrorStatus, | |
55 | ||
6b8517dc | 56 | pub trap_type: Option<(TrapType,Oid<'a>,NetworkAddress)>, |
2df840a8 | 57 | |
6b8517dc | 58 | pub vars: Vec<Oid<'a>>, |
2df840a8 PC |
59 | } |
60 | ||
6b8517dc | 61 | pub struct SNMPTransaction<'a> { |
aa608e0c PC |
62 | /// PDU version |
63 | pub version: u32, | |
64 | ||
2df840a8 | 65 | /// PDU info, if present (and cleartext) |
6b8517dc | 66 | pub info: Option<SNMPPduInfo<'a>>, |
2df840a8 PC |
67 | |
68 | /// Community, if present (SNMPv2) | |
69 | pub community: Option<String>, | |
70 | ||
71 | /// USM info, if present (SNMPv3) | |
72 | pub usm: Option<String>, | |
73 | ||
74 | /// True if transaction was encrypted | |
75 | pub encrypted: bool, | |
76 | ||
77 | /// The internal transaction id | |
78 | id: u64, | |
79 | ||
2df840a8 PC |
80 | /// The events associated with this transaction |
81 | events: *mut core::AppLayerDecoderEvents, | |
82 | ||
11e24345 | 83 | tx_data: applayer::AppLayerTxData, |
2df840a8 PC |
84 | } |
85 | ||
3f2d2bc1 JI |
86 | impl<'a> Transaction for SNMPTransaction<'a> { |
87 | fn id(&self) -> u64 { | |
88 | self.id | |
89 | } | |
90 | } | |
2df840a8 | 91 | |
6b8517dc ET |
92 | impl<'a> SNMPState<'a> { |
93 | pub fn new() -> SNMPState<'a> { | |
2df840a8 PC |
94 | SNMPState{ |
95 | version: 0, | |
96 | transactions: Vec::new(), | |
97 | tx_id: 0, | |
98 | } | |
99 | } | |
100 | } | |
101 | ||
6b8517dc ET |
102 | impl<'a> Default for SNMPPduInfo<'a> { |
103 | fn default() -> SNMPPduInfo<'a> { | |
2df840a8 PC |
104 | SNMPPduInfo{ |
105 | pdu_type: PduType(0), | |
106 | err: ErrorStatus::NoError, | |
107 | trap_type: None, | |
108 | vars: Vec::new() | |
109 | } | |
110 | } | |
111 | } | |
112 | ||
3f2d2bc1 JI |
113 | impl<'a> State<SNMPTransaction<'a>> for SNMPState<'a> { |
114 | fn get_transactions(&self) -> &[SNMPTransaction<'a>] { | |
115 | &self.transactions | |
116 | } | |
117 | } | |
118 | ||
6b8517dc ET |
119 | impl<'a> SNMPState<'a> { |
120 | fn add_pdu_info(&mut self, pdu: &SnmpPdu<'a>, tx: &mut SNMPTransaction<'a>) { | |
2df840a8 PC |
121 | let mut pdu_info = SNMPPduInfo::default(); |
122 | pdu_info.pdu_type = pdu.pdu_type(); | |
60324740 | 123 | match *pdu { |
2df840a8 PC |
124 | SnmpPdu::Generic(ref pdu) => { |
125 | pdu_info.err = pdu.err; | |
126 | }, | |
127 | SnmpPdu::Bulk(_) => { | |
128 | }, | |
129 | SnmpPdu::TrapV1(ref t) => { | |
ac3a20b6 | 130 | pdu_info.trap_type = Some((t.generic_trap,t.enterprise.clone(),t.agent_addr)); |
2df840a8 PC |
131 | } |
132 | } | |
6b8517dc ET |
133 | |
134 | for var in pdu.vars_iter() { | |
135 | pdu_info.vars.push(var.oid.to_owned()); | |
2df840a8 PC |
136 | } |
137 | tx.info = Some(pdu_info); | |
138 | } | |
139 | ||
baf30cfc | 140 | fn handle_snmp_v12(&mut self, msg: SnmpMessage<'a>, _direction: Direction) -> i32 { |
1880f694 | 141 | let mut tx = self.new_tx(); |
af7d245a PC |
142 | // in the message, version is encoded as 0 (version 1) or 1 (version 2) |
143 | if self.version != msg.version + 1 { | |
144 | SCLogDebug!("SNMP version mismatch: expected {}, received {}", self.version, msg.version+1); | |
145 | self.set_event_tx(&mut tx, SNMPEvent::VersionMismatch); | |
146 | } | |
1880f694 | 147 | self.add_pdu_info(&msg.pdu, &mut tx); |
ac3a20b6 | 148 | tx.community = Some(msg.community); |
1880f694 PC |
149 | self.transactions.push(tx); |
150 | 0 | |
2df840a8 PC |
151 | } |
152 | ||
baf30cfc | 153 | fn handle_snmp_v3(&mut self, msg: SnmpV3Message<'a>, _direction: Direction) -> i32 { |
1880f694 | 154 | let mut tx = self.new_tx(); |
af7d245a PC |
155 | if self.version != msg.version { |
156 | SCLogDebug!("SNMP version mismatch: expected {}, received {}", self.version, msg.version); | |
157 | self.set_event_tx(&mut tx, SNMPEvent::VersionMismatch); | |
158 | } | |
1880f694 PC |
159 | match msg.data { |
160 | ScopedPduData::Plaintext(pdu) => { | |
161 | self.add_pdu_info(&pdu.data, &mut tx); | |
2df840a8 | 162 | }, |
1880f694 PC |
163 | _ => { |
164 | tx.encrypted = true; | |
165 | } | |
166 | } | |
167 | match msg.security_params { | |
168 | SecurityParameters::USM(usm) => { | |
ac3a20b6 | 169 | tx.usm = Some(usm.msg_user_name); |
2df840a8 | 170 | }, |
1880f694 PC |
171 | _ => { |
172 | self.set_event_tx(&mut tx, SNMPEvent::UnknownSecurityModel); | |
173 | } | |
2df840a8 | 174 | } |
1880f694 PC |
175 | self.transactions.push(tx); |
176 | 0 | |
2df840a8 PC |
177 | } |
178 | ||
179 | /// Parse an SNMP request message | |
180 | /// | |
44d3f264 | 181 | /// Returns 0 if successful, or -1 on error |
baf30cfc | 182 | fn parse(&mut self, i: &'a [u8], direction: Direction) -> i32 { |
2df840a8 PC |
183 | if self.version == 0 { |
184 | match parse_pdu_enveloppe_version(i) { | |
185 | Ok((_,x)) => self.version = x, | |
186 | _ => (), | |
187 | } | |
188 | } | |
1880f694 PC |
189 | match parse_snmp_generic_message(i) { |
190 | Ok((_rem,SnmpGenericMessage::V1(msg))) | | |
191 | Ok((_rem,SnmpGenericMessage::V2(msg))) => self.handle_snmp_v12(msg, direction), | |
192 | Ok((_rem,SnmpGenericMessage::V3(msg))) => self.handle_snmp_v3(msg, direction), | |
ef575533 PA |
193 | Err(_e) => { |
194 | SCLogDebug!("parse_snmp failed: {:?}", _e); | |
1880f694 PC |
195 | self.set_event(SNMPEvent::MalformedData); |
196 | -1 | |
197 | }, | |
2df840a8 PC |
198 | } |
199 | } | |
200 | ||
201 | fn free(&mut self) { | |
202 | // All transactions are freed when the `transactions` object is freed. | |
203 | // But let's be explicit | |
204 | self.transactions.clear(); | |
205 | } | |
206 | ||
6b8517dc | 207 | fn new_tx(&mut self) -> SNMPTransaction<'a> { |
2df840a8 | 208 | self.tx_id += 1; |
aa608e0c | 209 | SNMPTransaction::new(self.version, self.tx_id) |
2df840a8 PC |
210 | } |
211 | ||
212 | fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SNMPTransaction> { | |
57b233f4 | 213 | self.transactions.iter().rev().find(|&tx| tx.id == tx_id + 1) |
2df840a8 PC |
214 | } |
215 | ||
216 | fn free_tx(&mut self, tx_id: u64) { | |
69cf5c9e | 217 | let tx = self.transactions.iter().position(|tx| tx.id == tx_id + 1); |
2df840a8 PC |
218 | debug_assert!(tx != None); |
219 | if let Some(idx) = tx { | |
220 | let _ = self.transactions.remove(idx); | |
221 | } | |
222 | } | |
223 | ||
224 | /// Set an event. The event is set on the most recent transaction. | |
225 | fn set_event(&mut self, event: SNMPEvent) { | |
226 | if let Some(tx) = self.transactions.last_mut() { | |
227 | let ev = event as u8; | |
228 | core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events, ev); | |
229 | } | |
230 | } | |
231 | ||
232 | /// Set an event on a specific transaction. | |
233 | fn set_event_tx(&self, tx: &mut SNMPTransaction, event: SNMPEvent) { | |
234 | core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events, event as u8); | |
235 | } | |
2df840a8 PC |
236 | } |
237 | ||
6b8517dc ET |
238 | impl<'a> SNMPTransaction<'a> { |
239 | pub fn new(version: u32, id: u64) -> SNMPTransaction<'a> { | |
2df840a8 | 240 | SNMPTransaction { |
aa608e0c | 241 | version, |
2df840a8 PC |
242 | info: None, |
243 | community: None, | |
244 | usm: None, | |
245 | encrypted: false, | |
246 | id: id, | |
2df840a8 | 247 | events: std::ptr::null_mut(), |
11e24345 | 248 | tx_data: applayer::AppLayerTxData::new(), |
2df840a8 PC |
249 | } |
250 | } | |
251 | ||
252 | fn free(&mut self) { | |
922a453d | 253 | if !self.events.is_null() { |
2df840a8 PC |
254 | core::sc_app_layer_decoder_events_free_events(&mut self.events); |
255 | } | |
256 | } | |
257 | } | |
258 | ||
6b8517dc | 259 | impl<'a> Drop for SNMPTransaction<'a> { |
2df840a8 PC |
260 | fn drop(&mut self) { |
261 | self.free(); | |
262 | } | |
263 | } | |
264 | ||
265 | ||
266 | ||
267 | ||
268 | ||
269 | ||
270 | /// Returns *mut SNMPState | |
271 | #[no_mangle] | |
547d6c2d | 272 | pub extern "C" fn rs_snmp_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void { |
2df840a8 PC |
273 | let state = SNMPState::new(); |
274 | let boxed = Box::new(state); | |
53413f2d | 275 | return Box::into_raw(boxed) as *mut _; |
2df840a8 PC |
276 | } |
277 | ||
278 | /// Params: | |
279 | /// - state: *mut SNMPState as void pointer | |
280 | #[no_mangle] | |
643864a8 | 281 | pub extern "C" fn rs_snmp_state_free(state: *mut std::os::raw::c_void) { |
53413f2d | 282 | let mut snmp_state = unsafe{ Box::from_raw(state as *mut SNMPState) }; |
2df840a8 PC |
283 | snmp_state.free(); |
284 | } | |
285 | ||
286 | #[no_mangle] | |
363b5f99 | 287 | pub unsafe extern "C" fn rs_snmp_parse_request(_flow: *const core::Flow, |
643864a8 JL |
288 | state: *mut std::os::raw::c_void, |
289 | _pstate: *mut std::os::raw::c_void, | |
c1b30fe9 | 290 | input: *const u8, |
2df840a8 | 291 | input_len: u32, |
643864a8 | 292 | _data: *const std::os::raw::c_void, |
44d3f264 | 293 | _flags: u8) -> AppLayerResult { |
2df840a8 PC |
294 | let buf = build_slice!(input,input_len as usize); |
295 | let state = cast_pointer!(state,SNMPState); | |
baf30cfc | 296 | state.parse(buf, Direction::ToServer).into() |
2df840a8 PC |
297 | } |
298 | ||
299 | #[no_mangle] | |
363b5f99 | 300 | pub unsafe extern "C" fn rs_snmp_parse_response(_flow: *const core::Flow, |
643864a8 JL |
301 | state: *mut std::os::raw::c_void, |
302 | _pstate: *mut std::os::raw::c_void, | |
c1b30fe9 | 303 | input: *const u8, |
2df840a8 | 304 | input_len: u32, |
643864a8 | 305 | _data: *const std::os::raw::c_void, |
44d3f264 | 306 | _flags: u8) -> AppLayerResult { |
2df840a8 PC |
307 | let buf = build_slice!(input,input_len as usize); |
308 | let state = cast_pointer!(state,SNMPState); | |
baf30cfc | 309 | state.parse(buf, Direction::ToClient).into() |
2df840a8 PC |
310 | } |
311 | ||
312 | #[no_mangle] | |
363b5f99 | 313 | pub unsafe extern "C" fn rs_snmp_state_get_tx(state: *mut std::os::raw::c_void, |
c1b30fe9 | 314 | tx_id: u64) |
643864a8 | 315 | -> *mut std::os::raw::c_void |
2df840a8 PC |
316 | { |
317 | let state = cast_pointer!(state,SNMPState); | |
318 | match state.get_tx_by_id(tx_id) { | |
53413f2d | 319 | Some(tx) => tx as *const _ as *mut _, |
2df840a8 PC |
320 | None => std::ptr::null_mut(), |
321 | } | |
322 | } | |
323 | ||
324 | #[no_mangle] | |
363b5f99 | 325 | pub unsafe extern "C" fn rs_snmp_state_get_tx_count(state: *mut std::os::raw::c_void) |
c1b30fe9 | 326 | -> u64 |
2df840a8 PC |
327 | { |
328 | let state = cast_pointer!(state,SNMPState); | |
329 | state.tx_id | |
330 | } | |
331 | ||
332 | #[no_mangle] | |
363b5f99 | 333 | pub unsafe extern "C" fn rs_snmp_state_tx_free(state: *mut std::os::raw::c_void, |
c1b30fe9 | 334 | tx_id: u64) |
2df840a8 PC |
335 | { |
336 | let state = cast_pointer!(state,SNMPState); | |
337 | state.free_tx(tx_id); | |
338 | } | |
339 | ||
2df840a8 | 340 | #[no_mangle] |
643864a8 | 341 | pub extern "C" fn rs_snmp_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void, |
c1b30fe9 | 342 | _direction: u8) |
643864a8 | 343 | -> std::os::raw::c_int |
2df840a8 PC |
344 | { |
345 | 1 | |
346 | } | |
347 | ||
2df840a8 | 348 | #[no_mangle] |
363b5f99 | 349 | pub unsafe extern "C" fn rs_snmp_state_get_events(tx: *mut std::os::raw::c_void) |
643864a8 | 350 | -> *mut core::AppLayerDecoderEvents |
2df840a8 | 351 | { |
643864a8 JL |
352 | let tx = cast_pointer!(tx, SNMPTransaction); |
353 | return tx.events; | |
2df840a8 PC |
354 | } |
355 | ||
2df840a8 PC |
356 | static mut ALPROTO_SNMP : AppProto = ALPROTO_UNKNOWN; |
357 | ||
358 | // Read PDU sequence and extract version, if similar to SNMP definition | |
359 | fn parse_pdu_enveloppe_version(i:&[u8]) -> IResult<&[u8],u32> { | |
360 | match parse_der_sequence(i) { | |
361 | Ok((_,x)) => { | |
362 | match x.content { | |
5b809f77 | 363 | BerObjectContent::Sequence(ref v) => { |
2df840a8 PC |
364 | if v.len() == 3 { |
365 | match v[0].as_u32() { | |
366 | Ok(0) => { return Ok((i,1)); }, // possibly SNMPv1 | |
367 | Ok(1) => { return Ok((i,2)); }, // possibly SNMPv2c | |
368 | _ => () | |
369 | } | |
370 | } else if v.len() == 4 && v[0].as_u32() == Ok(3) { | |
371 | return Ok((i,3)); // possibly SNMPv3 | |
372 | } | |
373 | }, | |
374 | _ => () | |
375 | }; | |
376 | Err(nom::Err::Error(error_position!(i, ErrorKind::Verify))) | |
377 | }, | |
378 | Err(nom::Err::Incomplete(i)) => Err(nom::Err::Incomplete(i)), | |
379 | Err(nom::Err::Failure(_)) | | |
380 | Err(nom::Err::Error(_)) => Err(nom::Err::Error(error_position!(i,ErrorKind::Verify))) | |
381 | } | |
382 | } | |
383 | ||
384 | #[no_mangle] | |
363b5f99 | 385 | pub unsafe extern "C" fn rs_snmp_probing_parser(_flow: *const Flow, |
2df840a8 | 386 | _direction: u8, |
c1b30fe9 | 387 | input:*const u8, |
2df840a8 PC |
388 | input_len: u32, |
389 | _rdir: *mut u8) -> AppProto { | |
390 | let slice = build_slice!(input,input_len as usize); | |
363b5f99 JI |
391 | let alproto = ALPROTO_SNMP; |
392 | if slice.len() < 4 { return ALPROTO_FAILED; } | |
2df840a8 PC |
393 | match parse_pdu_enveloppe_version(slice) { |
394 | Ok((_,_)) => alproto, | |
395 | Err(nom::Err::Incomplete(_)) => ALPROTO_UNKNOWN, | |
363b5f99 | 396 | _ => ALPROTO_FAILED, |
2df840a8 PC |
397 | } |
398 | } | |
399 | ||
11e24345 | 400 | export_tx_data_get!(rs_snmp_get_tx_data, SNMPTransaction); |
e2c846d0 | 401 | |
2df840a8 PC |
402 | const PARSER_NAME : &'static [u8] = b"snmp\0"; |
403 | ||
404 | #[no_mangle] | |
405 | pub unsafe extern "C" fn rs_register_snmp_parser() { | |
406 | let default_port = CString::new("161").unwrap(); | |
407 | let mut parser = RustParser { | |
6911cc01 JL |
408 | name : PARSER_NAME.as_ptr() as *const std::os::raw::c_char, |
409 | default_port : default_port.as_ptr(), | |
410 | ipproto : core::IPPROTO_UDP, | |
66632465 PA |
411 | probe_ts : Some(rs_snmp_probing_parser), |
412 | probe_tc : Some(rs_snmp_probing_parser), | |
6911cc01 JL |
413 | min_depth : 0, |
414 | max_depth : 16, | |
415 | state_new : rs_snmp_state_new, | |
416 | state_free : rs_snmp_state_free, | |
417 | tx_free : rs_snmp_state_tx_free, | |
418 | parse_ts : rs_snmp_parse_request, | |
419 | parse_tc : rs_snmp_parse_response, | |
420 | get_tx_count : rs_snmp_state_get_tx_count, | |
421 | get_tx : rs_snmp_state_get_tx, | |
efc9a7a3 VJ |
422 | tx_comp_st_ts : 1, |
423 | tx_comp_st_tc : 1, | |
6911cc01 | 424 | tx_get_progress : rs_snmp_tx_get_alstate_progress, |
6911cc01 | 425 | get_events : Some(rs_snmp_state_get_events), |
18448f6e JI |
426 | get_eventinfo : Some(SNMPEvent::get_event_info), |
427 | get_eventinfo_byid : Some(SNMPEvent::get_event_info_by_id), | |
6911cc01 JL |
428 | localstorage_new : None, |
429 | localstorage_free : None, | |
6911cc01 | 430 | get_files : None, |
3f2d2bc1 | 431 | get_tx_iterator : Some(applayer::state_get_tx_iterator::<SNMPState, SNMPTransaction>), |
c94a5e63 | 432 | get_tx_data : rs_snmp_get_tx_data, |
5665fc83 | 433 | apply_tx_config : None, |
fc7d59d9 | 434 | flags : APP_LAYER_PARSER_OPT_UNIDIR_TXS, |
4da0d9bd | 435 | truncate : None, |
2df840a8 PC |
436 | }; |
437 | let ip_proto_str = CString::new("udp").unwrap(); | |
438 | if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
439 | // port 161 | |
440 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); | |
441 | // store the allocated ID for the probe function | |
442 | ALPROTO_SNMP = alproto; | |
443 | if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
444 | let _ = AppLayerRegisterParser(&parser, alproto); | |
445 | } | |
2df840a8 PC |
446 | // port 162 |
447 | let default_port_traps = CString::new("162").unwrap(); | |
448 | parser.default_port = default_port_traps.as_ptr(); | |
449 | let _ = AppLayerRegisterProtocolDetection(&parser, 1); | |
450 | if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
451 | let _ = AppLayerRegisterParser(&parser, alproto); | |
452 | } | |
453 | } else { | |
6911cc01 | 454 | SCLogDebug!("Protocol detector and parser disabled for SNMP."); |
2df840a8 PC |
455 | } |
456 | } |