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