/src/suricata7/rust/src/ike/ike.rs
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2020-2021 Open Information Security Foundation |
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 | | // Author: Frank Honza <frank.honza@dcso.de> |
19 | | |
20 | | extern crate ipsec_parser; |
21 | | use self::ipsec_parser::*; |
22 | | |
23 | | use crate::applayer; |
24 | | use crate::applayer::*; |
25 | | use crate::core::{self, *}; |
26 | | use crate::ike::ikev1::{handle_ikev1, IkeV1Header, Ikev1Container}; |
27 | | use crate::ike::ikev2::{handle_ikev2, Ikev2Container}; |
28 | | use crate::ike::parser::*; |
29 | | use nom7::Err; |
30 | | use std; |
31 | | use std::collections::HashSet; |
32 | | use std::ffi::CString; |
33 | | |
34 | 99 | #[derive(AppLayerEvent)] Unexecuted instantiation: <suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::from_id <suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::as_i32 Line | Count | Source | 34 | 44 | #[derive(AppLayerEvent)] |
Unexecuted instantiation: <suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::to_cstring <suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::from_string Line | Count | Source | 34 | 55 | #[derive(AppLayerEvent)] |
<suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::get_event_info Line | Count | Source | 34 | 55 | #[derive(AppLayerEvent)] |
Unexecuted instantiation: <suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::get_event_info_by_id |
35 | | pub enum IkeEvent { |
36 | | MalformedData, |
37 | | NoEncryption, |
38 | | WeakCryptoEnc, |
39 | | WeakCryptoPrf, |
40 | | WeakCryptoDh, |
41 | | WeakCryptoAuth, |
42 | | WeakCryptoNoDh, |
43 | | WeakCryptoNoAuth, |
44 | | InvalidProposal, |
45 | | UnknownProposal, |
46 | | PayloadExtraData, |
47 | | MultipleServerProposal, |
48 | | } |
49 | | |
50 | | pub struct IkeHeaderWrapper { |
51 | | pub spi_initiator: String, |
52 | | pub spi_responder: String, |
53 | | pub maj_ver: u8, |
54 | | pub min_ver: u8, |
55 | | pub msg_id: u32, |
56 | | pub flags: u8, |
57 | | pub ikev1_transforms: Vec<Vec<SaAttribute>>, |
58 | | pub ikev2_transforms: Vec<IkeV2Transform>, |
59 | | pub ikev1_header: IkeV1Header, |
60 | | pub ikev2_header: IkeV2Header, |
61 | | } |
62 | | |
63 | | impl Default for IkeHeaderWrapper { |
64 | 364k | fn default() -> Self { |
65 | 364k | Self::new() |
66 | 364k | } |
67 | | } |
68 | | |
69 | | impl IkeHeaderWrapper { |
70 | 364k | pub fn new() -> Self { |
71 | 364k | Self { |
72 | 364k | spi_initiator: String::new(), |
73 | 364k | spi_responder: String::new(), |
74 | 364k | maj_ver: 0, |
75 | 364k | min_ver: 0, |
76 | 364k | msg_id: 0, |
77 | 364k | flags: 0, |
78 | 364k | ikev1_transforms: Vec::new(), |
79 | 364k | ikev2_transforms: Vec::new(), |
80 | 364k | ikev1_header: IkeV1Header::default(), |
81 | 364k | ikev2_header: IkeV2Header { |
82 | 364k | init_spi: 0, |
83 | 364k | resp_spi: 0, |
84 | 364k | next_payload: IkePayloadType::NoNextPayload, |
85 | 364k | maj_ver: 0, |
86 | 364k | min_ver: 0, |
87 | 364k | exch_type: IkeExchangeType(0), |
88 | 364k | flags: 0, |
89 | 364k | msg_id: 0, |
90 | 364k | length: 0, |
91 | 364k | }, |
92 | 364k | } |
93 | 364k | } |
94 | | } |
95 | | |
96 | | #[derive(Default)] |
97 | | pub struct IkePayloadWrapper { |
98 | | pub ikev1_payload_types: Option<HashSet<u8>>, |
99 | | pub ikev2_payload_types: Vec<IkePayloadType>, |
100 | | } |
101 | | |
102 | | #[derive(Default)] |
103 | | pub struct IKETransaction { |
104 | | tx_id: u64, |
105 | | |
106 | | pub ike_version: u8, |
107 | | pub direction: Direction, |
108 | | pub hdr: IkeHeaderWrapper, |
109 | | pub payload_types: IkePayloadWrapper, |
110 | | pub notify_types: Vec<NotifyType>, |
111 | | |
112 | | /// errors seen during exchange |
113 | | pub errors: u32, |
114 | | |
115 | | logged: LoggerFlags, |
116 | | pub tx_data: applayer::AppLayerTxData, |
117 | | } |
118 | | |
119 | | impl Transaction for IKETransaction { |
120 | 565k | fn id(&self) -> u64 { |
121 | 565k | self.tx_id |
122 | 565k | } |
123 | | } |
124 | | |
125 | | impl IKETransaction { |
126 | 364k | pub fn new(direction: Direction) -> Self { |
127 | 364k | Self { |
128 | 364k | direction, |
129 | 364k | tx_data: applayer::AppLayerTxData::for_direction(direction), |
130 | 364k | ..Default::default() |
131 | 364k | } |
132 | 364k | } |
133 | | |
134 | | /// Set an event. |
135 | 206k | pub fn set_event(&mut self, event: IkeEvent) { |
136 | 206k | self.tx_data.set_event(event as u8); |
137 | 206k | } |
138 | | } |
139 | | |
140 | | #[derive(Default)] |
141 | | pub struct IKEState { |
142 | | state_data: AppLayerStateData, |
143 | | tx_id: u64, |
144 | | pub transactions: Vec<IKETransaction>, |
145 | | |
146 | | pub ikev1_container: Ikev1Container, |
147 | | pub ikev2_container: Ikev2Container, |
148 | | } |
149 | | |
150 | | impl State<IKETransaction> for IKEState { |
151 | 375k | fn get_transaction_count(&self) -> usize { |
152 | 375k | self.transactions.len() |
153 | 375k | } |
154 | | |
155 | 282k | fn get_transaction_by_index(&self, index: usize) -> Option<&IKETransaction> { |
156 | 282k | self.transactions.get(index) |
157 | 282k | } |
158 | | } |
159 | | |
160 | | impl IKEState { |
161 | | // Free a transaction by ID. |
162 | 272k | fn free_tx(&mut self, tx_id: u64) { |
163 | 272k | let tx = self |
164 | 272k | .transactions |
165 | 272k | .iter() |
166 | 272k | .position(|tx| tx.tx_id == tx_id + 1); |
167 | 272k | debug_assert!(tx.is_some()); |
168 | 272k | if let Some(idx) = tx { |
169 | 272k | let _ = self.transactions.remove(idx); |
170 | 272k | } |
171 | 272k | } |
172 | | |
173 | 450 | pub fn get_tx(&mut self, tx_id: u64) -> Option<&mut IKETransaction> { |
174 | 450 | self.transactions.iter_mut().find(|tx| tx.tx_id == tx_id + 1) |
175 | 450 | } |
176 | | |
177 | 364k | pub fn new_tx(&mut self, direction: Direction) -> IKETransaction { |
178 | 364k | let mut tx = IKETransaction::new(direction); |
179 | 364k | self.tx_id += 1; |
180 | 364k | tx.tx_id = self.tx_id; |
181 | 364k | return tx; |
182 | 364k | } |
183 | | |
184 | | /// Set an event. The event is set on the most recent transaction. |
185 | 77.9k | pub fn set_event(&mut self, event: IkeEvent) { |
186 | 77.9k | if let Some(tx) = self.transactions.last_mut() { |
187 | 0 | tx.set_event(event); |
188 | 77.9k | } else { |
189 | 77.9k | SCLogDebug!( |
190 | 77.9k | "IKE: trying to set event {} on non-existing transaction", |
191 | 77.9k | event as u32 |
192 | 77.9k | ); |
193 | 77.9k | } |
194 | 77.9k | } |
195 | | |
196 | 366k | fn handle_input(&mut self, input: &[u8], direction: Direction) -> AppLayerResult { |
197 | 366k | // We're not interested in empty requests. |
198 | 366k | if input.is_empty() { |
199 | 0 | return AppLayerResult::ok(); |
200 | 366k | } |
201 | 366k | |
202 | 366k | let mut current = input; |
203 | 366k | match parse_isakmp_header(current) { |
204 | 366k | Ok((rem, isakmp_header)) => { |
205 | 366k | current = rem; |
206 | 366k | |
207 | 366k | if isakmp_header.maj_ver != 1 && isakmp_header.maj_ver != 2 { |
208 | | SCLogDebug!("Unsupported ISAKMP major_version"); |
209 | 2.34k | return AppLayerResult::err(); |
210 | 364k | } |
211 | 364k | |
212 | 364k | if isakmp_header.maj_ver == 1 { |
213 | 195k | handle_ikev1(self, current, isakmp_header, direction); |
214 | 195k | } else if isakmp_header.maj_ver == 2 { |
215 | 168k | handle_ikev2(self, current, isakmp_header, direction); |
216 | 168k | } else { |
217 | 0 | return AppLayerResult::err(); |
218 | | } |
219 | 364k | return AppLayerResult::ok(); // todo either remove outer loop or check header length-field if we have completely read everything |
220 | | } |
221 | | Err(Err::Incomplete(_)) => { |
222 | | SCLogDebug!("Insufficient data while parsing IKE"); |
223 | 489 | return AppLayerResult::err(); |
224 | | } |
225 | | Err(_) => { |
226 | | SCLogDebug!("Error while parsing IKE packet"); |
227 | 0 | return AppLayerResult::err(); |
228 | | } |
229 | | } |
230 | 366k | } |
231 | | } |
232 | | |
233 | | /// Probe to see if this input looks like a request or response. |
234 | 827 | fn probe(input: &[u8], direction: Direction, rdir: *mut u8) -> bool { |
235 | 827 | match parse_isakmp_header(input) { |
236 | 827 | Ok((_, isakmp_header)) => { |
237 | 827 | if isakmp_header.maj_ver == 1 { |
238 | 530 | if isakmp_header.resp_spi == 0 && direction != Direction::ToServer { |
239 | 1 | unsafe { |
240 | 1 | *rdir = Direction::ToServer.into(); |
241 | 1 | } |
242 | 529 | } |
243 | 530 | return true; |
244 | 297 | } else if isakmp_header.maj_ver == 2 { |
245 | 200 | if isakmp_header.min_ver != 0 { |
246 | | SCLogDebug!( |
247 | | "ipsec_probe: could be ipsec, but with unsupported/invalid version {}.{}", |
248 | | isakmp_header.maj_ver, |
249 | | isakmp_header.min_ver |
250 | | ); |
251 | 59 | return false; |
252 | 141 | } |
253 | 141 | if isakmp_header.exch_type < 34 || isakmp_header.exch_type > 37 { |
254 | | SCLogDebug!("ipsec_probe: could be ipsec, but with unsupported/invalid exchange type {}", |
255 | | isakmp_header.exch_type); |
256 | 64 | return false; |
257 | 77 | } |
258 | 77 | if isakmp_header.length as usize != input.len() { |
259 | | SCLogDebug!("ipsec_probe: could be ipsec, but length does not match"); |
260 | 10 | return false; |
261 | 67 | } |
262 | 67 | |
263 | 67 | if isakmp_header.resp_spi == 0 && direction != Direction::ToServer { |
264 | 1 | unsafe { |
265 | 1 | *rdir = Direction::ToServer.into(); |
266 | 1 | } |
267 | 66 | } |
268 | 67 | return true; |
269 | 97 | } |
270 | 97 | |
271 | 97 | return false; |
272 | | } |
273 | 0 | Err(_) => return false, |
274 | | } |
275 | 827 | } |
276 | | |
277 | | // C exports. |
278 | | |
279 | | /// C entry point for a probing parser. |
280 | | #[no_mangle] |
281 | 836 | pub unsafe extern "C" fn rs_ike_probing_parser( |
282 | 836 | _flow: *const Flow, direction: u8, input: *const u8, input_len: u32, rdir: *mut u8, |
283 | 836 | ) -> AppProto { |
284 | 836 | if input_len < 28 { |
285 | | // at least the ISAKMP_HEADER must be there, not ALPROTO_UNKNOWN because over UDP |
286 | 9 | return ALPROTO_FAILED; |
287 | 827 | } |
288 | 827 | |
289 | 827 | if !input.is_null() { |
290 | 827 | let slice = build_slice!(input, input_len as usize); |
291 | 827 | if probe(slice, direction.into(), rdir) { |
292 | 597 | return ALPROTO_IKE; |
293 | 230 | } |
294 | 0 | } |
295 | 230 | return ALPROTO_FAILED; |
296 | 836 | } |
297 | | |
298 | | #[no_mangle] |
299 | 5.92k | pub extern "C" fn rs_ike_state_new( |
300 | 5.92k | _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto, |
301 | 5.92k | ) -> *mut std::os::raw::c_void { |
302 | 5.92k | let state = IKEState::default(); |
303 | 5.92k | let boxed = Box::new(state); |
304 | 5.92k | return Box::into_raw(boxed) as *mut _; |
305 | 5.92k | } |
306 | | |
307 | | #[no_mangle] |
308 | 5.92k | pub unsafe extern "C" fn rs_ike_state_free(state: *mut std::os::raw::c_void) { |
309 | 5.92k | // Just unbox... |
310 | 5.92k | std::mem::drop(Box::from_raw(state as *mut IKEState)); |
311 | 5.92k | } |
312 | | |
313 | | #[no_mangle] |
314 | 272k | pub unsafe extern "C" fn rs_ike_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) { |
315 | 272k | let state = cast_pointer!(state, IKEState); |
316 | 272k | state.free_tx(tx_id); |
317 | 272k | } |
318 | | |
319 | | #[no_mangle] |
320 | 189k | pub unsafe extern "C" fn rs_ike_parse_request( |
321 | 189k | _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, |
322 | 189k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
323 | 189k | ) -> AppLayerResult { |
324 | 189k | let state = cast_pointer!(state, IKEState); |
325 | 189k | return state.handle_input(stream_slice.as_slice(), Direction::ToServer); |
326 | 189k | } |
327 | | |
328 | | #[no_mangle] |
329 | 177k | pub unsafe extern "C" fn rs_ike_parse_response( |
330 | 177k | _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, |
331 | 177k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
332 | 177k | ) -> AppLayerResult { |
333 | 177k | let state = cast_pointer!(state, IKEState); |
334 | 177k | return state.handle_input(stream_slice.as_slice(), Direction::ToClient); |
335 | 177k | } |
336 | | |
337 | | #[no_mangle] |
338 | 450 | pub unsafe extern "C" fn rs_ike_state_get_tx( |
339 | 450 | state: *mut std::os::raw::c_void, tx_id: u64, |
340 | 450 | ) -> *mut std::os::raw::c_void { |
341 | 450 | let state = cast_pointer!(state, IKEState); |
342 | 450 | match state.get_tx(tx_id) { |
343 | 450 | Some(tx) => { |
344 | 450 | return tx as *const _ as *mut _; |
345 | | } |
346 | | None => { |
347 | 0 | return std::ptr::null_mut(); |
348 | | } |
349 | | } |
350 | 450 | } |
351 | | |
352 | | #[no_mangle] |
353 | 1.10M | pub unsafe extern "C" fn rs_ike_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 { |
354 | 1.10M | let state = cast_pointer!(state, IKEState); |
355 | 1.10M | return state.tx_id; |
356 | 1.10M | } |
357 | | |
358 | | #[no_mangle] |
359 | 0 | pub extern "C" fn rs_ike_state_progress_completion_status(_direction: u8) -> std::os::raw::c_int { |
360 | 0 | // This parser uses 1 to signal transaction completion status. |
361 | 0 | return 1; |
362 | 0 | } |
363 | | |
364 | | #[no_mangle] |
365 | 563k | pub extern "C" fn rs_ike_tx_get_alstate_progress( |
366 | 563k | _tx: *mut std::os::raw::c_void, _direction: u8, |
367 | 563k | ) -> std::os::raw::c_int { |
368 | 563k | return 1; |
369 | 563k | } |
370 | | |
371 | | #[no_mangle] |
372 | 0 | pub unsafe extern "C" fn rs_ike_tx_get_logged( |
373 | 0 | _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void, |
374 | 0 | ) -> u32 { |
375 | 0 | let tx = cast_pointer!(tx, IKETransaction); |
376 | 0 | return tx.logged.get(); |
377 | 0 | } |
378 | | |
379 | | #[no_mangle] |
380 | 0 | pub unsafe extern "C" fn rs_ike_tx_set_logged( |
381 | 0 | _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void, logged: u32, |
382 | 0 | ) { |
383 | 0 | let tx = cast_pointer!(tx, IKETransaction); |
384 | 0 | tx.logged.set(logged); |
385 | 0 | } |
386 | | |
387 | | static mut ALPROTO_IKE: AppProto = ALPROTO_UNKNOWN; |
388 | | |
389 | | // Parser name as a C style string. |
390 | | const PARSER_NAME: &[u8] = b"ike\0"; |
391 | | const PARSER_ALIAS: &[u8] = b"ikev2\0"; |
392 | | |
393 | | export_tx_data_get!(rs_ike_get_tx_data, IKETransaction); |
394 | | export_state_data_get!(rs_ike_get_state_data, IKEState); |
395 | | |
396 | | #[no_mangle] |
397 | 34 | pub unsafe extern "C" fn rs_ike_register_parser() { |
398 | 34 | let default_port = CString::new("500").unwrap(); |
399 | 34 | let parser = RustParser { |
400 | 34 | name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, |
401 | 34 | default_port: default_port.as_ptr(), |
402 | 34 | ipproto: core::IPPROTO_UDP, |
403 | 34 | probe_ts: Some(rs_ike_probing_parser), |
404 | 34 | probe_tc: Some(rs_ike_probing_parser), |
405 | 34 | min_depth: 0, |
406 | 34 | max_depth: 16, |
407 | 34 | state_new: rs_ike_state_new, |
408 | 34 | state_free: rs_ike_state_free, |
409 | 34 | tx_free: rs_ike_state_tx_free, |
410 | 34 | parse_ts: rs_ike_parse_request, |
411 | 34 | parse_tc: rs_ike_parse_response, |
412 | 34 | get_tx_count: rs_ike_state_get_tx_count, |
413 | 34 | get_tx: rs_ike_state_get_tx, |
414 | 34 | tx_comp_st_ts: 1, |
415 | 34 | tx_comp_st_tc: 1, |
416 | 34 | tx_get_progress: rs_ike_tx_get_alstate_progress, |
417 | 34 | get_eventinfo: Some(IkeEvent::get_event_info), |
418 | 34 | get_eventinfo_byid: Some(IkeEvent::get_event_info_by_id), |
419 | 34 | localstorage_new: None, |
420 | 34 | localstorage_free: None, |
421 | 34 | get_tx_files: None, |
422 | 34 | get_tx_iterator: Some(applayer::state_get_tx_iterator::<IKEState, IKETransaction>), |
423 | 34 | get_tx_data: rs_ike_get_tx_data, |
424 | 34 | get_state_data: rs_ike_get_state_data, |
425 | 34 | apply_tx_config: None, |
426 | 34 | flags: 0, |
427 | 34 | truncate: None, |
428 | 34 | get_frame_id_by_name: None, |
429 | 34 | get_frame_name_by_id: None, |
430 | 34 | }; |
431 | 34 | |
432 | 34 | let ip_proto_str = CString::new("udp").unwrap(); |
433 | 34 | |
434 | 34 | if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
435 | 34 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
436 | 34 | ALPROTO_IKE = alproto; |
437 | 34 | if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
438 | 34 | let _ = AppLayerRegisterParser(&parser, alproto); |
439 | 34 | } |
440 | | |
441 | 34 | AppLayerRegisterParserAlias( |
442 | 34 | PARSER_NAME.as_ptr() as *const std::os::raw::c_char, |
443 | 34 | PARSER_ALIAS.as_ptr() as *const std::os::raw::c_char, |
444 | 34 | ); |
445 | | SCLogDebug!("Rust IKE parser registered."); |
446 | 0 | } else { |
447 | 0 | SCLogDebug!("Protocol detector and parser disabled for IKE."); |
448 | 0 | } |
449 | 34 | } |