]>
Commit | Line | Data |
---|---|---|
c99b9462 PC |
1 | /* Copyright (C) 2017-2018 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 | // written by Pierre Chifflier <chifflier@wzdftpd.net> | |
19 | ||
20 | use ikev2::ipsec_parser::*; | |
21 | use ikev2::state::IKEV2ConnectionState; | |
22 | use core; | |
23 | use core::{AppProto,Flow,ALPROTO_UNKNOWN,ALPROTO_FAILED,STREAM_TOSERVER,STREAM_TOCLIENT}; | |
24 | use applayer; | |
25 | use parser::*; | |
26 | use libc; | |
27 | use std; | |
28 | use std::ffi::{CStr,CString}; | |
29 | ||
30 | use log::*; | |
31 | ||
13b73997 | 32 | use nom; |
c99b9462 PC |
33 | |
34 | #[repr(u32)] | |
35 | pub enum IKEV2Event { | |
36 | MalformedData = 0, | |
37 | NoEncryption, | |
38 | WeakCryptoEnc, | |
39 | WeakCryptoPRF, | |
40 | WeakCryptoDH, | |
41 | WeakCryptoAuth, | |
42 | WeakCryptoNoDH, | |
43 | WeakCryptoNoAuth, | |
44 | InvalidProposal, | |
45 | UnknownProposal, | |
46 | } | |
47 | ||
48 | pub struct IKEV2State { | |
49 | /// List of transactions for this session | |
50 | transactions: Vec<IKEV2Transaction>, | |
51 | ||
c99b9462 PC |
52 | /// tx counter for assigning incrementing id's to tx's |
53 | tx_id: u64, | |
54 | ||
55 | /// The connection state | |
56 | connection_state: IKEV2ConnectionState, | |
57 | ||
58 | /// The transforms proposed by the initiator | |
59 | pub client_transforms : Vec<Vec<IkeV2Transform>>, | |
60 | ||
61 | /// The transforms selected by the responder | |
62 | pub server_transforms : Vec<Vec<IkeV2Transform>>, | |
63 | ||
64 | /// The encryption algorithm selected by the responder | |
65 | pub alg_enc: IkeTransformEncType, | |
66 | /// The authentication algorithm selected by the responder | |
67 | pub alg_auth: IkeTransformAuthType, | |
68 | /// The PRF algorithm selected by the responder | |
69 | pub alg_prf: IkeTransformPRFType, | |
70 | /// The Diffie-Hellman algorithm selected by the responder | |
71 | pub alg_dh: IkeTransformDHType, | |
72 | /// The extended sequence numbers parameter selected by the responder | |
73 | pub alg_esn: IkeTransformESNType, | |
74 | ||
75 | /// The Diffie-Hellman group from the server KE message, if present. | |
76 | pub dh_group: IkeTransformDHType, | |
77 | ||
78 | } | |
79 | ||
80 | #[derive(Debug)] | |
81 | pub struct IKEV2Transaction { | |
82 | /// The IKEV2 reference ID | |
83 | pub xid: u64, | |
84 | ||
85 | pub hdr: IkeV2Header, | |
86 | ||
d9434628 PC |
87 | pub payload_types: Vec<IkePayloadType>, |
88 | pub notify_types: Vec<NotifyType>, | |
89 | ||
90 | /// IKEv2 errors seen during exchange | |
91 | pub errors: u32, | |
92 | ||
c99b9462 PC |
93 | /// The internal transaction id |
94 | id: u64, | |
95 | ||
96 | /// The detection engine state, if present | |
97 | de_state: Option<*mut core::DetectEngineState>, | |
98 | ||
99 | /// The events associated with this transaction | |
100 | events: *mut core::AppLayerDecoderEvents, | |
101 | ||
102 | logged: applayer::LoggerFlags, | |
103 | } | |
104 | ||
105 | ||
106 | ||
107 | impl IKEV2State { | |
108 | pub fn new() -> IKEV2State { | |
109 | IKEV2State{ | |
110 | transactions: Vec::new(), | |
c99b9462 PC |
111 | tx_id: 0, |
112 | connection_state: IKEV2ConnectionState::Init, | |
113 | dh_group: IkeTransformDHType::None, | |
114 | client_transforms: Vec::new(), | |
115 | server_transforms: Vec::new(), | |
116 | alg_enc: IkeTransformEncType::ENCR_NULL, | |
117 | alg_auth: IkeTransformAuthType::NONE, | |
118 | alg_prf: IkeTransformPRFType::PRF_NULL, | |
119 | alg_dh: IkeTransformDHType::None, | |
120 | alg_esn: IkeTransformESNType::NoESN, | |
121 | } | |
122 | } | |
123 | } | |
124 | ||
125 | impl IKEV2State { | |
126 | /// Parse an IKEV2 request message | |
127 | /// | |
128 | /// Returns The number of messages parsed, or -1 on error | |
ae10a92b | 129 | fn parse(&mut self, i: &[u8], direction: u8) -> i32 { |
c99b9462 | 130 | match parse_ikev2_header(i) { |
13b73997 | 131 | Ok((rem,ref hdr)) => { |
c99b9462 PC |
132 | if rem.len() == 0 && hdr.length == 28 { |
133 | return 1; | |
134 | } | |
135 | // Rule 0: check version | |
136 | if hdr.maj_ver != 2 || hdr.min_ver != 0 { | |
bf66948a PC |
137 | self.set_event(IKEV2Event::MalformedData); |
138 | return -1; | |
c99b9462 PC |
139 | } |
140 | if hdr.init_spi == 0 { | |
c99b9462 PC |
141 | self.set_event(IKEV2Event::MalformedData); |
142 | return -1; | |
143 | } | |
144 | // only analyse IKE_SA, other payloads are encrypted | |
145 | if hdr.exch_type != IkeExchangeType::IKE_SA_INIT { | |
146 | return 0; | |
147 | } | |
148 | let mut tx = self.new_tx(); | |
149 | // use init_spi as transaction identifier | |
150 | tx.xid = hdr.init_spi; | |
151 | tx.hdr = (*hdr).clone(); | |
152 | match parse_ikev2_payload_list(rem,hdr.next_payload) { | |
13b73997 | 153 | Ok((_,Ok(ref p))) => { |
c99b9462 | 154 | for payload in p { |
d9434628 | 155 | tx.payload_types.push(payload.hdr.next_payload_type); |
c99b9462 PC |
156 | match payload.content { |
157 | IkeV2PayloadContent::Dummy => (), | |
158 | IkeV2PayloadContent::SA(ref prop) => { | |
159 | // if hdr.flags & IKEV2_FLAG_INITIATOR != 0 { | |
160 | self.add_proposals(prop, direction); | |
161 | // } | |
162 | }, | |
163 | IkeV2PayloadContent::KE(ref kex) => { | |
164 | SCLogDebug!("KEX {:?}", kex.dh_group); | |
165 | if direction == STREAM_TOCLIENT { | |
166 | self.dh_group = kex.dh_group; | |
167 | } | |
168 | }, | |
169 | IkeV2PayloadContent::Nonce(ref n) => { | |
170 | SCLogDebug!("Nonce: {:?}", n); | |
171 | }, | |
172 | IkeV2PayloadContent::Notify(ref n) => { | |
173 | SCLogDebug!("Notify: {:?}", n); | |
d9434628 PC |
174 | if n.notify_type.is_error() { |
175 | tx.errors += 1; | |
176 | } | |
177 | tx.notify_types.push(n.notify_type); | |
c99b9462 PC |
178 | }, |
179 | // XXX CertificateRequest | |
180 | // XXX Certificate | |
181 | // XXX Authentication | |
182 | // XXX TSi | |
183 | // XXX TSr | |
184 | // XXX IDr | |
185 | _ => { | |
bf66948a | 186 | SCLogDebug!("Unknown payload content {:?}", payload.content); |
c99b9462 PC |
187 | }, |
188 | } | |
189 | self.connection_state = self.connection_state.advance(payload); | |
190 | }; | |
191 | }, | |
bf66948a | 192 | e => { SCLogDebug!("parse_ikev2_payload_with_type: {:?}",e); () }, |
c99b9462 PC |
193 | } |
194 | self.transactions.push(tx); | |
195 | 1 | |
196 | }, | |
13b73997 | 197 | Err(nom::Err::Incomplete(_)) => { |
c99b9462 PC |
198 | SCLogDebug!("Insufficient data while parsing IKEV2 data"); |
199 | self.set_event(IKEV2Event::MalformedData); | |
200 | -1 | |
201 | }, | |
13b73997 | 202 | Err(_) => { |
c99b9462 PC |
203 | SCLogDebug!("Error while parsing IKEV2 data"); |
204 | self.set_event(IKEV2Event::MalformedData); | |
205 | -1 | |
206 | }, | |
207 | } | |
208 | } | |
209 | ||
210 | fn free(&mut self) { | |
211 | // All transactions are freed when the `transactions` object is freed. | |
212 | // But let's be explicit | |
213 | self.transactions.clear(); | |
214 | } | |
215 | ||
216 | fn new_tx(&mut self) -> IKEV2Transaction { | |
217 | self.tx_id += 1; | |
218 | IKEV2Transaction::new(self.tx_id) | |
219 | } | |
220 | ||
221 | fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&IKEV2Transaction> { | |
222 | self.transactions.iter().find(|&tx| tx.id == tx_id + 1) | |
223 | } | |
224 | ||
225 | fn free_tx(&mut self, tx_id: u64) { | |
226 | let tx = self.transactions.iter().position(|ref tx| tx.id == tx_id + 1); | |
227 | debug_assert!(tx != None); | |
228 | if let Some(idx) = tx { | |
229 | let _ = self.transactions.remove(idx); | |
230 | } | |
231 | } | |
232 | ||
233 | /// Set an event. The event is set on the most recent transaction. | |
234 | fn set_event(&mut self, event: IKEV2Event) { | |
235 | if let Some(tx) = self.transactions.last_mut() { | |
236 | let ev = event as u8; | |
237 | core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events, ev); | |
c99b9462 PC |
238 | } |
239 | } | |
240 | ||
241 | fn add_proposals(&mut self, prop: &Vec<IkeV2Proposal>, direction: u8) { | |
242 | for ref p in prop { | |
243 | let transforms : Vec<IkeV2Transform> = p.transforms.iter().map(|x| x.into()).collect(); | |
244 | // Rule 1: warn on weak or unknown transforms | |
245 | for xform in &transforms { | |
246 | match *xform { | |
247 | IkeV2Transform::Encryption(ref enc) => { | |
248 | match *enc { | |
249 | IkeTransformEncType::ENCR_DES_IV64 | | |
250 | IkeTransformEncType::ENCR_DES | | |
251 | IkeTransformEncType::ENCR_3DES | | |
252 | IkeTransformEncType::ENCR_RC5 | | |
253 | IkeTransformEncType::ENCR_IDEA | | |
254 | IkeTransformEncType::ENCR_CAST | | |
255 | IkeTransformEncType::ENCR_BLOWFISH | | |
256 | IkeTransformEncType::ENCR_3IDEA | | |
257 | IkeTransformEncType::ENCR_DES_IV32 | | |
258 | IkeTransformEncType::ENCR_NULL => { | |
259 | SCLogDebug!("Weak Encryption: {:?}", enc); | |
260 | // XXX send event only if direction == STREAM_TOCLIENT ? | |
261 | self.set_event(IKEV2Event::WeakCryptoEnc); | |
262 | }, | |
263 | _ => (), | |
264 | } | |
265 | }, | |
266 | IkeV2Transform::PRF(ref prf) => { | |
267 | match *prf { | |
268 | IkeTransformPRFType::PRF_NULL => { | |
269 | SCLogDebug!("'Null' PRF transform proposed"); | |
270 | self.set_event(IKEV2Event::InvalidProposal); | |
271 | }, | |
272 | IkeTransformPRFType::PRF_HMAC_MD5 | | |
273 | IkeTransformPRFType::PRF_HMAC_SHA1 => { | |
274 | SCLogDebug!("Weak PRF: {:?}", prf); | |
275 | self.set_event(IKEV2Event::WeakCryptoPRF); | |
276 | }, | |
277 | _ => (), | |
278 | } | |
279 | }, | |
280 | IkeV2Transform::Auth(ref auth) => { | |
281 | match *auth { | |
282 | IkeTransformAuthType::NONE => { | |
283 | // Note: this could be expected with an AEAD encription alg. | |
284 | // See rule 4 | |
285 | () | |
286 | }, | |
287 | IkeTransformAuthType::AUTH_HMAC_MD5_96 | | |
288 | IkeTransformAuthType::AUTH_HMAC_SHA1_96 | | |
289 | IkeTransformAuthType::AUTH_DES_MAC | | |
290 | IkeTransformAuthType::AUTH_KPDK_MD5 | | |
291 | IkeTransformAuthType::AUTH_AES_XCBC_96 | | |
292 | IkeTransformAuthType::AUTH_HMAC_MD5_128 | | |
293 | IkeTransformAuthType::AUTH_HMAC_SHA1_160 => { | |
294 | SCLogDebug!("Weak auth: {:?}", auth); | |
295 | self.set_event(IKEV2Event::WeakCryptoAuth); | |
296 | }, | |
297 | _ => (), | |
298 | } | |
299 | }, | |
300 | IkeV2Transform::DH(ref dh) => { | |
301 | match *dh { | |
302 | IkeTransformDHType::None => { | |
303 | SCLogDebug!("'None' DH transform proposed"); | |
304 | self.set_event(IKEV2Event::InvalidProposal); | |
305 | }, | |
306 | IkeTransformDHType::Modp768 | | |
307 | IkeTransformDHType::Modp1024 | | |
308 | IkeTransformDHType::Modp1024s160 | | |
309 | IkeTransformDHType::Modp1536 => { | |
310 | SCLogDebug!("Weak DH: {:?}", dh); | |
311 | self.set_event(IKEV2Event::WeakCryptoDH); | |
312 | }, | |
313 | _ => (), | |
314 | } | |
315 | }, | |
316 | IkeV2Transform::Unknown(tx_type,tx_id) => { | |
317 | SCLogDebug!("Unknown proposal: type={:?}, id={}", tx_type, tx_id); | |
318 | self.set_event(IKEV2Event::UnknownProposal); | |
319 | }, | |
320 | _ => (), | |
321 | } | |
322 | } | |
323 | // Rule 2: check if no DH was proposed | |
324 | if ! transforms.iter().any(|x| { | |
325 | match *x { | |
326 | IkeV2Transform::DH(_) => true, | |
327 | _ => false | |
328 | } | |
329 | }) | |
330 | { | |
331 | SCLogDebug!("No DH transform found"); | |
332 | self.set_event(IKEV2Event::WeakCryptoNoDH); | |
333 | } | |
334 | // Rule 3: check if proposing AH ([RFC7296] section 3.3.1) | |
335 | if p.protocol_id == ProtocolID::AH { | |
336 | SCLogDebug!("Proposal uses protocol AH - no confidentiality"); | |
337 | self.set_event(IKEV2Event::NoEncryption); | |
338 | } | |
339 | // Rule 4: lack of integrity is accepted only if using an AEAD proposal | |
340 | // Look if no auth was proposed, including if proposal is Auth::None | |
341 | if ! transforms.iter().any(|x| { | |
342 | match *x { | |
343 | IkeV2Transform::Auth(IkeTransformAuthType::NONE) => false, | |
344 | IkeV2Transform::Auth(_) => true, | |
345 | _ => false, | |
346 | } | |
347 | }) | |
348 | { | |
349 | if ! transforms.iter().any(|x| { | |
350 | match *x { | |
351 | IkeV2Transform::Encryption(ref enc) => enc.is_aead(), | |
352 | _ => false | |
353 | } | |
354 | }) { | |
355 | SCLogDebug!("No integrity transform found"); | |
356 | self.set_event(IKEV2Event::WeakCryptoNoAuth); | |
357 | } | |
358 | } | |
359 | // Finally | |
360 | if direction == STREAM_TOCLIENT { | |
361 | transforms.iter().for_each(|t| | |
362 | match *t { | |
363 | IkeV2Transform::Encryption(ref e) => self.alg_enc = *e, | |
364 | IkeV2Transform::Auth(ref a) => self.alg_auth = *a, | |
365 | IkeV2Transform::PRF(ref p) => self.alg_prf = *p, | |
366 | IkeV2Transform::DH(ref dh) => self.alg_dh = *dh, | |
367 | IkeV2Transform::ESN(ref e) => self.alg_esn = *e, | |
368 | _ => (), | |
369 | }); | |
370 | SCLogDebug!("Selected transforms: {:?}", transforms); | |
371 | self.server_transforms.push(transforms); | |
372 | } else { | |
373 | SCLogDebug!("Proposed transforms: {:?}", transforms); | |
374 | self.client_transforms.push(transforms); | |
375 | } | |
376 | } | |
377 | } | |
378 | } | |
379 | ||
380 | impl IKEV2Transaction { | |
381 | pub fn new(id: u64) -> IKEV2Transaction { | |
382 | IKEV2Transaction { | |
383 | xid: 0, | |
384 | hdr: IkeV2Header { | |
385 | init_spi: 0, | |
386 | resp_spi: 0, | |
387 | next_payload: IkePayloadType::NoNextPayload, | |
388 | maj_ver: 0, | |
389 | min_ver: 0, | |
390 | exch_type: IkeExchangeType(0), | |
391 | flags: 0, | |
392 | msg_id: 0, | |
393 | length: 0, | |
394 | }, | |
d9434628 PC |
395 | payload_types: Vec::new(), |
396 | notify_types: Vec::new(), | |
397 | errors: 0, | |
c99b9462 PC |
398 | id: id, |
399 | de_state: None, | |
400 | events: std::ptr::null_mut(), | |
401 | logged: applayer::LoggerFlags::new(), | |
402 | } | |
403 | } | |
404 | ||
405 | fn free(&mut self) { | |
406 | if self.events != std::ptr::null_mut() { | |
407 | core::sc_app_layer_decoder_events_free_events(&mut self.events); | |
408 | } | |
083908f3 VJ |
409 | if let Some(state) = self.de_state { |
410 | core::sc_detect_engine_state_free(state); | |
411 | } | |
c99b9462 PC |
412 | } |
413 | } | |
414 | ||
415 | impl Drop for IKEV2Transaction { | |
416 | fn drop(&mut self) { | |
417 | self.free(); | |
418 | } | |
419 | } | |
420 | ||
421 | ||
422 | ||
423 | ||
424 | ||
425 | ||
426 | /// Returns *mut IKEV2State | |
427 | #[no_mangle] | |
428 | pub extern "C" fn rs_ikev2_state_new() -> *mut libc::c_void { | |
429 | let state = IKEV2State::new(); | |
430 | let boxed = Box::new(state); | |
431 | return unsafe{std::mem::transmute(boxed)}; | |
432 | } | |
433 | ||
434 | /// Params: | |
435 | /// - state: *mut IKEV2State as void pointer | |
436 | #[no_mangle] | |
437 | pub extern "C" fn rs_ikev2_state_free(state: *mut libc::c_void) { | |
438 | // Just unbox... | |
439 | let mut ikev2_state: Box<IKEV2State> = unsafe{std::mem::transmute(state)}; | |
440 | ikev2_state.free(); | |
441 | } | |
442 | ||
443 | #[no_mangle] | |
444 | pub extern "C" fn rs_ikev2_parse_request(_flow: *const core::Flow, | |
445 | state: *mut libc::c_void, | |
446 | _pstate: *mut libc::c_void, | |
447 | input: *const libc::uint8_t, | |
448 | input_len: u32, | |
7bc3c3ac | 449 | _data: *const libc::c_void, |
ae10a92b | 450 | _flags: u8) -> i32 { |
c99b9462 PC |
451 | let buf = build_slice!(input,input_len as usize); |
452 | let state = cast_pointer!(state,IKEV2State); | |
453 | state.parse(buf, STREAM_TOSERVER) | |
454 | } | |
455 | ||
456 | #[no_mangle] | |
457 | pub extern "C" fn rs_ikev2_parse_response(_flow: *const core::Flow, | |
458 | state: *mut libc::c_void, | |
459 | pstate: *mut libc::c_void, | |
460 | input: *const libc::uint8_t, | |
461 | input_len: u32, | |
7bc3c3ac | 462 | _data: *const libc::c_void, |
ae10a92b | 463 | _flags: u8) -> i32 { |
c99b9462 PC |
464 | let buf = build_slice!(input,input_len as usize); |
465 | let state = cast_pointer!(state,IKEV2State); | |
466 | let res = state.parse(buf, STREAM_TOCLIENT); | |
467 | if state.connection_state == IKEV2ConnectionState::ParsingDone { | |
468 | unsafe{ | |
469 | AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION | | |
470 | APP_LAYER_PARSER_NO_REASSEMBLY | | |
471 | APP_LAYER_PARSER_BYPASS_READY) | |
472 | }; | |
473 | } | |
474 | res | |
475 | } | |
476 | ||
477 | #[no_mangle] | |
478 | pub extern "C" fn rs_ikev2_state_get_tx(state: *mut libc::c_void, | |
479 | tx_id: libc::uint64_t) | |
480 | -> *mut libc::c_void | |
481 | { | |
482 | let state = cast_pointer!(state,IKEV2State); | |
483 | match state.get_tx_by_id(tx_id) { | |
484 | Some(tx) => unsafe{std::mem::transmute(tx)}, | |
485 | None => std::ptr::null_mut(), | |
486 | } | |
487 | } | |
488 | ||
489 | #[no_mangle] | |
490 | pub extern "C" fn rs_ikev2_state_get_tx_count(state: *mut libc::c_void) | |
491 | -> libc::uint64_t | |
492 | { | |
493 | let state = cast_pointer!(state,IKEV2State); | |
494 | state.tx_id | |
495 | } | |
496 | ||
497 | #[no_mangle] | |
498 | pub extern "C" fn rs_ikev2_state_tx_free(state: *mut libc::c_void, | |
499 | tx_id: libc::uint64_t) | |
500 | { | |
501 | let state = cast_pointer!(state,IKEV2State); | |
502 | state.free_tx(tx_id); | |
503 | } | |
504 | ||
505 | #[no_mangle] | |
506 | pub extern "C" fn rs_ikev2_state_progress_completion_status( | |
507 | _direction: libc::uint8_t) | |
508 | -> libc::c_int | |
509 | { | |
510 | return 1; | |
511 | } | |
512 | ||
513 | #[no_mangle] | |
514 | pub extern "C" fn rs_ikev2_tx_get_alstate_progress(_tx: *mut libc::c_void, | |
515 | _direction: libc::uint8_t) | |
516 | -> libc::c_int | |
517 | { | |
518 | 1 | |
519 | } | |
520 | ||
521 | ||
522 | ||
523 | ||
524 | ||
525 | #[no_mangle] | |
526 | pub extern "C" fn rs_ikev2_tx_set_logged(_state: *mut libc::c_void, | |
527 | tx: *mut libc::c_void, | |
528 | logged: libc::uint32_t) | |
529 | { | |
530 | let tx = cast_pointer!(tx,IKEV2Transaction); | |
531 | tx.logged.set(logged); | |
532 | } | |
533 | ||
534 | #[no_mangle] | |
535 | pub extern "C" fn rs_ikev2_tx_get_logged(_state: *mut libc::c_void, | |
536 | tx: *mut libc::c_void) | |
537 | -> u32 | |
538 | { | |
539 | let tx = cast_pointer!(tx,IKEV2Transaction); | |
540 | return tx.logged.get(); | |
541 | } | |
542 | ||
543 | ||
544 | #[no_mangle] | |
545 | pub extern "C" fn rs_ikev2_state_set_tx_detect_state( | |
546 | tx: *mut libc::c_void, | |
547 | de_state: &mut core::DetectEngineState) -> libc::c_int | |
548 | { | |
549 | let tx = cast_pointer!(tx,IKEV2Transaction); | |
550 | tx.de_state = Some(de_state); | |
551 | 0 | |
552 | } | |
553 | ||
554 | #[no_mangle] | |
555 | pub extern "C" fn rs_ikev2_state_get_tx_detect_state( | |
556 | tx: *mut libc::c_void) | |
557 | -> *mut core::DetectEngineState | |
558 | { | |
559 | let tx = cast_pointer!(tx,IKEV2Transaction); | |
560 | match tx.de_state { | |
561 | Some(ds) => ds, | |
562 | None => std::ptr::null_mut(), | |
563 | } | |
564 | } | |
565 | ||
566 | ||
567 | #[no_mangle] | |
568 | pub extern "C" fn rs_ikev2_state_get_events(state: *mut libc::c_void, | |
569 | tx_id: libc::uint64_t) | |
570 | -> *mut core::AppLayerDecoderEvents | |
571 | { | |
572 | let state = cast_pointer!(state,IKEV2State); | |
573 | match state.get_tx_by_id(tx_id) { | |
574 | Some(tx) => tx.events, | |
575 | _ => std::ptr::null_mut(), | |
576 | } | |
577 | } | |
578 | ||
579 | #[no_mangle] | |
580 | pub extern "C" fn rs_ikev2_state_get_event_info(event_name: *const libc::c_char, | |
581 | event_id: *mut libc::c_int, | |
582 | event_type: *mut core::AppLayerEventType) | |
583 | -> libc::c_int | |
584 | { | |
585 | if event_name == std::ptr::null() { return -1; } | |
586 | let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) }; | |
587 | let event = match c_event_name.to_str() { | |
588 | Ok(s) => { | |
589 | match s { | |
590 | "malformed_data" => IKEV2Event::MalformedData as i32, | |
591 | "no_encryption" => IKEV2Event::NoEncryption as i32, | |
592 | "weak_crypto_enc" => IKEV2Event::WeakCryptoEnc as i32, | |
593 | "weak_crypto_prf" => IKEV2Event::WeakCryptoPRF as i32, | |
594 | "weak_crypto_auth" => IKEV2Event::WeakCryptoAuth as i32, | |
595 | "weak_crypto_dh" => IKEV2Event::WeakCryptoDH as i32, | |
596 | "weak_crypto_nodh" => IKEV2Event::WeakCryptoNoDH as i32, | |
597 | "weak_crypto_noauth" => IKEV2Event::WeakCryptoNoAuth as i32, | |
598 | "invalid_proposal" => IKEV2Event::InvalidProposal as i32, | |
599 | "unknown_proposal" => IKEV2Event::UnknownProposal as i32, | |
600 | _ => -1, // unknown event | |
601 | } | |
602 | }, | |
603 | Err(_) => -1, // UTF-8 conversion failed | |
604 | }; | |
605 | unsafe{ | |
606 | *event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION; | |
607 | *event_id = event as libc::c_int; | |
608 | }; | |
609 | 0 | |
610 | } | |
611 | ||
612 | ||
613 | static mut ALPROTO_IKEV2 : AppProto = ALPROTO_UNKNOWN; | |
614 | ||
615 | #[no_mangle] | |
fd389891 | 616 | pub extern "C" fn rs_ikev2_probing_parser(_flow: *const Flow, input:*const libc::uint8_t, input_len: u32) -> AppProto { |
c99b9462 PC |
617 | let slice = build_slice!(input,input_len as usize); |
618 | let alproto = unsafe{ ALPROTO_IKEV2 }; | |
619 | match parse_ikev2_header(slice) { | |
13b73997 | 620 | Ok((_, ref hdr)) => { |
c99b9462 PC |
621 | if hdr.maj_ver != 2 || hdr.min_ver != 0 { |
622 | SCLogDebug!("ipsec_probe: could be ipsec, but with unsupported/invalid version {}.{}", | |
623 | hdr.maj_ver, hdr.min_ver); | |
624 | return unsafe{ALPROTO_FAILED}; | |
625 | } | |
626 | if hdr.exch_type.0 < 34 || hdr.exch_type.0 > 37 { | |
627 | SCLogDebug!("ipsec_probe: could be ipsec, but with unsupported/invalid exchange type {}", | |
628 | hdr.exch_type.0); | |
629 | return unsafe{ALPROTO_FAILED}; | |
630 | } | |
631 | if hdr.length as usize != slice.len() { | |
632 | SCLogDebug!("ipsec_probe: could be ipsec, but length does not match"); | |
633 | return unsafe{ALPROTO_FAILED}; | |
634 | } | |
635 | return alproto; | |
636 | }, | |
13b73997 | 637 | Err(nom::Err::Incomplete(_)) => { |
c99b9462 PC |
638 | return ALPROTO_UNKNOWN; |
639 | }, | |
13b73997 | 640 | Err(_) => { |
c99b9462 PC |
641 | return unsafe{ALPROTO_FAILED}; |
642 | }, | |
643 | } | |
644 | } | |
645 | ||
646 | const PARSER_NAME : &'static [u8] = b"ikev2\0"; | |
647 | ||
648 | #[no_mangle] | |
649 | pub unsafe extern "C" fn rs_register_ikev2_parser() { | |
650 | let default_port = CString::new("500").unwrap(); | |
651 | let parser = RustParser { | |
652 | name : PARSER_NAME.as_ptr() as *const libc::c_char, | |
653 | default_port : default_port.as_ptr(), | |
654 | ipproto : libc::IPPROTO_UDP, | |
655 | probe_ts : rs_ikev2_probing_parser, | |
656 | probe_tc : rs_ikev2_probing_parser, | |
657 | min_depth : 0, | |
658 | max_depth : 16, | |
659 | state_new : rs_ikev2_state_new, | |
660 | state_free : rs_ikev2_state_free, | |
661 | tx_free : rs_ikev2_state_tx_free, | |
662 | parse_ts : rs_ikev2_parse_request, | |
663 | parse_tc : rs_ikev2_parse_response, | |
664 | get_tx_count : rs_ikev2_state_get_tx_count, | |
665 | get_tx : rs_ikev2_state_get_tx, | |
666 | tx_get_comp_st : rs_ikev2_state_progress_completion_status, | |
667 | tx_get_progress : rs_ikev2_tx_get_alstate_progress, | |
668 | get_tx_logged : Some(rs_ikev2_tx_get_logged), | |
669 | set_tx_logged : Some(rs_ikev2_tx_set_logged), | |
670 | get_de_state : rs_ikev2_state_get_tx_detect_state, | |
671 | set_de_state : rs_ikev2_state_set_tx_detect_state, | |
672 | get_events : Some(rs_ikev2_state_get_events), | |
673 | get_eventinfo : Some(rs_ikev2_state_get_event_info), | |
674 | localstorage_new : None, | |
675 | localstorage_free : None, | |
676 | get_tx_mpm_id : None, | |
677 | set_tx_mpm_id : None, | |
678 | get_files : None, | |
da4912df | 679 | get_tx_iterator : None, |
c99b9462 PC |
680 | }; |
681 | ||
682 | let ip_proto_str = CString::new("udp").unwrap(); | |
683 | if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
684 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); | |
685 | // store the allocated ID for the probe function | |
686 | ALPROTO_IKEV2 = alproto; | |
687 | if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { | |
688 | let _ = AppLayerRegisterParser(&parser, alproto); | |
689 | } | |
690 | } else { | |
691 | SCLogDebug!("Protocol detecter and parser disabled for IKEV2."); | |
692 | } | |
693 | } | |
694 | ||
695 | ||
696 | #[cfg(test)] | |
697 | mod tests { | |
698 | use super::IKEV2State; | |
699 | ||
700 | #[test] | |
701 | fn test_ikev2_parse_request_valid() { | |
702 | // A UDP IKEV2 v4 request, in client mode | |
703 | const REQ : &[u8] = &[ | |
704 | 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
705 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
706 | 0x00, 0x20, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, | |
707 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
708 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
709 | 0x18, 0x57, 0xab, 0xc3, 0x4a, 0x5f, 0x2c, 0xfe | |
710 | ]; | |
711 | ||
712 | let mut state = IKEV2State::new(); | |
713 | assert_eq!(1, state.parse(REQ, 0)); | |
714 | } | |
715 | } |