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