]>
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 | ||
11e24345 | 80 | tx_data: applayer::AppLayerTxData, |
2df840a8 PC |
81 | } |
82 | ||
3f2d2bc1 JI |
83 | impl<'a> Transaction for SNMPTransaction<'a> { |
84 | fn id(&self) -> u64 { | |
85 | self.id | |
86 | } | |
87 | } | |
2df840a8 | 88 | |
6b8517dc ET |
89 | impl<'a> SNMPState<'a> { |
90 | pub fn new() -> SNMPState<'a> { | |
2df840a8 PC |
91 | SNMPState{ |
92 | version: 0, | |
93 | transactions: Vec::new(), | |
94 | tx_id: 0, | |
95 | } | |
96 | } | |
97 | } | |
98 | ||
6b8517dc ET |
99 | impl<'a> Default for SNMPPduInfo<'a> { |
100 | fn default() -> SNMPPduInfo<'a> { | |
2df840a8 PC |
101 | SNMPPduInfo{ |
102 | pdu_type: PduType(0), | |
103 | err: ErrorStatus::NoError, | |
104 | trap_type: None, | |
105 | vars: Vec::new() | |
106 | } | |
107 | } | |
108 | } | |
109 | ||
3f2d2bc1 JI |
110 | impl<'a> State<SNMPTransaction<'a>> for SNMPState<'a> { |
111 | fn get_transactions(&self) -> &[SNMPTransaction<'a>] { | |
112 | &self.transactions | |
113 | } | |
114 | } | |
115 | ||
6b8517dc ET |
116 | impl<'a> SNMPState<'a> { |
117 | fn add_pdu_info(&mut self, pdu: &SnmpPdu<'a>, tx: &mut SNMPTransaction<'a>) { | |
2df840a8 PC |
118 | let mut pdu_info = SNMPPduInfo::default(); |
119 | pdu_info.pdu_type = pdu.pdu_type(); | |
60324740 | 120 | match *pdu { |
2df840a8 PC |
121 | SnmpPdu::Generic(ref pdu) => { |
122 | pdu_info.err = pdu.err; | |
123 | }, | |
124 | SnmpPdu::Bulk(_) => { | |
125 | }, | |
126 | SnmpPdu::TrapV1(ref t) => { | |
ac3a20b6 | 127 | pdu_info.trap_type = Some((t.generic_trap,t.enterprise.clone(),t.agent_addr)); |
2df840a8 PC |
128 | } |
129 | } | |
6b8517dc ET |
130 | |
131 | for var in pdu.vars_iter() { | |
132 | pdu_info.vars.push(var.oid.to_owned()); | |
2df840a8 PC |
133 | } |
134 | tx.info = Some(pdu_info); | |
135 | } | |
136 | ||
baf30cfc | 137 | fn handle_snmp_v12(&mut self, msg: SnmpMessage<'a>, _direction: Direction) -> i32 { |
1880f694 | 138 | let mut tx = self.new_tx(); |
af7d245a PC |
139 | // in the message, version is encoded as 0 (version 1) or 1 (version 2) |
140 | if self.version != msg.version + 1 { | |
141 | SCLogDebug!("SNMP version mismatch: expected {}, received {}", self.version, msg.version+1); | |
142 | self.set_event_tx(&mut tx, SNMPEvent::VersionMismatch); | |
143 | } | |
1880f694 | 144 | self.add_pdu_info(&msg.pdu, &mut tx); |
ac3a20b6 | 145 | tx.community = Some(msg.community); |
1880f694 PC |
146 | self.transactions.push(tx); |
147 | 0 | |
2df840a8 PC |
148 | } |
149 | ||
baf30cfc | 150 | fn handle_snmp_v3(&mut self, msg: SnmpV3Message<'a>, _direction: Direction) -> i32 { |
1880f694 | 151 | let mut tx = self.new_tx(); |
af7d245a PC |
152 | if self.version != msg.version { |
153 | SCLogDebug!("SNMP version mismatch: expected {}, received {}", self.version, msg.version); | |
154 | self.set_event_tx(&mut tx, SNMPEvent::VersionMismatch); | |
155 | } | |
1880f694 PC |
156 | match msg.data { |
157 | ScopedPduData::Plaintext(pdu) => { | |
158 | self.add_pdu_info(&pdu.data, &mut tx); | |
2df840a8 | 159 | }, |
1880f694 PC |
160 | _ => { |
161 | tx.encrypted = true; | |
162 | } | |
163 | } | |
164 | match msg.security_params { | |
165 | SecurityParameters::USM(usm) => { | |
ac3a20b6 | 166 | tx.usm = Some(usm.msg_user_name); |
2df840a8 | 167 | }, |
1880f694 PC |
168 | _ => { |
169 | self.set_event_tx(&mut tx, SNMPEvent::UnknownSecurityModel); | |
170 | } | |
2df840a8 | 171 | } |
1880f694 PC |
172 | self.transactions.push(tx); |
173 | 0 | |
2df840a8 PC |
174 | } |
175 | ||
176 | /// Parse an SNMP request message | |
177 | /// | |
44d3f264 | 178 | /// Returns 0 if successful, or -1 on error |
baf30cfc | 179 | fn parse(&mut self, i: &'a [u8], direction: Direction) -> i32 { |
2df840a8 PC |
180 | if self.version == 0 { |
181 | match parse_pdu_enveloppe_version(i) { | |
182 | Ok((_,x)) => self.version = x, | |
183 | _ => (), | |
184 | } | |
185 | } | |
1880f694 PC |
186 | match parse_snmp_generic_message(i) { |
187 | Ok((_rem,SnmpGenericMessage::V1(msg))) | | |
188 | Ok((_rem,SnmpGenericMessage::V2(msg))) => self.handle_snmp_v12(msg, direction), | |
189 | Ok((_rem,SnmpGenericMessage::V3(msg))) => self.handle_snmp_v3(msg, direction), | |
ef575533 PA |
190 | Err(_e) => { |
191 | SCLogDebug!("parse_snmp failed: {:?}", _e); | |
1880f694 PC |
192 | self.set_event(SNMPEvent::MalformedData); |
193 | -1 | |
194 | }, | |
2df840a8 PC |
195 | } |
196 | } | |
197 | ||
198 | fn free(&mut self) { | |
199 | // All transactions are freed when the `transactions` object is freed. | |
200 | // But let's be explicit | |
201 | self.transactions.clear(); | |
202 | } | |
203 | ||
6b8517dc | 204 | fn new_tx(&mut self) -> SNMPTransaction<'a> { |
2df840a8 | 205 | self.tx_id += 1; |
aa608e0c | 206 | SNMPTransaction::new(self.version, self.tx_id) |
2df840a8 PC |
207 | } |
208 | ||
209 | fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SNMPTransaction> { | |
57b233f4 | 210 | self.transactions.iter().rev().find(|&tx| tx.id == tx_id + 1) |
2df840a8 PC |
211 | } |
212 | ||
213 | fn free_tx(&mut self, tx_id: u64) { | |
69cf5c9e | 214 | let tx = self.transactions.iter().position(|tx| tx.id == tx_id + 1); |
2df840a8 PC |
215 | debug_assert!(tx != None); |
216 | if let Some(idx) = tx { | |
217 | let _ = self.transactions.remove(idx); | |
218 | } | |
219 | } | |
220 | ||
221 | /// Set an event. The event is set on the most recent transaction. | |
222 | fn set_event(&mut self, event: SNMPEvent) { | |
223 | if let Some(tx) = self.transactions.last_mut() { | |
7732efbe | 224 | tx.tx_data.set_event(event as u8); |
2df840a8 PC |
225 | } |
226 | } | |
227 | ||
228 | /// Set an event on a specific transaction. | |
229 | fn set_event_tx(&self, tx: &mut SNMPTransaction, event: SNMPEvent) { | |
7732efbe | 230 | tx.tx_data.set_event(event as u8); |
2df840a8 | 231 | } |
2df840a8 PC |
232 | } |
233 | ||
6b8517dc ET |
234 | impl<'a> SNMPTransaction<'a> { |
235 | pub fn new(version: u32, id: u64) -> SNMPTransaction<'a> { | |
2df840a8 | 236 | SNMPTransaction { |
aa608e0c | 237 | version, |
2df840a8 PC |
238 | info: None, |
239 | community: None, | |
240 | usm: None, | |
241 | encrypted: false, | |
242 | id: id, | |
11e24345 | 243 | tx_data: applayer::AppLayerTxData::new(), |
2df840a8 PC |
244 | } |
245 | } | |
2df840a8 PC |
246 | } |
247 | ||
2df840a8 PC |
248 | /// Returns *mut SNMPState |
249 | #[no_mangle] | |
547d6c2d | 250 | 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 |
251 | let state = SNMPState::new(); |
252 | let boxed = Box::new(state); | |
53413f2d | 253 | return Box::into_raw(boxed) as *mut _; |
2df840a8 PC |
254 | } |
255 | ||
256 | /// Params: | |
257 | /// - state: *mut SNMPState as void pointer | |
258 | #[no_mangle] | |
643864a8 | 259 | pub extern "C" fn rs_snmp_state_free(state: *mut std::os::raw::c_void) { |
53413f2d | 260 | let mut snmp_state = unsafe{ Box::from_raw(state as *mut SNMPState) }; |
2df840a8 PC |
261 | snmp_state.free(); |
262 | } | |
263 | ||
264 | #[no_mangle] | |
363b5f99 | 265 | pub unsafe extern "C" fn rs_snmp_parse_request(_flow: *const core::Flow, |
643864a8 JL |
266 | state: *mut std::os::raw::c_void, |
267 | _pstate: *mut std::os::raw::c_void, | |
c1b30fe9 | 268 | input: *const u8, |
2df840a8 | 269 | input_len: u32, |
643864a8 | 270 | _data: *const std::os::raw::c_void, |
44d3f264 | 271 | _flags: u8) -> AppLayerResult { |
2df840a8 PC |
272 | let buf = build_slice!(input,input_len as usize); |
273 | let state = cast_pointer!(state,SNMPState); | |
baf30cfc | 274 | state.parse(buf, Direction::ToServer).into() |
2df840a8 PC |
275 | } |
276 | ||
277 | #[no_mangle] | |
363b5f99 | 278 | pub unsafe extern "C" fn rs_snmp_parse_response(_flow: *const core::Flow, |
643864a8 JL |
279 | state: *mut std::os::raw::c_void, |
280 | _pstate: *mut std::os::raw::c_void, | |
c1b30fe9 | 281 | input: *const u8, |
2df840a8 | 282 | input_len: u32, |
643864a8 | 283 | _data: *const std::os::raw::c_void, |
44d3f264 | 284 | _flags: u8) -> AppLayerResult { |
2df840a8 PC |
285 | let buf = build_slice!(input,input_len as usize); |
286 | let state = cast_pointer!(state,SNMPState); | |
baf30cfc | 287 | state.parse(buf, Direction::ToClient).into() |
2df840a8 PC |
288 | } |
289 | ||
290 | #[no_mangle] | |
363b5f99 | 291 | pub unsafe extern "C" fn rs_snmp_state_get_tx(state: *mut std::os::raw::c_void, |
c1b30fe9 | 292 | tx_id: u64) |
643864a8 | 293 | -> *mut std::os::raw::c_void |
2df840a8 PC |
294 | { |
295 | let state = cast_pointer!(state,SNMPState); | |
296 | match state.get_tx_by_id(tx_id) { | |
53413f2d | 297 | Some(tx) => tx as *const _ as *mut _, |
2df840a8 PC |
298 | None => std::ptr::null_mut(), |
299 | } | |
300 | } | |
301 | ||
302 | #[no_mangle] | |
363b5f99 | 303 | pub unsafe extern "C" fn rs_snmp_state_get_tx_count(state: *mut std::os::raw::c_void) |
c1b30fe9 | 304 | -> u64 |
2df840a8 PC |
305 | { |
306 | let state = cast_pointer!(state,SNMPState); | |
307 | state.tx_id | |
308 | } | |
309 | ||
310 | #[no_mangle] | |
363b5f99 | 311 | pub unsafe extern "C" fn rs_snmp_state_tx_free(state: *mut std::os::raw::c_void, |
c1b30fe9 | 312 | tx_id: u64) |
2df840a8 PC |
313 | { |
314 | let state = cast_pointer!(state,SNMPState); | |
315 | state.free_tx(tx_id); | |
316 | } | |
317 | ||
2df840a8 | 318 | #[no_mangle] |
643864a8 | 319 | pub extern "C" fn rs_snmp_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void, |
c1b30fe9 | 320 | _direction: u8) |
643864a8 | 321 | -> std::os::raw::c_int |
2df840a8 PC |
322 | { |
323 | 1 | |
324 | } | |
325 | ||
2df840a8 PC |
326 | static mut ALPROTO_SNMP : AppProto = ALPROTO_UNKNOWN; |
327 | ||
328 | // Read PDU sequence and extract version, if similar to SNMP definition | |
329 | fn parse_pdu_enveloppe_version(i:&[u8]) -> IResult<&[u8],u32> { | |
330 | match parse_der_sequence(i) { | |
331 | Ok((_,x)) => { | |
332 | match x.content { | |
5b809f77 | 333 | BerObjectContent::Sequence(ref v) => { |
2df840a8 PC |
334 | if v.len() == 3 { |
335 | match v[0].as_u32() { | |
336 | Ok(0) => { return Ok((i,1)); }, // possibly SNMPv1 | |
337 | Ok(1) => { return Ok((i,2)); }, // possibly SNMPv2c | |
338 | _ => () | |
339 | } | |
340 | } else if v.len() == 4 && v[0].as_u32() == Ok(3) { | |
341 | return Ok((i,3)); // possibly SNMPv3 | |
342 | } | |
343 | }, | |
344 | _ => () | |
345 | }; | |
346 | Err(nom::Err::Error(error_position!(i, ErrorKind::Verify))) | |
347 | }, | |
348 | Err(nom::Err::Incomplete(i)) => Err(nom::Err::Incomplete(i)), | |
349 | Err(nom::Err::Failure(_)) | | |
350 | Err(nom::Err::Error(_)) => Err(nom::Err::Error(error_position!(i,ErrorKind::Verify))) | |
351 | } | |
352 | } | |
353 | ||
354 | #[no_mangle] | |
363b5f99 | 355 | pub unsafe extern "C" fn rs_snmp_probing_parser(_flow: *const Flow, |
2df840a8 | 356 | _direction: u8, |
c1b30fe9 | 357 | input:*const u8, |
2df840a8 PC |
358 | input_len: u32, |
359 | _rdir: *mut u8) -> AppProto { | |
360 | let slice = build_slice!(input,input_len as usize); | |
363b5f99 JI |
361 | let alproto = ALPROTO_SNMP; |
362 | if slice.len() < 4 { return ALPROTO_FAILED; } | |
2df840a8 PC |
363 | match parse_pdu_enveloppe_version(slice) { |
364 | Ok((_,_)) => alproto, | |
365 | Err(nom::Err::Incomplete(_)) => ALPROTO_UNKNOWN, | |
363b5f99 | 366 | _ => ALPROTO_FAILED, |
2df840a8 PC |
367 | } |
368 | } | |
369 | ||
11e24345 | 370 | export_tx_data_get!(rs_snmp_get_tx_data, SNMPTransaction); |
e2c846d0 | 371 | |
2df840a8 PC |
372 | const PARSER_NAME : &'static [u8] = b"snmp\0"; |
373 | ||
374 | #[no_mangle] | |
375 | pub unsafe extern "C" fn rs_register_snmp_parser() { | |
376 | let default_port = CString::new("161").unwrap(); | |
377 | let mut parser = RustParser { | |
6911cc01 JL |
378 | name : PARSER_NAME.as_ptr() as *const std::os::raw::c_char, |
379 | default_port : default_port.as_ptr(), | |
380 | ipproto : core::IPPROTO_UDP, | |
66632465 PA |
381 | probe_ts : Some(rs_snmp_probing_parser), |
382 | probe_tc : Some(rs_snmp_probing_parser), | |
6911cc01 JL |
383 | min_depth : 0, |
384 | max_depth : 16, | |
385 | state_new : rs_snmp_state_new, | |
386 | state_free : rs_snmp_state_free, | |
387 | tx_free : rs_snmp_state_tx_free, | |
388 | parse_ts : rs_snmp_parse_request, | |
389 | parse_tc : rs_snmp_parse_response, | |
390 | get_tx_count : rs_snmp_state_get_tx_count, | |
391 | get_tx : rs_snmp_state_get_tx, | |
efc9a7a3 VJ |
392 | tx_comp_st_ts : 1, |
393 | tx_comp_st_tc : 1, | |
6911cc01 | 394 | tx_get_progress : rs_snmp_tx_get_alstate_progress, |
18448f6e JI |
395 | get_eventinfo : Some(SNMPEvent::get_event_info), |
396 | get_eventinfo_byid : Some(SNMPEvent::get_event_info_by_id), | |
6911cc01 JL |
397 | localstorage_new : None, |
398 | localstorage_free : None, | |
6911cc01 | 399 | get_files : None, |
3f2d2bc1 | 400 | get_tx_iterator : Some(applayer::state_get_tx_iterator::<SNMPState, SNMPTransaction>), |
c94a5e63 | 401 | get_tx_data : rs_snmp_get_tx_data, |
5665fc83 | 402 | apply_tx_config : None, |
fc7d59d9 | 403 | flags : APP_LAYER_PARSER_OPT_UNIDIR_TXS, |
4da0d9bd | 404 | truncate : None, |
2df840a8 PC |
405 | }; |
406 | let ip_proto_str = CString::new("udp").unwrap(); | |
407 | if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
408 | // port 161 | |
409 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); | |
410 | // store the allocated ID for the probe function | |
411 | ALPROTO_SNMP = alproto; | |
412 | if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
413 | let _ = AppLayerRegisterParser(&parser, alproto); | |
414 | } | |
2df840a8 PC |
415 | // port 162 |
416 | let default_port_traps = CString::new("162").unwrap(); | |
417 | parser.default_port = default_port_traps.as_ptr(); | |
418 | let _ = AppLayerRegisterProtocolDetection(&parser, 1); | |
419 | if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
420 | let _ = AppLayerRegisterParser(&parser, alproto); | |
421 | } | |
422 | } else { | |
6911cc01 | 423 | SCLogDebug!("Protocol detector and parser disabled for SNMP."); |
2df840a8 PC |
424 | } |
425 | } |