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