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