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